Merge pull request #870 from lifupan/cli_refactor

cli: refactor the config into a separated package
This commit is contained in:
Eric Ernst 2018-11-08 14:23:16 -08:00 committed by GitHub
commit ebd86d604e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 560 additions and 346 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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"

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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) {

77
pkg/katautils/utils.go Normal file
View File

@ -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
}

213
pkg/katautils/utils_test.go Normal file
View File

@ -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)
}
}