diff --git a/Makefile b/Makefile index 121f5a0419..57969f7c4f 100644 --- a/Makefile +++ b/Makefile @@ -291,6 +291,8 @@ const systemdUnitName = "$(PROJECT_TAG).target" // original URL for this project const projectURL = "$(PROJECT_URL)" +const defaultRootDirectory = "$(PKGRUNDIR)" + // commit is the git commit the runtime is compiled from. var commit = "$(COMMIT)" @@ -305,46 +307,12 @@ var checkCmd = fmt.Sprintf("%s-check", projectPrefix) var configFilePathOption = fmt.Sprintf("%s-config", projectPrefix) var showConfigPathsOption = fmt.Sprintf("%s-show-default-config-paths", projectPrefix) -var defaultHypervisorPath = "$(QEMUPATH)" -var defaultImagePath = "$(IMAGEPATH)" -var defaultKernelPath = "$(KERNELPATH)" -var defaultInitrdPath = "$(INITRDPATH)" -var defaultFirmwarePath = "$(FIRMWAREPATH)" -var defaultMachineAccelerators = "$(MACHINEACCELERATORS)" -var defaultShimPath = "$(SHIMPATH)" - -const defaultKernelParams = "$(KERNELPARAMS)" -const defaultMachineType = "$(MACHINETYPE)" -const defaultRootDirectory = "$(PKGRUNDIR)" - -const defaultVCPUCount uint32 = $(DEFVCPUS) -const defaultMaxVCPUCount uint32 = $(DEFMAXVCPUS) -const defaultMemSize uint32 = $(DEFMEMSZ) // MiB -const defaultMemSlots uint32 = $(DEFMEMSLOTS) -const defaultBridgesCount uint32 = $(DEFBRIDGES) -const defaultInterNetworkingModel = "$(DEFNETWORKMODEL)" -const defaultDisableBlockDeviceUse bool = $(DEFDISABLEBLOCK) -const defaultBlockDeviceDriver = "$(DEFBLOCKSTORAGEDRIVER)" -const defaultEnableIOThreads bool = $(DEFENABLEIOTHREADS) -const defaultEnableMemPrealloc bool = $(DEFENABLEMEMPREALLOC) -const defaultEnableHugePages bool = $(DEFENABLEHUGEPAGES) -const defaultEnableSwap bool = $(DEFENABLESWAP) -const defaultEnableDebug bool = $(DEFENABLEDEBUG) -const defaultDisableNestingChecks bool = $(DEFDISABLENESTINGCHECKS) -const defaultMsize9p uint32 = $(DEFMSIZE9P) -const defaultHotplugVFIOOnRootBus bool = $(DEFHOTPLUGVFIOONROOTBUS) -const defaultEntropySource = "$(DEFENTROPYSOURCE)" -const defaultGuestHookPath string = "" - // Default config file used by stateless systems. var defaultRuntimeConfiguration = "$(CONFIG_PATH)" // Alternate config file that takes precedence over // defaultRuntimeConfiguration. var defaultSysConfRuntimeConfiguration = "$(SYSCONFIG)" - -var defaultProxyPath = "$(PROXYPATH)" -var defaultNetmonPath = "$(NETMONPATH)" endef export GENERATED_CODE diff --git a/cli/kata-check.go b/cli/kata-check.go index 8f6b1057c1..713a3e5678 100644 --- a/cli/kata-check.go +++ b/cli/kata-check.go @@ -23,6 +23,7 @@ import ( "strings" "syscall" + "github.com/kata-containers/runtime/pkg/katautils" vc "github.com/kata-containers/runtime/virtcontainers" "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -70,7 +71,7 @@ var ( // getCPUInfo returns details of the first CPU read from the specified cpuinfo file func getCPUInfo(cpuInfoFile string) (string, error) { - text, err := getFileContents(cpuInfoFile) + text, err := katautils.GetFileContents(cpuInfoFile) if err != nil { return "", err } @@ -206,7 +207,7 @@ func checkKernelModules(modules map[string]kernelModule, handler kernelParamHand for param, expected := range details.parameters { path := filepath.Join(sysModuleDir, module, moduleParamDir, param) - value, err := getFileContents(path) + value, err := katautils.GetFileContents(path) if err != nil { return 0, err } diff --git a/cli/kata-check_ppc64le.go b/cli/kata-check_ppc64le.go index 627a7e9dde..2dbf5c8516 100644 --- a/cli/kata-check_ppc64le.go +++ b/cli/kata-check_ppc64le.go @@ -10,6 +10,7 @@ import ( "os/exec" "strings" + "github.com/kata-containers/runtime/pkg/katautils" "github.com/sirupsen/logrus" ) @@ -60,7 +61,7 @@ func hostIsVMContainerCapable(details vmContainerCapableDetails) error { return err } - text, err := getFileContents(details.cpuInfoFile) + text, err := katautils.GetFileContents(details.cpuInfoFile) if err != nil { return err } diff --git a/cli/kata-env_test.go b/cli/kata-env_test.go index 8defce9c58..298010d442 100644 --- a/cli/kata-env_test.go +++ b/cli/kata-env_test.go @@ -24,8 +24,10 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" + "github.com/kata-containers/runtime/pkg/katautils" "github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/stretchr/testify/assert" + "strconv" ) const testProxyVersion = "proxy version 0.1" @@ -33,6 +35,20 @@ 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" +const defaultVCPUCount uint32 = 1 +const defaultMaxVCPUCount uint32 = 0 +const defaultMemSize uint32 = 2048 // MiB +const defaultMsize9p uint32 = 8192 +const defaultGuestHookPath string = "" + +var ( + hypervisorDebug = false + proxyDebug = false + runtimeDebug = false + shimDebug = false + netmonDebug = false +) + // makeVersionBinary creates a shell script with the specified file // name. When run as "file --version", it will display the specified // version to stdout and exit successfully. @@ -52,6 +68,57 @@ func makeVersionBinary(file, version string) error { return nil } +func createConfig(configPath string, fileData string) error { + + err := ioutil.WriteFile(configPath, []byte(fileData), testFileMode) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to create config file %s %v\n", configPath, err) + return err + } + + return nil +} + +func makeRuntimeConfigFileData(hypervisor, hypervisorPath, kernelPath, imagePath, kernelParams, machineType, shimPath, proxyPath, netmonPath, logPath string, disableBlock bool, blockDeviceDriver string, enableIOThreads bool, hotplugVFIOOnRootBus, disableNewNetNs bool) string { + return ` + # Runtime configuration file + + [hypervisor.` + hypervisor + `] + path = "` + hypervisorPath + `" + kernel = "` + kernelPath + `" + block_device_driver = "` + blockDeviceDriver + `" + kernel_params = "` + kernelParams + `" + image = "` + imagePath + `" + machine_type = "` + machineType + `" + default_vcpus = ` + strconv.FormatUint(uint64(defaultVCPUCount), 10) + ` + default_maxvcpus = ` + strconv.FormatUint(uint64(defaultMaxVCPUCount), 10) + ` + default_memory = ` + strconv.FormatUint(uint64(defaultMemSize), 10) + ` + disable_block_device_use = ` + strconv.FormatBool(disableBlock) + ` + enable_iothreads = ` + strconv.FormatBool(enableIOThreads) + ` + hotplug_vfio_on_root_bus = ` + strconv.FormatBool(hotplugVFIOOnRootBus) + ` + msize_9p = ` + strconv.FormatUint(uint64(defaultMsize9p), 10) + ` + enable_debug = ` + strconv.FormatBool(hypervisorDebug) + ` + guest_hook_path = "` + defaultGuestHookPath + `" + + [proxy.kata] + enable_debug = ` + strconv.FormatBool(proxyDebug) + ` + path = "` + proxyPath + `" + + [shim.kata] + path = "` + shimPath + `" + enable_debug = ` + strconv.FormatBool(shimDebug) + ` + + [agent.kata] + + [netmon] + path = "` + netmonPath + `" + enable_debug = ` + strconv.FormatBool(netmonDebug) + ` + + [runtime] + enable_debug = ` + strconv.FormatBool(runtimeDebug) + ` + disable_new_netns= ` + strconv.FormatBool(disableNewNetNs) +} + func makeRuntimeConfig(prefixDir string) (configFile string, config oci.RuntimeConfig, err error) { const logPath = "/log/path" hypervisorPath := filepath.Join(prefixDir, "hypervisor") @@ -76,7 +143,7 @@ func makeRuntimeConfig(prefixDir string) (configFile string, config oci.RuntimeC for _, file := range filesToCreate { // files must exist and be >0 bytes. - err := writeFile(file, "foo", testFileMode) + err := katautils.WriteFile(file, "foo", testFileMode) if err != nil { return "", oci.RuntimeConfig{}, err } @@ -126,7 +193,7 @@ func makeRuntimeConfig(prefixDir string) (configFile string, config oci.RuntimeC return "", oci.RuntimeConfig{}, err } - _, config, err = loadConfiguration(configFile, true) + _, config, _, err = katautils.LoadConfiguration(configFile, true, false) if err != nil { return "", oci.RuntimeConfig{}, err } @@ -812,7 +879,7 @@ func testEnvShowTOMLSettings(t *testing.T, tmpdir string, tmpfile *os.File) erro return err } - contents, err := getFileContents(tmpfile.Name()) + contents, err := katautils.GetFileContents(tmpfile.Name()) assert.NoError(t, err) buf := new(bytes.Buffer) @@ -881,7 +948,7 @@ func testEnvShowJSONSettings(t *testing.T, tmpdir string, tmpfile *os.File) erro return err } - contents, err := getFileContents(tmpfile.Name()) + contents, err := katautils.GetFileContents(tmpfile.Name()) assert.NoError(t, err) buf := new(bytes.Buffer) diff --git a/cli/list_test.go b/cli/list_test.go index eee924292f..268ef469cb 100644 --- a/cli/list_test.go +++ b/cli/list_test.go @@ -18,6 +18,7 @@ import ( "testing" "time" + "github.com/kata-containers/runtime/pkg/katautils" vc "github.com/kata-containers/runtime/virtcontainers" vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" @@ -686,7 +687,7 @@ func TestListCLIFunctionQuiet(t *testing.T) { assert.NoError(err) f.Close() - text, err := getFileContents(output) + text, err := katautils.GetFileContents(output) assert.NoError(err) trimmed := strings.TrimSpace(text) diff --git a/cli/main.go b/cli/main.go index 7926a12c05..607fd92668 100644 --- a/cli/main.go +++ b/cli/main.go @@ -17,6 +17,7 @@ import ( "strings" "syscall" + "github.com/kata-containers/runtime/pkg/katautils" "github.com/kata-containers/runtime/pkg/signals" vc "github.com/kata-containers/runtime/virtcontainers" vf "github.com/kata-containers/runtime/virtcontainers/factory" @@ -240,11 +241,18 @@ func setExternalLoggers(ctx context.Context, logger *logrus.Entry) { // Set the OCI package logger. oci.SetLogger(ctx, logger) + + // Set the katautils package logger + katautils.SetLogger(ctx, logger, originalLoggerLevel) } // beforeSubcommands is the function to perform preliminary checks // before command-line parsing occurs. func beforeSubcommands(c *cli.Context) error { + var configFile string + var runtimeConfig oci.RuntimeConfig + var err error + handleShowConfig(c) if userWantsUsage(c) || (c.NArg() == 1 && (c.Args()[0] == checkCmd)) { @@ -297,11 +305,16 @@ func beforeSubcommands(c *cli.Context) error { ignoreLogging = true } - configFile, runtimeConfig, err := loadConfiguration(c.GlobalString(configFilePathOption), ignoreLogging) + katautils.SetConfigOptions(name, defaultRuntimeConfiguration, defaultSysConfRuntimeConfiguration) + + configFile, runtimeConfig, tracing, err = katautils.LoadConfiguration(c.GlobalString(configFilePathOption), ignoreLogging, false) if err != nil { fatal(err) } + debug = runtimeConfig.Debug + crashOnError = runtimeConfig.Debug + if traceRootSpan != "" { // Create the tracer. // @@ -336,7 +349,7 @@ func beforeSubcommands(c *cli.Context) error { // paths. If so, it will display them and then exit. func handleShowConfig(context *cli.Context) { if context.GlobalBool(showConfigPathsOption) { - files := getDefaultConfigFilePaths() + files := katautils.GetDefaultConfigFilePaths() for _, file := range files { fmt.Fprintf(defaultOutputFile, "%s\n", file) diff --git a/cli/main_test.go b/cli/main_test.go index 04ddd8e716..161406c035 100644 --- a/cli/main_test.go +++ b/cli/main_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/dlespiau/covertool/pkg/cover" + "github.com/kata-containers/runtime/pkg/katautils" vc "github.com/kata-containers/runtime/virtcontainers" "github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" @@ -747,7 +748,7 @@ func TestMainBeforeSubCommandsShowCCConfigPaths(t *testing.T) { _ = beforeSubcommands(ctx) assert.Equal(exitStatus, 0) - text, err := getFileContents(output) + text, err := katautils.GetFileContents(output) assert.NoError(err) lines := strings.Split(text, "\n") @@ -801,7 +802,7 @@ func TestMainFatal(t *testing.T) { fatal(exitError) assert.Equal(exitStatus, 1) - text, err := getFileContents(output) + text, err := katautils.GetFileContents(output) assert.NoError(err) trimmed := strings.TrimSpace(text) diff --git a/cli/oci.go b/cli/oci.go index c54e6d60f9..67077843e5 100644 --- a/cli/oci.go +++ b/cli/oci.go @@ -18,6 +18,7 @@ import ( "syscall" "github.com/containernetworking/plugins/pkg/ns" + "github.com/kata-containers/runtime/pkg/katautils" vc "github.com/kata-containers/runtime/virtcontainers" "github.com/opencontainers/runc/libcontainer/utils" ) @@ -107,7 +108,7 @@ func validCreateParams(ctx context.Context, containerID, bundlePath string) (str return "", fmt.Errorf("Invalid bundle path '%s', it should be a directory", bundlePath) } - resolved, err := resolvePath(bundlePath) + resolved, err := katautils.ResolvePath(bundlePath) if err != nil { return "", err } diff --git a/cli/utils.go b/cli/utils.go index b13dff9338..3e25c3a9f3 100644 --- a/cli/utils.go +++ b/cli/utils.go @@ -7,12 +7,11 @@ package main import ( "fmt" - "io/ioutil" "os" "os/exec" - "path/filepath" "strings" - "syscall" + + "github.com/kata-containers/runtime/pkg/katautils" ) const ( @@ -37,15 +36,6 @@ func fileExists(path string) bool { return true } -func getFileContents(file string) (string, error) { - bytes, err := ioutil.ReadFile(file) - if err != nil { - return "", err - } - - return string(bytes), nil -} - // IsEphemeralStorage returns true if the given path // to the storage belongs to kubernetes ephemeral storage // @@ -67,7 +57,7 @@ func IsEphemeralStorage(path string) bool { } func getKernelVersion() (string, error) { - contents, err := getFileContents(procVersion) + contents, err := katautils.GetFileContents(procVersion) if err != nil { return "", err } @@ -90,7 +80,7 @@ func getDistroDetails() (name, version string, err error) { files := []string{osRelease, osReleaseClr} for _, file := range files { - contents, err := getFileContents(file) + contents, err := katautils.GetFileContents(file) if err != nil { if os.IsNotExist(err) { continue @@ -162,31 +152,6 @@ func genericGetCPUDetails() (vendor, model string, err error) { return vendor, model, nil } -// resolvePath returns the fully resolved and expanded value of the -// specified path. -func resolvePath(path string) (string, error) { - if path == "" { - return "", fmt.Errorf("path must be specified") - } - - absolute, err := filepath.Abs(path) - if err != nil { - return "", err - } - - resolved, err := filepath.EvalSymlinks(absolute) - if err != nil { - if os.IsNotExist(err) { - // Make the error clearer than the default - return "", fmt.Errorf("file %v does not exist", absolute) - } - - return "", err - } - - return resolved, nil -} - // runCommandFull returns the commands space-trimmed standard output and // error on success. Note that if the command fails, the requested output will // still be returned, along with an error. @@ -210,30 +175,3 @@ func runCommandFull(args []string, includeStderr bool) (string, error) { func runCommand(args []string) (string, error) { return runCommandFull(args, false) } - -// writeFile write data into specified file -func writeFile(filePath string, data string, fileMode os.FileMode) error { - // Normally dir should not be empty, one case is that cgroup subsystem - // is not mounted, we will get empty dir, and we want it fail here. - if filePath == "" { - return fmt.Errorf("no such file for %s", filePath) - } - - if err := ioutil.WriteFile(filePath, []byte(data), fileMode); err != nil { - return fmt.Errorf("failed to write %v to %v: %v", data, filePath, err) - } - - return nil -} - -// fileSize returns the number of bytes in the specified file -func fileSize(file string) (int64, error) { - st := syscall.Stat_t{} - - err := syscall.Stat(file, &st) - if err != nil { - return 0, err - } - - return st.Size, nil -} diff --git a/cli/utils_test.go b/cli/utils_test.go index 4fa64e0d6c..6daea066ee 100644 --- a/cli/utils_test.go +++ b/cli/utils_test.go @@ -9,9 +9,7 @@ import ( "fmt" "io/ioutil" "os" - "path" "path/filepath" - "syscall" "testing" "github.com/stretchr/testify/assert" @@ -52,48 +50,6 @@ func TestIsEphemeralStorage(t *testing.T) { } } -func TestGetFileContents(t *testing.T) { - type testData struct { - contents string - } - - data := []testData{ - {""}, - {" "}, - {"\n"}, - {"\n\n"}, - {"\n\n\n"}, - {"foo"}, - {"foo\nbar"}, - {"processor : 0\nvendor_id : GenuineIntel\n"}, - } - - dir, err := ioutil.TempDir(testDir, "") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - file := filepath.Join(dir, "foo") - - // file doesn't exist - _, err = getFileContents(file) - assert.Error(t, err) - - for _, d := range data { - // create the file - err = ioutil.WriteFile(file, []byte(d.contents), testFileMode) - if err != nil { - t.Fatal(err) - } - defer os.Remove(file) - - contents, err := getFileContents(file) - assert.NoError(t, err) - assert.Equal(t, contents, d.contents) - } -} - func TestGetKernelVersion(t *testing.T) { type testData struct { contents string @@ -230,76 +186,6 @@ VERSION_ID="%s" } } -func TestUtilsResolvePathEmptyPath(t *testing.T) { - _, err := resolvePath("") - assert.Error(t, err) -} - -func TestUtilsResolvePathValidPath(t *testing.T) { - dir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - target := path.Join(dir, "target") - linkDir := path.Join(dir, "a/b/c") - linkFile := path.Join(linkDir, "link") - - err = createEmptyFile(target) - assert.NoError(t, err) - - absolute, err := filepath.Abs(target) - assert.NoError(t, err) - - resolvedTarget, err := filepath.EvalSymlinks(absolute) - assert.NoError(t, err) - - err = os.MkdirAll(linkDir, testDirMode) - assert.NoError(t, err) - - err = syscall.Symlink(target, linkFile) - assert.NoError(t, err) - - resolvedLink, err := resolvePath(linkFile) - assert.NoError(t, err) - - assert.Equal(t, resolvedTarget, resolvedLink) -} - -func TestUtilsResolvePathENOENT(t *testing.T) { - dir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - - target := path.Join(dir, "target") - linkDir := path.Join(dir, "a/b/c") - linkFile := path.Join(linkDir, "link") - - err = createEmptyFile(target) - assert.NoError(t, err) - - err = os.MkdirAll(linkDir, testDirMode) - assert.NoError(t, err) - - err = syscall.Symlink(target, linkFile) - assert.NoError(t, err) - - cwd, err := os.Getwd() - assert.NoError(t, err) - defer os.Chdir(cwd) - - err = os.Chdir(dir) - assert.NoError(t, err) - - err = os.RemoveAll(dir) - assert.NoError(t, err) - - _, err = resolvePath(filepath.Base(linkFile)) - assert.Error(t, err) -} - func TestUtilsRunCommand(t *testing.T) { output, err := runCommand([]string{"true"}) assert.NoError(t, err) @@ -343,56 +229,3 @@ func TestUtilsRunCommandInvalidCmds(t *testing.T) { assert.Equal(t, "", output) } } - -func TestWriteFileErrWriteFail(t *testing.T) { - assert := assert.New(t) - - err := writeFile("", "", 0000) - assert.Error(err) -} - -func TestWriteFileErrNoPath(t *testing.T) { - assert := assert.New(t) - - dir, err := ioutil.TempDir(testDir, "") - assert.NoError(err) - defer os.RemoveAll(dir) - - // attempt to write a file over an existing directory - err = writeFile(dir, "", 0000) - assert.Error(err) -} - -func TestFileSize(t *testing.T) { - assert := assert.New(t) - - dir, err := ioutil.TempDir(testDir, "") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - file := filepath.Join(dir, "foo") - - // ENOENT - _, err = fileSize(file) - assert.Error(err) - - err = createEmptyFile(file) - assert.NoError(err) - - // zero size - size, err := fileSize(file) - assert.NoError(err) - assert.Equal(size, int64(0)) - - msg := "hello" - msgLen := len(msg) - - err = writeFile(file, msg, testFileMode) - assert.NoError(err) - - size, err = fileSize(file) - assert.NoError(err) - assert.Equal(size, int64(msgLen)) -} diff --git a/pkg/katautils/config-settings.go b/pkg/katautils/config-settings.go new file mode 100644 index 0000000000..b05d69827b --- /dev/null +++ b/pkg/katautils/config-settings.go @@ -0,0 +1,50 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Note that some variables are "var" to allow them to be modified +// by the tests. + +package katautils + +var defaultHypervisorPath = "/usr/bin/qemu-lite-system-x86_64" +var defaultImagePath = "/usr/share/kata-containers/kata-containers.img" +var defaultKernelPath = "/usr/share/kata-containers/vmlinuz.container" +var defaultInitrdPath = "/usr/share/kata-containers/kata-containers-initrd.img" +var defaultFirmwarePath = "" +var defaultMachineAccelerators = "" +var defaultShimPath = "/usr/libexec/kata-containers/kata-shim" + +const defaultKernelParams = "" +const defaultMachineType = "pc" + +const defaultVCPUCount uint32 = 1 +const defaultMaxVCPUCount uint32 = 0 +const defaultMemSize uint32 = 2048 // MiB +const defaultMemSlots uint32 = 10 +const defaultBridgesCount uint32 = 1 +const defaultInterNetworkingModel = "macvtap" +const defaultDisableBlockDeviceUse bool = false +const defaultBlockDeviceDriver = "virtio-scsi" +const defaultEnableIOThreads bool = false +const defaultEnableMemPrealloc bool = false +const defaultEnableHugePages bool = false +const defaultEnableSwap bool = false +const defaultEnableDebug bool = false +const defaultDisableNestingChecks bool = false +const defaultMsize9p uint32 = 8192 +const defaultHotplugVFIOOnRootBus bool = false +const defaultEntropySource = "/dev/urandom" +const defaultGuestHookPath string = "" + +// Default config file used by stateless systems. +var defaultRuntimeConfiguration = "/usr/share/defaults/kata-containers/configuration.toml" + +// Alternate config file that takes precedence over +// defaultRuntimeConfiguration. +var defaultSysConfRuntimeConfiguration = "/etc/kata-containers/configuration.toml" + +var name = "kata" +var defaultProxyPath = "/usr/libexec/kata-containers/kata-proxy" +var defaultNetmonPath = "/usr/libexec/kata-containers/kata-netmon" diff --git a/cli/config.go b/pkg/katautils/config.go similarity index 88% rename from cli/config.go rename to pkg/katautils/config.go index 6fd068f036..e4bd97f99a 100644 --- a/cli/config.go +++ b/pkg/katautils/config.go @@ -1,9 +1,10 @@ // Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. // // SPDX-License-Identifier: Apache-2.0 // -package main +package katautils import ( "errors" @@ -21,11 +22,14 @@ import ( const ( defaultHypervisor = vc.QemuHypervisor - defaultProxy = vc.KataProxyType - defaultShim = vc.KataShimType defaultAgent = vc.KataContainersAgent ) +var ( + defaultProxy = vc.KataProxyType + defaultShim = vc.KataShimType +) + // The TOML configuration file contains a number of sections (or // tables). The names of these tables are in dotted ("nested table") // form: @@ -134,7 +138,7 @@ func (h hypervisor) path() (string, error) { p = defaultHypervisorPath } - return resolvePath(p) + return ResolvePath(p) } func (h hypervisor) kernel() (string, error) { @@ -144,7 +148,7 @@ func (h hypervisor) kernel() (string, error) { p = defaultKernelPath } - return resolvePath(p) + return ResolvePath(p) } func (h hypervisor) initrd() (string, error) { @@ -154,7 +158,7 @@ func (h hypervisor) initrd() (string, error) { return "", nil } - return resolvePath(p) + return ResolvePath(p) } func (h hypervisor) image() (string, error) { @@ -164,7 +168,7 @@ func (h hypervisor) image() (string, error) { return "", nil } - return resolvePath(p) + return ResolvePath(p) } func (h hypervisor) firmware() (string, error) { @@ -177,7 +181,7 @@ func (h hypervisor) firmware() (string, error) { p = defaultFirmwarePath } - return resolvePath(p) + return ResolvePath(p) } func (h hypervisor) machineAccelerators() string { @@ -330,7 +334,7 @@ func (s shim) path() (string, error) { p = defaultShimPath } - return resolvePath(p) + return ResolvePath(p) } func (s shim) debug() bool { @@ -401,10 +405,10 @@ func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) { useVSock := false if h.useVSock() { if utils.SupportsVsocks() { - kataLog.Info("vsock supported") + kataUtilsLogger.Info("vsock supported") useVSock = true } else { - kataLog.Warn("No vsock support, falling back to legacy serial port") + kataUtilsLogger.Warn("No vsock support, falling back to legacy serial port") } } @@ -527,15 +531,9 @@ func updateRuntimeConfig(configPath string, tomlConf tomlConfig, config *oci.Run return nil } -// loadConfiguration loads the configuration file and converts it into a -// runtime configuration. -// -// If ignoreLogging is true, the system logger will not be initialised nor -// will this function make any log calls. -// -// All paths are resolved fully meaning if this function does not return an -// error, all paths are valid at the time of the call. -func loadConfiguration(configPath string, ignoreLogging bool) (resolvedConfigPath string, config oci.RuntimeConfig, err error) { +func initConfig(builtIn bool) (config oci.RuntimeConfig, err error) { + var defaultAgentConfig interface{} + defaultHypervisorConfig := vc.HypervisorConfig{ HypervisorPath: defaultHypervisorPath, KernelPath: defaultKernelPath, @@ -562,10 +560,17 @@ func loadConfiguration(configPath string, ignoreLogging bool) (resolvedConfigPat err = config.InterNetworkModel.SetModel(defaultInterNetworkingModel) if err != nil { - return "", config, err + return oci.RuntimeConfig{}, err } - defaultAgentConfig := vc.HyperConfig{} + defaultAgentConfig = vc.HyperConfig{} + + if builtIn { + defaultProxy = vc.KataBuiltInProxyType + defaultShim = vc.KataBuiltInShimType + + defaultAgentConfig = vc.KataAgentConfig{LongLiveConn: true} + } config = oci.RuntimeConfig{ HypervisorType: defaultHypervisor, @@ -576,37 +581,51 @@ func loadConfiguration(configPath string, ignoreLogging bool) (resolvedConfigPat ShimType: defaultShim, } + return config, nil +} + +// LoadConfiguration loads the configuration file and converts it into a +// runtime configuration. +// +// If ignoreLogging is true, the system logger will not be initialised nor +// will this function make any log calls. +// +// All paths are resolved fully meaning if this function does not return an +// error, all paths are valid at the time of the call. +func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolvedConfigPath string, config oci.RuntimeConfig, tracing bool, err error) { var resolved string + config, err = initConfig(builtIn) + if err != nil { + return "", oci.RuntimeConfig{}, tracing, err + } + if configPath == "" { resolved, err = getDefaultConfigFile() } else { - resolved, err = resolvePath(configPath) + resolved, err = ResolvePath(configPath) } if err != nil { - return "", config, fmt.Errorf("Cannot find usable config file (%v)", err) + return "", config, tracing, fmt.Errorf("Cannot find usable config file (%v)", err) } configData, err := ioutil.ReadFile(resolved) if err != nil { - return "", config, err + return "", config, tracing, err } var tomlConf tomlConfig _, err = toml.Decode(string(configData), &tomlConf) if err != nil { - return "", config, err + return "", config, tracing, err } - if tomlConf.Runtime.Debug { - config.Debug = true - debug = true - crashOnError = true - } else { + config.Debug = tomlConf.Runtime.Debug + if !tomlConf.Runtime.Debug { // If debug is not required, switch back to the original // default log priority, otherwise continue in debug mode. - kataLog.Logger.Level = originalLoggerLevel + kataUtilsLogger.Logger.Level = originalLoggerLevel } tracing = tomlConf.Runtime.Tracing @@ -614,17 +633,17 @@ func loadConfiguration(configPath string, ignoreLogging bool) (resolvedConfigPat if tomlConf.Runtime.InterNetworkModel != "" { err = config.InterNetworkModel.SetModel(tomlConf.Runtime.InterNetworkModel) if err != nil { - return "", config, err + return "", config, tracing, err } } if !ignoreLogging { - err = handleSystemLog("", "") + err := handleSystemLog("", "") if err != nil { - return "", config, err + return "", config, tracing, err } - kataLog.WithFields( + kataUtilsLogger.WithFields( logrus.Fields{ "format": "TOML", "file": resolved, @@ -632,26 +651,26 @@ func loadConfiguration(configPath string, ignoreLogging bool) (resolvedConfigPat } if err := updateRuntimeConfig(resolved, tomlConf, &config); err != nil { - return "", config, err + return "", config, tracing, err } config.DisableNewNetNs = tomlConf.Runtime.DisableNewNetNs if err := checkNetNsConfig(config); err != nil { - return "", config, err + return "", config, tracing, err } // use no proxy if HypervisorConfig.UseVSock is true if config.HypervisorConfig.UseVSock { - kataLog.Info("VSOCK supported, configure to not use proxy") + kataUtilsLogger.Info("VSOCK supported, configure to not use proxy") config.ProxyType = vc.NoProxyType config.ProxyConfig = vc.ProxyConfig{} } if err := checkHypervisorConfig(config.HypervisorConfig); err != nil { - return "", config, err + return "", config, tracing, err } - return resolved, config, nil + return resolved, config, tracing, nil } // checkNetNsConfig performs sanity checks on disable_new_netns config. @@ -714,7 +733,6 @@ func checkHypervisorConfig(config vc.HypervisorConfig) error { msg := fmt.Sprintf("VM memory (%dMB) smaller than image %q size (%dMB)", memSizeMB, image.path, imageSizeMB) - if imageSizeMB >= memSizeMB { if image.initrd { // Initrd's need to be fully read into memory @@ -726,7 +744,7 @@ func checkHypervisorConfig(config vc.HypervisorConfig) error { // unusual to have an image larger // than the amount of memory assigned // to the VM. - kataLog.Warn(msg) + kataUtilsLogger.Warn(msg) } } } @@ -734,9 +752,9 @@ func checkHypervisorConfig(config vc.HypervisorConfig) error { return nil } -// getDefaultConfigFilePaths returns a list of paths that will be +// GetDefaultConfigFilePaths returns a list of paths that will be // considered as configuration files in priority order. -func getDefaultConfigFilePaths() []string { +func GetDefaultConfigFilePaths() []string { return []string{ // normally below "/etc" defaultSysConfRuntimeConfiguration, @@ -752,8 +770,8 @@ func getDefaultConfigFilePaths() []string { func getDefaultConfigFile() (string, error) { var errs []string - for _, file := range getDefaultConfigFilePaths() { - resolved, err := resolvePath(file) + for _, file := range GetDefaultConfigFilePaths() { + resolved, err := ResolvePath(file) if err == nil { return resolved, nil } @@ -763,3 +781,18 @@ func getDefaultConfigFile() (string, error) { return "", errors.New(strings.Join(errs, ", ")) } + +// SetConfigOptions will override some of the defaults settings. +func SetConfigOptions(n, runtimeConfig, sysRuntimeConfig string) { + if n != "" { + name = n + } + + if runtimeConfig != "" { + defaultRuntimeConfiguration = runtimeConfig + } + + if sysRuntimeConfig != "" { + defaultSysConfRuntimeConfiguration = sysRuntimeConfig + } +} diff --git a/cli/config_test.go b/pkg/katautils/config_test.go similarity index 98% rename from cli/config_test.go rename to pkg/katautils/config_test.go index b9a4ff251a..bd150db444 100644 --- a/cli/config_test.go +++ b/pkg/katautils/config_test.go @@ -1,9 +1,10 @@ // Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. // // SPDX-License-Identifier: Apache-2.0 // -package main +package katautils import ( "bytes" @@ -140,7 +141,7 @@ func createAllRuntimeConfigFiles(dir, hypervisor string) (config testRuntimeConf for _, file := range files { // create the resource (which must be >0 bytes) - err := writeFile(file, "foo", testFileMode) + err := WriteFile(file, "foo", testFileMode) if err != nil { return config, err } @@ -248,7 +249,7 @@ func testLoadConfiguration(t *testing.T, dir string, assert.NoError(t, err) } - resolvedConfigPath, config, err := loadConfiguration(file, ignoreLogging) + resolvedConfigPath, config, _, err := LoadConfiguration(file, ignoreLogging, false) if expectFail { assert.Error(t, err) @@ -476,7 +477,7 @@ func TestConfigLoadConfigurationFailTOMLConfigFileDuplicatedData(t *testing.T) { func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { expectFail := true - text, err := getFileContents(config.ConfigPath) + text, err := GetFileContents(config.ConfigPath) if err != nil { return expectFail, err } @@ -530,7 +531,7 @@ func TestMinimalRuntimeConfig(t *testing.T) { defaultKernelPath = kernelPath for _, file := range []string{defaultImagePath, defaultInitrdPath, defaultHypervisorPath, defaultKernelPath} { - err = writeFile(file, "foo", testFileMode) + err = WriteFile(file, "foo", testFileMode) if err != nil { t.Fatal(err) } @@ -557,7 +558,7 @@ func TestMinimalRuntimeConfig(t *testing.T) { t.Fatal(err) } - _, config, err := loadConfiguration(configPath, false) + _, config, _, err := LoadConfiguration(configPath, false, false) if err == nil { t.Fatalf("Expected loadConfiguration to fail as shim path does not exist: %+v", config) } @@ -582,7 +583,7 @@ func TestMinimalRuntimeConfig(t *testing.T) { t.Error(err) } - _, config, err = loadConfiguration(configPath, false) + _, config, _, err = LoadConfiguration(configPath, false, false) if err != nil { t.Fatal(err) } @@ -675,7 +676,7 @@ func TestMinimalRuntimeConfigWithVsock(t *testing.T) { defaultKernelPath = kernelPath for _, file := range []string{proxyPath, shimPath, hypervisorPath, kernelPath, imagePath} { - err = writeFile(file, "foo", testFileMode) + err = WriteFile(file, "foo", testFileMode) if err != nil { t.Fatal(err) } @@ -711,7 +712,7 @@ func TestMinimalRuntimeConfigWithVsock(t *testing.T) { t.Fatal(err) } - _, config, err := loadConfiguration(configPath, false) + _, config, _, err := LoadConfiguration(configPath, false, false) if err != nil { t.Fatal(err) } @@ -1152,7 +1153,7 @@ func TestShimDefaults(t *testing.T) { func TestGetDefaultConfigFilePaths(t *testing.T) { assert := assert.New(t) - results := getDefaultConfigFilePaths() + results := GetDefaultConfigFilePaths() // There should be atleast two config file locations assert.True(len(results) >= 2) @@ -1405,7 +1406,7 @@ func TestCheckHypervisorConfig(t *testing.T) { fileData := strings.Repeat("X", int(fileSizeBytes)) for _, file := range []string{image, initrd} { - err = writeFile(file, fileData, testFileMode) + err = WriteFile(file, fileData, testFileMode) assert.NoError(err) } @@ -1444,13 +1445,13 @@ func TestCheckHypervisorConfig(t *testing.T) { } for i, d := range data { - savedOut := kataLog.Logger.Out + savedOut := kataUtilsLogger.Logger.Out // create buffer to save logger output logBuf := &bytes.Buffer{} // capture output to buffer - kataLog.Logger.Out = logBuf + kataUtilsLogger.Logger.Out = logBuf config := vc.HypervisorConfig{ ImagePath: d.imagePath, @@ -1473,7 +1474,7 @@ func TestCheckHypervisorConfig(t *testing.T) { } // reset logger - kataLog.Logger.Out = savedOut + kataUtilsLogger.Logger.Out = savedOut } } diff --git a/cli/logger.go b/pkg/katautils/logger.go similarity index 76% rename from cli/logger.go rename to pkg/katautils/logger.go index a8ee2ae48b..544be045f3 100644 --- a/cli/logger.go +++ b/pkg/katautils/logger.go @@ -1,11 +1,13 @@ // Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. // // SPDX-License-Identifier: Apache-2.0 // -package main +package katautils import ( + "context" "log/syslog" "time" @@ -13,6 +15,19 @@ import ( lSyslog "github.com/sirupsen/logrus/hooks/syslog" ) +var originalLoggerLevel = logrus.InfoLevel +var kataUtilsLogger = logrus.NewEntry(logrus.New()) + +// SetLogger sets the logger for the factory. +func SetLogger(ctx context.Context, logger *logrus.Entry, level logrus.Level) { + fields := logrus.Fields{ + "source": "katautils", + } + + originalLoggerLevel = level + kataUtilsLogger = logger.WithFields(fields) +} + // sysLogHook wraps a syslog logrus hook and a formatter to be used for all // syslog entries. // @@ -64,7 +79,7 @@ func handleSystemLog(network, raddr string) error { return err } - kataLog.Logger.Hooks.Add(hook) + kataUtilsLogger.Logger.Hooks.Add(hook) return nil } diff --git a/cli/logger_test.go b/pkg/katautils/logger_test.go similarity index 95% rename from cli/logger_test.go rename to pkg/katautils/logger_test.go index 5b39d2f94b..9367eb6806 100644 --- a/cli/logger_test.go +++ b/pkg/katautils/logger_test.go @@ -1,9 +1,10 @@ // Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. // // SPDX-License-Identifier: Apache-2.0 // -package main +package katautils import ( "fmt" @@ -25,10 +26,10 @@ type testData struct { func init() { // Ensure all log levels are logged - kataLog.Logger.Level = logrus.DebugLevel + kataUtilsLogger.Logger.Level = logrus.DebugLevel // Discard log output - kataLog.Logger.Out = ioutil.Discard + kataUtilsLogger.Logger.Out = ioutil.Discard } func TestHandleSystemLog(t *testing.T) { diff --git a/pkg/katautils/utils.go b/pkg/katautils/utils.go new file mode 100644 index 0000000000..699ead9e87 --- /dev/null +++ b/pkg/katautils/utils.go @@ -0,0 +1,77 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "syscall" +) + +// ResolvePath returns the fully resolved and expanded value of the +// specified path. +func ResolvePath(path string) (string, error) { + if path == "" { + return "", fmt.Errorf("path must be specified") + } + + absolute, err := filepath.Abs(path) + if err != nil { + return "", err + } + + resolved, err := filepath.EvalSymlinks(absolute) + if err != nil { + if os.IsNotExist(err) { + // Make the error clearer than the default + return "", fmt.Errorf("file %v does not exist", absolute) + } + + return "", err + } + + return resolved, nil +} + +// fileSize returns the number of bytes in the specified file +func fileSize(file string) (int64, error) { + st := syscall.Stat_t{} + + err := syscall.Stat(file, &st) + if err != nil { + return 0, err + } + + return st.Size, nil +} + +// WriteFile write data into specified file +func WriteFile(filePath string, data string, fileMode os.FileMode) error { + // Normally dir should not be empty, one case is that cgroup subsystem + // is not mounted, we will get empty dir, and we want it fail here. + if filePath == "" { + return fmt.Errorf("no such file for %s", filePath) + } + + if err := ioutil.WriteFile(filePath, []byte(data), fileMode); err != nil { + return fmt.Errorf("failed to write %v to %v: %v", data, filePath, err) + } + + return nil +} + +// GetFileContents return the file contents as a string. +func GetFileContents(file string) (string, error) { + bytes, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + + return string(bytes), nil +} diff --git a/pkg/katautils/utils_test.go b/pkg/katautils/utils_test.go new file mode 100644 index 0000000000..a10f2c8676 --- /dev/null +++ b/pkg/katautils/utils_test.go @@ -0,0 +1,213 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "syscall" + "testing" + + "github.com/stretchr/testify/assert" +) + +const ( + testDirMode = os.FileMode(0750) + testFileMode = os.FileMode(0640) + + testDisabledNeedNonRoot = "Test disabled as requires non-root user" +) + +var testDir = "" + +func init() { + var err error + + fmt.Printf("INFO: creating test directory\n") + testDir, err = ioutil.TempDir("", fmt.Sprintf("%s-", name)) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create test directory: %v", err)) + } + + fmt.Printf("INFO: test directory is %v\n", testDir) +} + +func createFile(file, contents string) error { + return ioutil.WriteFile(file, []byte(contents), testFileMode) +} + +func createEmptyFile(path string) (err error) { + return ioutil.WriteFile(path, []byte(""), testFileMode) +} + +func TestUtilsResolvePathEmptyPath(t *testing.T) { + _, err := ResolvePath("") + assert.Error(t, err) +} + +func TestUtilsResolvePathValidPath(t *testing.T) { + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + target := path.Join(dir, "target") + linkDir := path.Join(dir, "a/b/c") + linkFile := path.Join(linkDir, "link") + + err = createEmptyFile(target) + assert.NoError(t, err) + + absolute, err := filepath.Abs(target) + assert.NoError(t, err) + + resolvedTarget, err := filepath.EvalSymlinks(absolute) + assert.NoError(t, err) + + err = os.MkdirAll(linkDir, testDirMode) + assert.NoError(t, err) + + err = syscall.Symlink(target, linkFile) + assert.NoError(t, err) + + resolvedLink, err := ResolvePath(linkFile) + assert.NoError(t, err) + + assert.Equal(t, resolvedTarget, resolvedLink) +} + +func TestUtilsResolvePathENOENT(t *testing.T) { + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + + target := path.Join(dir, "target") + linkDir := path.Join(dir, "a/b/c") + linkFile := path.Join(linkDir, "link") + + err = createEmptyFile(target) + assert.NoError(t, err) + + err = os.MkdirAll(linkDir, testDirMode) + assert.NoError(t, err) + + err = syscall.Symlink(target, linkFile) + assert.NoError(t, err) + + cwd, err := os.Getwd() + assert.NoError(t, err) + defer os.Chdir(cwd) + + err = os.Chdir(dir) + assert.NoError(t, err) + + err = os.RemoveAll(dir) + assert.NoError(t, err) + + _, err = ResolvePath(filepath.Base(linkFile)) + assert.Error(t, err) +} + +func TestFileSize(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir(testDir, "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + file := filepath.Join(dir, "foo") + + // ENOENT + _, err = fileSize(file) + assert.Error(err) + + err = createEmptyFile(file) + assert.NoError(err) + + // zero size + size, err := fileSize(file) + assert.NoError(err) + assert.Equal(size, int64(0)) + + msg := "hello" + msgLen := len(msg) + + err = WriteFile(file, msg, testFileMode) + assert.NoError(err) + + size, err = fileSize(file) + assert.NoError(err) + assert.Equal(size, int64(msgLen)) +} + +func TestWriteFileErrWriteFail(t *testing.T) { + assert := assert.New(t) + + err := WriteFile("", "", 0000) + assert.Error(err) +} + +func TestWriteFileErrNoPath(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(dir) + + // attempt to write a file over an existing directory + err = WriteFile(dir, "", 0000) + assert.Error(err) +} + +func TestGetFileContents(t *testing.T) { + type testData struct { + contents string + } + + data := []testData{ + {""}, + {" "}, + {"\n"}, + {"\n\n"}, + {"\n\n\n"}, + {"foo"}, + {"foo\nbar"}, + {"processor : 0\nvendor_id : GenuineIntel\n"}, + } + + dir, err := ioutil.TempDir(testDir, "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + file := filepath.Join(dir, "foo") + + // file doesn't exist + _, err = GetFileContents(file) + assert.Error(t, err) + + for _, d := range data { + // create the file + err = ioutil.WriteFile(file, []byte(d.contents), testFileMode) + if err != nil { + t.Fatal(err) + } + defer os.Remove(file) + + contents, err := GetFileContents(file) + assert.NoError(t, err) + assert.Equal(t, contents, d.contents) + } +}