diff --git a/cli/kata-check.go b/cli/kata-check.go index 683d2619f1..5d84a374e2 100644 --- a/cli/kata-check.go +++ b/cli/kata-check.go @@ -63,6 +63,7 @@ const ( moduleParamDir = "parameters" successMessageCapable = "System is capable of running " + project successMessageCreate = "System can currently create " + project + successMessageVersion = "Version consistency of " + project + " is verified" failMessage = "System is not capable of running " + project kernelPropertyCorrect = "Kernel property value correct" @@ -309,6 +310,10 @@ var kataCheckCLICommand = cli.Command{ Name: "verbose, v", Usage: "display the list of checks performed", }, + cli.BoolFlag{ + Name: "strict, s", + Usage: "perform strict checking", + }, }, Action: func(context *cli.Context) error { @@ -357,6 +362,16 @@ var kataCheckCLICommand = cli.Command{ fmt.Println(successMessageCreate) } + strict := context.Bool("strict") + if strict { + err = checkVersionConsistencyInComponents(runtimeConfig) + if err != nil { + return err + } + + fmt.Println(successMessageVersion) + } + return nil }, } @@ -454,3 +469,35 @@ func genericCheckKVMExtensions(extensions map[string]kvmExtension) (map[string]i return results, nil } + +// checkVersionConsistencyInComponents checks version consistency in Kata Components. +func checkVersionConsistencyInComponents(config oci.RuntimeConfig) error { + proxyInfo := getProxyInfo(config) + + shimInfo, err := getShimInfo(config) + if err != nil { + return err + } + shimVersionInfo := shimInfo.Version + + runtimeVersionInfo := constructVersionInfo(version) + + // kata-proxy exists + if proxyInfo.Type != string(vc.NoProxyType) { + proxyVersionInfo := proxyInfo.Version + if !versionEqual(proxyVersionInfo, runtimeVersionInfo) || !versionEqual(shimVersionInfo, runtimeVersionInfo) { + return fmt.Errorf("there exists version inconsistency in kata components. kata-proxy: v%d.%d.%d, kata-shim: v%d.%d.%d, kata-runtime: v%d.%d.%d", + proxyVersionInfo.Major, proxyVersionInfo.Minor, proxyVersionInfo.Patch, + shimVersionInfo.Major, shimVersionInfo.Minor, shimVersionInfo.Patch, + runtimeVersionInfo.Major, runtimeVersionInfo.Minor, runtimeVersionInfo.Patch) + } + } else { + if !versionEqual(shimVersionInfo, runtimeVersionInfo) { + return fmt.Errorf("there exists version inconsistency in kata components. kata-shim: v%d.%d.%d, kata-runtime: v%d.%d.%d", + shimVersionInfo.Major, shimVersionInfo.Minor, shimVersionInfo.Patch, + runtimeVersionInfo.Major, runtimeVersionInfo.Minor, runtimeVersionInfo.Patch) + } + } + + return nil +} diff --git a/cli/kata-check_test.go b/cli/kata-check_test.go index 6cb7ee5746..b824a01d6b 100644 --- a/cli/kata-check_test.go +++ b/cli/kata-check_test.go @@ -19,6 +19,7 @@ import ( ktu "github.com/kata-containers/runtime/pkg/katatestutils" "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/urfave/cli" @@ -905,3 +906,87 @@ func TestArchRequiredKernelModules(t *testing.T) { assert.EqualValues(count, expectedCount) } + +func TestCheckVersionConsistencyInComponents(t *testing.T) { + type testData struct { + proxyExist bool + expectError bool + shimVersion string + proxyVersion string + runtimeVersion string + } + + data := []testData{ + { + true, + true, + "kata-shim version 0.2.0-rc0-xxxxxxxxxxxxx", + "kata-proxy version 0.1.0-rc0-xxxxxxxxxxxxx", + "0.2.0-rc0", + }, + { + true, + true, + "kata-shim version 0.1.0-rc0-xxxxxxxxxxxxx", + "kata-proxy version 0.2.0-rc0-xxxxxxxxxxxxx", + "0.2.0-rc0", + }, + { + true, + true, + "kata-shim version 0.1.0-rc0-xxxxxxxxxxxxx", + "kata-proxy version 0.1.0-rc0-xxxxxxxxxxxxx", + "0.2.0-rc0", + }, + { + true, + false, + "kata-shim version 0.2.0-rc0-xxxxxxxxxxxxx", + "kata-proxy version 0.2.0-rc0-xxxxxxxxxxxxx", + "0.2.0-rc0", + }, + { + false, + true, + "kata-shim version 0.1.0-rc0-xxxxxxxxxxxxx", + "", + "0.2.0-rc0", + }, + { + false, + false, + "kata-shim version 0.2.0-rc0-xxxxxxxxxxxxx", + "", + "0.2.0-rc0", + }, + } + + origVersion := version + for _, d := range data { + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testShimVersion = d.shimVersion + if d.proxyExist { + testProxyVersion = d.proxyVersion + } + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + if !d.proxyExist { + config.ProxyType = vc.NoProxyType + } + version = d.runtimeVersion + defer func() { + version = origVersion + }() + + err = checkVersionConsistencyInComponents(config) + if d.expectError { + assert.Error(t, err, fmt.Sprintf("%+v", d)) + continue + } else { + assert.NoError(t, err, fmt.Sprintf("%+v", d)) + } + } +} diff --git a/cli/kata-env.go b/cli/kata-env.go index c973961aae..1c7fb0748d 100644 --- a/cli/kata-env.go +++ b/cli/kata-env.go @@ -74,11 +74,18 @@ type RuntimeInfo struct { Path string } +type VersionInfo struct { + Semver string + Major uint64 + Minor uint64 + Patch uint64 + Commit string +} + // RuntimeVersionInfo stores details of the runtime version type RuntimeVersionInfo struct { - Semver string - Commit string - OCI string + Version VersionInfo + OCI string } // HypervisorInfo stores hypervisor details @@ -100,7 +107,7 @@ type HypervisorInfo struct { // ProxyInfo stores proxy details type ProxyInfo struct { Type string - Version string + Version VersionInfo Path string Debug bool } @@ -108,7 +115,7 @@ type ProxyInfo struct { // ShimInfo stores shim details type ShimInfo struct { Type string - Version string + Version VersionInfo Path string Debug bool } @@ -140,7 +147,7 @@ type HostInfo struct { // NetmonInfo stores netmon details type NetmonInfo struct { - Version string + Version VersionInfo Path string Debug bool Enable bool @@ -171,10 +178,12 @@ func getMetaInfo() MetaInfo { } func getRuntimeInfo(configFile string, config oci.RuntimeConfig) RuntimeInfo { + runtimeVersionInfo := constructVersionInfo(version) + runtimeVersionInfo.Commit = commit + runtimeVersion := RuntimeVersionInfo{ - Semver: version, - Commit: commit, - OCI: specs.Version, + Version: runtimeVersionInfo, + OCI: specs.Version, } runtimeConfig := RuntimeConfigInfo{ @@ -250,43 +259,48 @@ func getHostInfo() (HostInfo, error) { return host, nil } -func getProxyInfo(config oci.RuntimeConfig) (ProxyInfo, error) { +func getProxyInfo(config oci.RuntimeConfig) ProxyInfo { if config.ProxyType == vc.NoProxyType { - return ProxyInfo{Type: string(config.ProxyType)}, nil + return ProxyInfo{Type: string(config.ProxyType)} } proxyConfig := config.ProxyConfig - version, err := getCommandVersion(proxyConfig.Path) - if err != nil { - version = unknown + + var proxyVersionInfo VersionInfo + if version, err := getCommandVersion(proxyConfig.Path); err != nil { + proxyVersionInfo = unknownVersionInfo + } else { + proxyVersionInfo = constructVersionInfo(version) } proxy := ProxyInfo{ Type: string(config.ProxyType), - Version: version, + Version: proxyVersionInfo, Path: proxyConfig.Path, Debug: proxyConfig.Debug, } - return proxy, nil + return proxy } -func getNetmonInfo(config oci.RuntimeConfig) (NetmonInfo, error) { +func getNetmonInfo(config oci.RuntimeConfig) NetmonInfo { netmonConfig := config.NetmonConfig - version, err := getCommandVersion(netmonConfig.Path) - if err != nil { - version = unknown + var netmonVersionInfo VersionInfo + if version, err := getCommandVersion(netmonConfig.Path); err != nil { + netmonVersionInfo = unknownVersionInfo + } else { + netmonVersionInfo = constructVersionInfo(version) } netmon := NetmonInfo{ - Version: version, + Version: netmonVersionInfo, Path: netmonConfig.Path, Debug: netmonConfig.Debug, Enable: netmonConfig.Enable, } - return netmon, nil + return netmon } func getCommandVersion(cmd string) (string, error) { @@ -301,14 +315,16 @@ func getShimInfo(config oci.RuntimeConfig) (ShimInfo, error) { shimPath := shimConfig.Path - version, err := getCommandVersion(shimPath) - if err != nil { - version = unknown + var shimVersionInfo VersionInfo + if version, err := getCommandVersion(shimConfig.Path); err != nil { + shimVersionInfo = unknownVersionInfo + } else { + shimVersionInfo = constructVersionInfo(version) } shim := ShimInfo{ Type: string(config.ShimType), - Version: version, + Version: shimVersionInfo, Path: shimPath, Debug: shimConfig.Debug, } @@ -378,9 +394,9 @@ func getEnvInfo(configFile string, config oci.RuntimeConfig) (env EnvInfo, err e return EnvInfo{}, err } - proxy, _ := getProxyInfo(config) + proxy := getProxyInfo(config) - netmon, _ := getNetmonInfo(config) + netmon := getNetmonInfo(config) shim, err := getShimInfo(config) if err != nil { diff --git a/cli/kata-env_test.go b/cli/kata-env_test.go index ed02de8ffd..5f030a4970 100644 --- a/cli/kata-env_test.go +++ b/cli/kata-env_test.go @@ -30,10 +30,12 @@ import ( "github.com/stretchr/testify/assert" ) -const testProxyVersion = "proxy version 0.1" -const testShimVersion = "shim version 0.1" -const testNetmonVersion = "netmon version 0.1" -const testHypervisorVersion = "QEMU emulator version 2.7.0+git.741f430a96-6.1, Copyright (c) 2003-2016 Fabrice Bellard and the QEMU Project developers" +var ( + testProxyVersion = "proxy version 0.1" + testShimVersion = "shim version 0.1" + testNetmonVersion = "netmon version 0.1" + testHypervisorVersion = "QEMU emulator version 2.7.0+git.741f430a96-6.1, Copyright (c) 2003-2016 Fabrice Bellard and the QEMU Project developers" +) var ( hypervisorDebug = false @@ -187,7 +189,7 @@ func makeRuntimeConfig(prefixDir string) (configFile string, config oci.RuntimeC func getExpectedProxyDetails(config oci.RuntimeConfig) (ProxyInfo, error) { return ProxyInfo{ Type: string(config.ProxyType), - Version: testProxyVersion, + Version: constructVersionInfo(testProxyVersion), Path: config.ProxyConfig.Path, Debug: config.ProxyConfig.Debug, }, nil @@ -195,7 +197,7 @@ func getExpectedProxyDetails(config oci.RuntimeConfig) (ProxyInfo, error) { func getExpectedNetmonDetails(config oci.RuntimeConfig) (NetmonInfo, error) { return NetmonInfo{ - Version: testNetmonVersion, + Version: constructVersionInfo(testNetmonVersion), Path: config.NetmonConfig.Path, Debug: config.NetmonConfig.Debug, Enable: config.NetmonConfig.Enable, @@ -212,7 +214,7 @@ func getExpectedShimDetails(config oci.RuntimeConfig) (ShimInfo, error) { return ShimInfo{ Type: string(config.ShimType), - Version: testShimVersion, + Version: constructVersionInfo(testShimVersion), Path: shimPath, Debug: shimConfig.Debug, }, nil @@ -353,11 +355,12 @@ func getExpectedKernel(config oci.RuntimeConfig) KernelInfo { func getExpectedRuntimeDetails(config oci.RuntimeConfig, configFile string) RuntimeInfo { runtimePath, _ := os.Executable() + runtimeVersionInfo := constructVersionInfo(version) + runtimeVersionInfo.Commit = commit return RuntimeInfo{ Version: RuntimeVersionInfo{ - Semver: version, - Commit: commit, - OCI: specs.Version, + Version: runtimeVersionInfo, + OCI: specs.Version, }, Config: RuntimeConfigInfo{ Path: configFile, @@ -678,7 +681,7 @@ func TestEnvGetProxyInfo(t *testing.T) { expectedProxy, err := getExpectedProxyDetails(config) assert.NoError(t, err) - proxy, err := getProxyInfo(config) + proxy := getProxyInfo(config) assert.NoError(t, err) assert.Equal(t, expectedProxy, proxy) @@ -701,9 +704,9 @@ func TestEnvGetProxyInfoNoVersion(t *testing.T) { err = os.Remove(config.ProxyConfig.Path) assert.NoError(t, err) - expectedProxy.Version = unknown + expectedProxy.Version = unknownVersionInfo - proxy, err := getProxyInfo(config) + proxy := getProxyInfo(config) assert.NoError(t, err) assert.Equal(t, expectedProxy, proxy) @@ -722,7 +725,7 @@ func TestEnvGetNetmonInfo(t *testing.T) { expectedNetmon, err := getExpectedNetmonDetails(config) assert.NoError(t, err) - netmon, err := getNetmonInfo(config) + netmon := getNetmonInfo(config) assert.NoError(t, err) assert.Equal(t, expectedNetmon, netmon) @@ -745,9 +748,9 @@ func TestEnvGetNetmonInfoNoVersion(t *testing.T) { err = os.Remove(config.NetmonConfig.Path) assert.NoError(t, err) - expectedNetmon.Version = unknown + expectedNetmon.Version = unknownVersionInfo - netmon, err := getNetmonInfo(config) + netmon := getNetmonInfo(config) assert.NoError(t, err) assert.Equal(t, expectedNetmon, netmon) @@ -792,7 +795,7 @@ func TestEnvGetShimInfoNoVersion(t *testing.T) { exit 1`) assert.NoError(t, err) - expectedShim.Version = unknown + expectedShim.Version = unknownVersionInfo shim, err := getShimInfo(config) assert.NoError(t, err) @@ -880,14 +883,14 @@ func testEnvShowTOMLSettings(t *testing.T, tmpdir string, tmpfile *os.File) erro proxy := ProxyInfo{ Type: "proxy-type", - Version: "proxy-version", + Version: constructVersionInfo(testProxyVersion), Path: "file:///proxy-url", Debug: false, } shim := ShimInfo{ Type: "shim-type", - Version: "shim-version", + Version: constructVersionInfo(testShimVersion), Path: "/resolved/shim/path", } @@ -949,14 +952,14 @@ func testEnvShowJSONSettings(t *testing.T, tmpdir string, tmpfile *os.File) erro proxy := ProxyInfo{ Type: "proxy-type", - Version: "proxy-version", + Version: constructVersionInfo(testProxyVersion), Path: "file:///proxy-url", Debug: false, } shim := ShimInfo{ Type: "shim-type", - Version: "shim-version", + Version: constructVersionInfo(testShimVersion), Path: "/resolved/shim/path", } diff --git a/cli/utils.go b/cli/utils.go index e4298162aa..f6c855c488 100644 --- a/cli/utils.go +++ b/cli/utils.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "github.com/blang/semver" "github.com/kata-containers/runtime/pkg/katautils" ) @@ -26,6 +27,11 @@ var ( // Clear Linux has a different path (for stateless support) osReleaseClr = "/usr/lib/os-release" + + unknownVersionInfo = VersionInfo{ + Semver: unknown, + Commit: unknown, + } ) func getKernelVersion() (string, error) { @@ -143,3 +149,55 @@ func parseBoolOrAuto(s string) (*bool, error) { b, err := strconv.ParseBool(s) return &b, err } + +// constructVersionInfo constructs VersionInfo-type value from a version string +// in the format of "Kata-Component version Major.Minor.Patch-rc_xxx-Commit". +func constructVersionInfo(version string) VersionInfo { + fields := strings.Split(version, " ") + realVersion := fields[len(fields)-1] + + sv, err := semver.Make(realVersion) + if err != nil { + return unknownVersionInfo + } + + pres := strings.Split(sv.Pre[0].VersionStr, "-") + + // version contains Commit info. + if len(pres) > 1 { + return VersionInfo{ + Semver: realVersion, + Major: sv.Major, + Minor: sv.Minor, + Patch: sv.Patch, + Commit: pres[1], + } + } + + return VersionInfo{ + Semver: realVersion, + Major: sv.Major, + Minor: sv.Minor, + Patch: sv.Patch, + Commit: unknown, + } + +} + +func versionEqual(a VersionInfo, b VersionInfo) bool { + av, err := semver.Make(a.Semver) + if err != nil { + return false + } + + bv, err := semver.Make(b.Semver) + if err != nil { + return false + } + + if av.Major == bv.Major && av.Minor == bv.Minor && av.Patch == bv.Patch { + return true + } + + return false +}