diff --git a/cli/kata-check_amd64_test.go b/cli/kata-check_amd64_test.go index 8aed40ecfb..1d28e44937 100644 --- a/cli/kata-check_amd64_test.go +++ b/cli/kata-check_amd64_test.go @@ -455,3 +455,7 @@ func TestKvmIsUsable(t *testing.T) { err = kvmIsUsable() assert.Error(err) } + +func TestGetCPUDetails(t *testing.T) { + genericTestGetCPUDetails(t) +} diff --git a/cli/kata-check_arm64.go b/cli/kata-check_arm64.go index 35a4d807f9..97af7d433a 100644 --- a/cli/kata-check_arm64.go +++ b/cli/kata-check_arm64.go @@ -53,6 +53,12 @@ func archHostCanCreateVMContainer() error { // hostIsVMContainerCapable checks to see if the host is theoretically capable // of creating a VM container. func hostIsVMContainerCapable(details vmContainerCapableDetails) error { + + _, err := getCPUInfo(details.cpuInfoFile) + if err != nil { + return err + } + count, err := checkKernelModules(details.requiredKernelModules, archKernelParamHandler) if err != nil { return err diff --git a/cli/kata-check_arm64_test.go b/cli/kata-check_arm64_test.go new file mode 100644 index 0000000000..4734790ed5 --- /dev/null +++ b/cli/kata-check_arm64_test.go @@ -0,0 +1,201 @@ +// Copyright (c) 2018 ARM Limited +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +func setupCheckHostIsVMContainerCapable(assert *assert.Assertions, cpuInfoFile string, moduleData []testModuleData) { + //For now, Arm64 only deal with module check + createModules(assert, cpuInfoFile, moduleData) + + err := makeCPUInfoFile(cpuInfoFile, "", "") + assert.NoError(err) + +} + +func TestCCCheckCLIFunction(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedSysModuleDir := sysModuleDir + savedProcCPUInfo := procCPUInfo + + cpuInfoFile := filepath.Join(dir, "cpuinfo") + + // XXX: override + sysModuleDir = filepath.Join(dir, "sys/module") + procCPUInfo = cpuInfoFile + + defer func() { + sysModuleDir = savedSysModuleDir + procCPUInfo = savedProcCPUInfo + }() + + err = os.MkdirAll(sysModuleDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + moduleData := []testModuleData{ + {filepath.Join(sysModuleDir, "kvm"), true, ""}, + {filepath.Join(sysModuleDir, "vhost"), true, ""}, + {filepath.Join(sysModuleDir, "vhost_net"), true, ""}, + } + + devNull, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0666) + assert.NoError(err) + defer devNull.Close() + + savedLogOutput := kataLog.Logger.Out + + // discard normal output + kataLog.Logger.Out = devNull + + defer func() { + kataLog.Logger.Out = savedLogOutput + }() + + setupCheckHostIsVMContainerCapable(assert, cpuInfoFile, moduleData) + + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + app.Name = "foo" + + // create buffer to save logger output + buf := &bytes.Buffer{} + + // capture output this time + kataLog.Logger.Out = buf + + fn, ok := kataCheckCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.NoError(err) + + output := buf.String() + + for _, m := range moduleData { + name := path.Base(m.path) + assert.True(findAnchoredString(output, name)) + } +} + +func TestKvmIsUsable(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedKvmDevice := kvmDevice + fakeKVMDevice := filepath.Join(dir, "kvm") + kvmDevice = fakeKVMDevice + + defer func() { + kvmDevice = savedKvmDevice + }() + + err = kvmIsUsable() + assert.Error(err) + + err = createEmptyFile(fakeKVMDevice) + assert.NoError(err) + + err = kvmIsUsable() + assert.Error(err) +} + +func TestGetCPUDetails(t *testing.T) { + type testData struct { + contents string + expectedVendor string + expectedModel string + expectedNormalizeVendor string + expectedNormalizeModel string + expectError bool + } + + const validVendorName = "0x41" + const validNormalizeVendorName = "ARM Limited" + validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName) + + const validModelName = "8" + const validNormalizeModelName = "v8" + validModel := fmt.Sprintf(`%s : %s`, archCPUModelField, validModelName) + + validContents := fmt.Sprintf(` +a : b +%s +foo : bar +%s +`, validVendor, validModel) + + data := []testData{ + {"", "", "", "", "", true}, + {"invalid", "", "", "", "", true}, + {archCPUVendorField, "", "", "", "", true}, + {validVendor, "", "", "", "", true}, + {validModel, "", "", "", "", true}, + {validContents, validVendorName, validModelName, validNormalizeVendorName, validNormalizeModelName, false}, + } + + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + savedProcCPUInfo := procCPUInfo + + testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") + + // override + procCPUInfo = testProcCPUInfo + + defer func() { + procCPUInfo = savedProcCPUInfo + }() + + _, _, err = getCPUDetails() + // ENOENT + assert.Error(t, err) + assert.True(t, os.IsNotExist(err)) + + for _, d := range data { + err := createFile(procCPUInfo, d.contents) + assert.NoError(t, err) + + vendor, model, err := getCPUDetails() + + if d.expectError { + assert.Error(t, err, fmt.Sprintf("%+v", d)) + continue + } else { + assert.NoError(t, err, fmt.Sprintf("%+v", d)) + assert.Equal(t, d.expectedNormalizeVendor, vendor) + assert.Equal(t, d.expectedNormalizeModel, model) + } + } +} diff --git a/cli/kata-check_data_arm64_test.go b/cli/kata-check_data_arm64_test.go new file mode 100644 index 0000000000..8b1c76f5c0 --- /dev/null +++ b/cli/kata-check_data_arm64_test.go @@ -0,0 +1,27 @@ +// Copyright (c) 2018 ARM Limited +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +const testCPUInfoTemplate = ` +processor : 0 +BogoMIPS : 500.00 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid +CPU implementer : 0x41 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0xd07 +CPU revision : 2 + +processor : 1 +BogoMIPS : 500.00 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid +CPU implementer : 0x41 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0xd07 +CPU revision : 2 + +` diff --git a/cli/kata-check_ppc64le_test.go b/cli/kata-check_ppc64le_test.go index 30ed96dd6c..4e2510e7e8 100644 --- a/cli/kata-check_ppc64le_test.go +++ b/cli/kata-check_ppc64le_test.go @@ -207,3 +207,7 @@ func TestKvmIsUsable(t *testing.T) { err = kvmIsUsable() assert.Error(err) } + +func TestGetCPUDetails(t *testing.T) { + genericTestGetCPUDetails(t) +} diff --git a/cli/kata-check_test.go b/cli/kata-check_test.go index bc3ea2a3e7..06ee64e02f 100644 --- a/cli/kata-check_test.go +++ b/cli/kata-check_test.go @@ -137,6 +137,75 @@ func makeCPUInfoFile(path, vendorID, flags string) error { return ioutil.WriteFile(path, contents.Bytes(), testFileMode) } +func genericTestGetCPUDetails(t *testing.T) { + type testData struct { + contents string + expectedVendor string + expectedModel string + expectError bool + } + + const validVendorName = "a vendor" + validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName) + + const validModelName = "some CPU model" + validModel := fmt.Sprintf(`%s : %s`, archCPUModelField, validModelName) + + validContents := fmt.Sprintf(` +a : b +%s +foo : bar +%s +`, validVendor, validModel) + + data := []testData{ + {"", "", "", true}, + {"invalid", "", "", true}, + {archCPUVendorField, "", "", true}, + {validVendor, "", "", true}, + {validModel, "", "", true}, + {validContents, validVendorName, validModelName, false}, + } + + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + savedProcCPUInfo := procCPUInfo + + testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") + + // override + procCPUInfo = testProcCPUInfo + + defer func() { + procCPUInfo = savedProcCPUInfo + }() + + _, _, err = getCPUDetails() + // ENOENT + assert.Error(t, err) + assert.True(t, os.IsNotExist(err)) + + for _, d := range data { + err := createFile(procCPUInfo, d.contents) + assert.NoError(t, err) + + vendor, model, err := getCPUDetails() + + if d.expectError { + assert.Error(t, err, fmt.Sprintf("%+v", d)) + continue + } else { + assert.NoError(t, err, fmt.Sprintf("%+v", d)) + assert.Equal(t, d.expectedVendor, vendor) + assert.Equal(t, d.expectedModel, model) + } + } +} + func TestCheckGetCPUInfo(t *testing.T) { assert := assert.New(t) diff --git a/cli/kata-env_amd64_test.go b/cli/kata-env_amd64_test.go new file mode 100644 index 0000000000..870514f5ea --- /dev/null +++ b/cli/kata-env_amd64_test.go @@ -0,0 +1,10 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +func getExpectedHostDetails(tmpdir string) (HostInfo, error) { + return genericGetExpectedHostDetails(tmpdir) +} diff --git a/cli/kata-env_arm64_test.go b/cli/kata-env_arm64_test.go new file mode 100644 index 0000000000..e23fee82ad --- /dev/null +++ b/cli/kata-env_arm64_test.go @@ -0,0 +1,94 @@ +// Copyright (c) 2018 ARM Limited +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "path/filepath" + goruntime "runtime" +) + +func getExpectedHostDetails(tmpdir string) (HostInfo, error) { + type filesToCreate struct { + file string + contents string + } + + const expectedKernelVersion = "99.1" + const expectedArch = goruntime.GOARCH + + expectedDistro := DistroInfo{ + Name: "Foo", + Version: "42", + } + + expectedCPU := CPUInfo{ + Vendor: "0x41", + Model: "8", + } + + expectedNormalizeCPU := CPUInfo{ + Vendor: "ARM Limited", + Model: "v8", + } + + expectedHostDetails := HostInfo{ + Kernel: expectedKernelVersion, + Architecture: expectedArch, + Distro: expectedDistro, + CPU: expectedNormalizeCPU, + VMContainerCapable: true, + } + + testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") + testOSRelease := filepath.Join(tmpdir, "os-release") + + // XXX: This file is *NOT* created by this function on purpose + // (to ensure the only file checked by the tests is + // testOSRelease). osReleaseClr handling is tested in + // utils_test.go. + testOSReleaseClr := filepath.Join(tmpdir, "os-release-clr") + + testProcVersion := filepath.Join(tmpdir, "proc-version") + + // override + procVersion = testProcVersion + osRelease = testOSRelease + osReleaseClr = testOSReleaseClr + procCPUInfo = testProcCPUInfo + + procVersionContents := fmt.Sprintf("Linux version %s a b c", + expectedKernelVersion) + + osReleaseContents := fmt.Sprintf(` +NAME="%s" +VERSION_ID="%s" +`, expectedDistro.Name, expectedDistro.Version) + + procCPUInfoContents := fmt.Sprintf(` +%s : %s +%s : %s +`, + archCPUVendorField, + expectedCPU.Vendor, + archCPUModelField, + expectedCPU.Model) + + data := []filesToCreate{ + {procVersion, procVersionContents}, + {osRelease, osReleaseContents}, + {procCPUInfo, procCPUInfoContents}, + } + + for _, d := range data { + err := createFile(d.file, d.contents) + if err != nil { + return HostInfo{}, err + } + } + + return expectedHostDetails, nil +} diff --git a/cli/kata-env_ppc64le_test.go b/cli/kata-env_ppc64le_test.go new file mode 100644 index 0000000000..a9cd394b9e --- /dev/null +++ b/cli/kata-env_ppc64le_test.go @@ -0,0 +1,10 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +func getExpectedHostDetails(tmpdir string) (HostInfo, error) { + return genericGetExpectedHostDetails(tmpdir) +} diff --git a/cli/kata-env_test.go b/cli/kata-env_test.go index c86fe4ef2c..d8a9100cd8 100644 --- a/cli/kata-env_test.go +++ b/cli/kata-env_test.go @@ -152,7 +152,7 @@ func getExpectedAgentDetails(config oci.RuntimeConfig) (AgentInfo, error) { }, nil } -func getExpectedHostDetails(tmpdir string) (HostInfo, error) { +func genericGetExpectedHostDetails(tmpdir string) (HostInfo, error) { type filesToCreate struct { file string contents string diff --git a/cli/utils_test.go b/cli/utils_test.go index 69a131c9f0..2a7e5682ff 100644 --- a/cli/utils_test.go +++ b/cli/utils_test.go @@ -216,69 +216,6 @@ VERSION_ID="%s" } } -func TestGetCPUDetails(t *testing.T) { - type testData struct { - contents string - expectedVendor string - expectedModel string - expectError bool - } - - const validVendorName = "a vendor" - validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName) - - const validModelName = "some CPU model" - validModel := fmt.Sprintf(`%s : %s`, archCPUModelField, validModelName) - - validContents := fmt.Sprintf(` -a : b -%s -foo : bar -%s -`, validVendor, validModel) - - data := []testData{ - {"", "", "", true}, - {"invalid", "", "", true}, - {archCPUVendorField, "", "", true}, - {validVendor, "", "", true}, - {validModel, "", "", true}, - {validContents, validVendorName, validModelName, false}, - } - - tmpdir, err := ioutil.TempDir("", "") - if err != nil { - panic(err) - } - defer os.RemoveAll(tmpdir) - - testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") - - // override - procCPUInfo = testProcCPUInfo - - _, _, err = getCPUDetails() - // ENOENT - assert.Error(t, err) - assert.True(t, os.IsNotExist(err)) - - for _, d := range data { - err := createFile(procCPUInfo, d.contents) - assert.NoError(t, err) - - vendor, model, err := getCPUDetails() - - if d.expectError { - assert.Error(t, err, fmt.Sprintf("%+v", d)) - continue - } else { - assert.NoError(t, err, fmt.Sprintf("%+v", d)) - assert.Equal(t, d.expectedVendor, vendor) - assert.Equal(t, d.expectedModel, model) - } - } -} - func TestUtilsResolvePathEmptyPath(t *testing.T) { _, err := resolvePath("") assert.Error(t, err)