From 63c06bee7058064a2b2d0340f4b1ae7377524974 Mon Sep 17 00:00:00 2001 From: Graham Whaley Date: Fri, 6 Jul 2018 17:27:55 +0100 Subject: [PATCH 1/4] kata-env: Add ability to output as JSON Having a direct JSON output for kata-env will help record results in our CIs in some instances. Add that ability with a kata-env command line extension. Fixes: #474 Signed-off-by: Graham Whaley --- cli/kata-env.go | 59 +++++++++++++++++++++++++++++++------------- cli/kata-env_test.go | 10 +++++++- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/cli/kata-env.go b/cli/kata-env.go index d0daf3490f..150b51ca7f 100644 --- a/cli/kata-env.go +++ b/cli/kata-env.go @@ -6,6 +6,7 @@ package main import ( + "encoding/json" "errors" "os" "strings" @@ -328,28 +329,17 @@ func getEnvInfo(configFile string, config oci.RuntimeConfig) (env EnvInfo, err e return env, nil } -func showSettings(env EnvInfo, file *os.File) error { - encoder := toml.NewEncoder(file) - - err := encoder.Encode(env) - if err != nil { - return err - } - - return nil -} - -func handleSettings(file *os.File, metadata map[string]interface{}) error { +func handleSettings(file *os.File, c *cli.Context) error { if file == nil { return errors.New("Invalid output file specified") } - configFile, ok := metadata["configFile"].(string) + configFile, ok := c.App.Metadata["configFile"].(string) if !ok { return errors.New("cannot determine config file") } - runtimeConfig, ok := metadata["runtimeConfig"].(oci.RuntimeConfig) + runtimeConfig, ok := c.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) if !ok { return errors.New("cannot determine runtime config") } @@ -359,13 +349,48 @@ func handleSettings(file *os.File, metadata map[string]interface{}) error { return err } - return showSettings(env, file) + if c.Bool("json") { + return writeJSONSettings(env, file) + } + + return writeTOMLSettings(env, file) +} + +func writeTOMLSettings(env EnvInfo, file *os.File) error { + encoder := toml.NewEncoder(file) + + err := encoder.Encode(env) + if err != nil { + return err + } + + return nil +} + +func writeJSONSettings(env EnvInfo, file *os.File) error { + encoder := json.NewEncoder(file) + + // Make it more human readable + encoder.SetIndent("", " ") + + err := encoder.Encode(env) + if err != nil { + return err + } + + return nil } var kataEnvCLICommand = cli.Command{ Name: envCmd, - Usage: "display settings", + Usage: "display settings. Default to TOML", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "json", + Usage: "Format output as JSON", + }, + }, Action: func(context *cli.Context) error { - return handleSettings(defaultOutputFile, context.App.Metadata) + return handleSettings(defaultOutputFile, context) }, } diff --git a/cli/kata-env_test.go b/cli/kata-env_test.go index d8a9100cd8..84971fd111 100644 --- a/cli/kata-env_test.go +++ b/cli/kata-env_test.go @@ -7,6 +7,7 @@ package main import ( "bytes" + "flag" "fmt" "io/ioutil" "os" @@ -717,7 +718,7 @@ func testEnvShowSettings(t *testing.T, tmpdir string, tmpfile *os.File) error { Host: expectedHostDetails, } - err = showSettings(env, tmpfile) + err = writeTOMLSettings(env, tmpfile) if err != nil { return err } @@ -906,6 +907,13 @@ func TestEnvCLIFunction(t *testing.T) { err = fn(ctx) assert.NoError(t, err) + + set := flag.NewFlagSet("", 0) + set.Bool("json", true, "") + ctx = cli.NewContext(app, set, nil) + + err = fn(ctx) + assert.NoError(t, err) } func TestEnvCLIFunctionFail(t *testing.T) { From 1f8f7629a0703ea5a4e52fdae272c042e59d1906 Mon Sep 17 00:00:00 2001 From: Graham Whaley Date: Tue, 10 Jul 2018 20:04:27 +0100 Subject: [PATCH 2/4] kata-env: Fix test cases for kata-env JSON With the addition of the JSON kata-env output, we need to fix up the tests: - add a test for the JSON flag - fix the format/layout of the other tests to take into account the change in function API and the additon of a flagset to the cmdline ctx. Signed-off-by: Graham Whaley --- cli/kata-env_test.go | 65 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/cli/kata-env_test.go b/cli/kata-env_test.go index 84971fd111..6cf1fb0f65 100644 --- a/cli/kata-env_test.go +++ b/cli/kata-env_test.go @@ -783,7 +783,11 @@ func TestEnvHandleSettings(t *testing.T) { _, err = getExpectedSettings(config, tmpdir, configFile) assert.NoError(t, err) - m := map[string]interface{}{ + app := cli.NewApp() + set := flag.NewFlagSet("test", flag.ContinueOnError) + ctx := cli.NewContext(app, set, nil) + app.Name = "foo" + ctx.App.Metadata = map[string]interface{}{ "configFile": configFile, "runtimeConfig": config, } @@ -792,7 +796,7 @@ func TestEnvHandleSettings(t *testing.T) { assert.NoError(t, err) defer os.Remove(tmpfile.Name()) - err = handleSettings(tmpfile, m) + err = handleSettings(tmpfile, ctx) assert.NoError(t, err) var env EnvInfo @@ -816,7 +820,10 @@ func TestEnvHandleSettingsInvalidShimConfig(t *testing.T) { config.ShimConfig = "invalid shim config" - m := map[string]interface{}{ + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + app.Name = "foo" + ctx.App.Metadata = map[string]interface{}{ "configFile": configFile, "runtimeConfig": config, } @@ -825,47 +832,75 @@ func TestEnvHandleSettingsInvalidShimConfig(t *testing.T) { assert.NoError(err) defer os.Remove(tmpfile.Name()) - err = handleSettings(tmpfile, m) + err = handleSettings(tmpfile, ctx) assert.Error(err) } func TestEnvHandleSettingsInvalidParams(t *testing.T) { - err := handleSettings(nil, map[string]interface{}{}) - assert.Error(t, err) + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + configFile, _, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + app.Name = "foo" + ctx.App.Metadata = map[string]interface{}{ + "configFile": configFile, + } + err = handleSettings(nil, ctx) + assert.Error(err) } func TestEnvHandleSettingsEmptyMap(t *testing.T) { - err := handleSettings(os.Stdout, map[string]interface{}{}) + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + app.Name = "foo" + ctx.App.Metadata = map[string]interface{}{} + err := handleSettings(os.Stdout, ctx) assert.Error(t, err) } func TestEnvHandleSettingsInvalidFile(t *testing.T) { - m := map[string]interface{}{ + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + app.Name = "foo" + ctx.App.Metadata = map[string]interface{}{ "configFile": "foo", "runtimeConfig": oci.RuntimeConfig{}, } - err := handleSettings(nil, m) + err := handleSettings(nil, ctx) assert.Error(t, err) } func TestEnvHandleSettingsInvalidConfigFileType(t *testing.T) { - m := map[string]interface{}{ + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + app.Name = "foo" + ctx.App.Metadata = map[string]interface{}{ "configFile": 123, "runtimeConfig": oci.RuntimeConfig{}, } - err := handleSettings(os.Stderr, m) + err := handleSettings(os.Stderr, ctx) assert.Error(t, err) } func TestEnvHandleSettingsInvalidRuntimeConfigType(t *testing.T) { - m := map[string]interface{}{ + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + app.Name = "foo" + ctx.App.Metadata = map[string]interface{}{ "configFile": "/some/where", "runtimeConfig": true, } - err := handleSettings(os.Stderr, m) + err := handleSettings(os.Stderr, ctx) assert.Error(t, err) } @@ -883,7 +918,8 @@ func TestEnvCLIFunction(t *testing.T) { assert.NoError(t, err) app := cli.NewApp() - ctx := cli.NewContext(app, nil, nil) + set := flag.NewFlagSet("test", flag.ContinueOnError) + ctx := cli.NewContext(app, set, nil) app.Name = "foo" ctx.App.Metadata = map[string]interface{}{ @@ -908,7 +944,6 @@ func TestEnvCLIFunction(t *testing.T) { err = fn(ctx) assert.NoError(t, err) - set := flag.NewFlagSet("", 0) set.Bool("json", true, "") ctx = cli.NewContext(app, set, nil) From e45f5912195521c20c4447e4d931f7d183e1a9fc Mon Sep 17 00:00:00 2001 From: Graham Whaley Date: Wed, 11 Jul 2018 17:10:06 +0100 Subject: [PATCH 3/4] kata-env: Do not leave temp files on test One of the test cases was not defer removing the tmpfile it uses. Add that defer. Signed-off-by: Graham Whaley --- cli/kata-env_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/kata-env_test.go b/cli/kata-env_test.go index 6cf1fb0f65..417e6240e8 100644 --- a/cli/kata-env_test.go +++ b/cli/kata-env_test.go @@ -762,6 +762,7 @@ func TestEnvShowSettingsInvalidFile(t *testing.T) { tmpfile, err := ioutil.TempFile("", "envShowSettings-") assert.NoError(t, err) + defer os.Remove(tmpfile.Name()) // close the file tmpfile.Close() From bd6db3031a2e4b6118b3bcf8f8f190be06144f0f Mon Sep 17 00:00:00 2001 From: Graham Whaley Date: Wed, 11 Jul 2018 17:21:40 +0100 Subject: [PATCH 4/4] kata-env: tests: add JSON out/in verify test Add a test to ensure the JSON output passes the same parameter check and write/re-read test as the TOML one. Signed-off-by: Graham Whaley --- cli/kata-env_test.go | 90 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/cli/kata-env_test.go b/cli/kata-env_test.go index 417e6240e8..cf285c7c40 100644 --- a/cli/kata-env_test.go +++ b/cli/kata-env_test.go @@ -7,6 +7,7 @@ package main import ( "bytes" + "encoding/json" "flag" "fmt" "io/ioutil" @@ -669,7 +670,7 @@ func TestEnvGetAgentInfo(t *testing.T) { assert.Equal(t, expectedAgent, agent) } -func testEnvShowSettings(t *testing.T, tmpdir string, tmpfile *os.File) error { +func testEnvShowTOMLSettings(t *testing.T, tmpdir string, tmpfile *os.File) error { runtime := RuntimeInfo{} @@ -738,6 +739,77 @@ func testEnvShowSettings(t *testing.T, tmpdir string, tmpfile *os.File) error { return nil } +func testEnvShowJSONSettings(t *testing.T, tmpdir string, tmpfile *os.File) error { + + runtime := RuntimeInfo{} + + hypervisor := HypervisorInfo{ + Path: "/resolved/hypervisor/path", + MachineType: "hypervisor-machine-type", + } + + image := ImageInfo{ + Path: "/resolved/image/path", + } + + kernel := KernelInfo{ + Path: "/kernel/path", + Parameters: "foo=bar xyz", + } + + proxy := ProxyInfo{ + Type: "proxy-type", + Version: "proxy-version", + Path: "file:///proxy-url", + Debug: false, + } + + shim := ShimInfo{ + Type: "shim-type", + Version: "shim-version", + Path: "/resolved/shim/path", + } + + agent := AgentInfo{ + Type: "agent-type", + } + + expectedHostDetails, err := getExpectedHostDetails(tmpdir) + assert.NoError(t, err) + + env := EnvInfo{ + Runtime: runtime, + Hypervisor: hypervisor, + Image: image, + Kernel: kernel, + Proxy: proxy, + Shim: shim, + Agent: agent, + Host: expectedHostDetails, + } + + err = writeJSONSettings(env, tmpfile) + if err != nil { + return err + } + + contents, err := getFileContents(tmpfile.Name()) + assert.NoError(t, err) + + buf := new(bytes.Buffer) + encoder := json.NewEncoder(buf) + // Ensure we have the same human readable layout + encoder.SetIndent("", " ") + err = encoder.Encode(env) + assert.NoError(t, err) + + expectedContents := buf.String() + + assert.Equal(t, expectedContents, contents) + + return nil +} + func TestEnvShowSettings(t *testing.T) { tmpdir, err := ioutil.TempDir("", "") if err != nil { @@ -749,7 +821,13 @@ func TestEnvShowSettings(t *testing.T) { assert.NoError(t, err) defer os.Remove(tmpfile.Name()) - err = testEnvShowSettings(t, tmpdir, tmpfile) + err = testEnvShowTOMLSettings(t, tmpdir, tmpfile) + assert.NoError(t, err) + + // Reset the file to empty for next test + tmpfile.Truncate(0) + tmpfile.Seek(0, 0) + err = testEnvShowJSONSettings(t, tmpdir, tmpfile) assert.NoError(t, err) } @@ -767,7 +845,13 @@ func TestEnvShowSettingsInvalidFile(t *testing.T) { // close the file tmpfile.Close() - err = testEnvShowSettings(t, tmpdir, tmpfile) + err = testEnvShowTOMLSettings(t, tmpdir, tmpfile) + assert.Error(t, err) + + // Reset the file to empty for next test + tmpfile.Truncate(0) + tmpfile.Seek(0, 0) + err = testEnvShowJSONSettings(t, tmpdir, tmpfile) assert.Error(t, err) }