diff --git a/cli/config.go b/cli/config.go index 5c728aa763..a800f19f71 100644 --- a/cli/config.go +++ b/cli/config.go @@ -359,7 +359,7 @@ func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) { } func newFactoryConfig(f factory) (oci.FactoryConfig, error) { - return oci.FactoryConfig{f.Template}, nil + return oci.FactoryConfig{Template: f.Template}, nil } func newShimConfig(s shim) (vc.ShimConfig, error) { diff --git a/cli/create.go b/cli/create.go index 5fcb0e0518..2ce57aa91f 100644 --- a/cli/create.go +++ b/cli/create.go @@ -15,7 +15,7 @@ import ( "strings" vc "github.com/kata-containers/runtime/virtcontainers" - vm "github.com/kata-containers/runtime/virtcontainers/factory" + vf "github.com/kata-containers/runtime/virtcontainers/factory" "github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -102,7 +102,7 @@ func create(containerID, bundlePath, console, pidFilePath string, detach bool, } if runtimeConfig.FactoryConfig.Template { - factoryConfig := vm.FactoryConfig{ + factoryConfig := vf.Config{ Template: true, VMConfig: vc.VMConfig{ HypervisorType: runtimeConfig.HypervisorType, @@ -112,7 +112,7 @@ func create(containerID, bundlePath, console, pidFilePath string, detach bool, }, } kataLog.WithField("factory", factoryConfig).Info("load vm factory") - f, err := vm.NewFactory(factoryConfig, true) + f, err := vf.NewFactory(factoryConfig, true) if err != nil { kataLog.WithError(err).Info("load vm factory failed") } else { diff --git a/cli/factory.go b/cli/factory.go index afcbde035c..c2c1adcffc 100644 --- a/cli/factory.go +++ b/cli/factory.go @@ -7,9 +7,10 @@ package main import ( "errors" + "fmt" vc "github.com/kata-containers/runtime/virtcontainers" - vm "github.com/kata-containers/runtime/virtcontainers/factory" + vf "github.com/kata-containers/runtime/virtcontainers/factory" "github.com/kata-containers/runtime/virtcontainers/pkg/oci" "github.com/urfave/cli" ) @@ -38,7 +39,7 @@ var initFactoryCommand = cli.Command{ } if runtimeConfig.FactoryConfig.Template { - factoryConfig := vm.FactoryConfig{ + factoryConfig := vf.Config{ Template: true, VMConfig: vc.VMConfig{ HypervisorType: runtimeConfig.HypervisorType, @@ -48,13 +49,15 @@ var initFactoryCommand = cli.Command{ }, } kataLog.WithField("factory", factoryConfig).Info("create vm factory") - _, err := vm.NewFactory(factoryConfig, false) + _, err := vf.NewFactory(factoryConfig, false) if err != nil { kataLog.WithError(err).Error("create vm factory failed") return err } + fmt.Println("vm factory initialized") } else { kataLog.Error("vm factory is not enabled") + fmt.Println("vm factory is not enabled") } return nil @@ -71,7 +74,7 @@ var destroyFactoryCommand = cli.Command{ } if runtimeConfig.FactoryConfig.Template { - factoryConfig := vm.FactoryConfig{ + factoryConfig := vf.Config{ Template: true, VMConfig: vc.VMConfig{ HypervisorType: runtimeConfig.HypervisorType, @@ -81,7 +84,7 @@ var destroyFactoryCommand = cli.Command{ }, } kataLog.WithField("factory", factoryConfig).Info("load vm factory") - f, err := vm.NewFactory(factoryConfig, true) + f, err := vf.NewFactory(factoryConfig, true) if err != nil { kataLog.WithError(err).Error("load vm factory failed") // ignore error @@ -89,6 +92,7 @@ var destroyFactoryCommand = cli.Command{ f.CloseFactory() } } + fmt.Println("vm factory destroyed") return nil }, } diff --git a/virtcontainers/agent_test.go b/virtcontainers/agent_test.go index 108d69f938..a1fa120db3 100644 --- a/virtcontainers/agent_test.go +++ b/virtcontainers/agent_test.go @@ -99,7 +99,7 @@ func TestNewAgentFromUnknownAgentType(t *testing.T) { } func testNewAgentConfig(t *testing.T, config SandboxConfig, expected interface{}) { - agentConfig := newAgentConfig(config) + agentConfig := newAgentConfig(config.AgentType, config.AgentConfig) if reflect.DeepEqual(agentConfig, expected) == false { t.Fatal() } diff --git a/virtcontainers/api_test.go b/virtcontainers/api_test.go index a7dc007033..c7a5ebe8d3 100644 --- a/virtcontainers/api_test.go +++ b/virtcontainers/api_test.go @@ -244,7 +244,7 @@ func TestCreateSandboxNoopAgentSuccessful(t *testing.T) { config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -289,7 +289,7 @@ func TestCreateSandboxHyperstartAgentSuccessful(t *testing.T) { proxy.Start() defer proxy.Stop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -330,7 +330,7 @@ func TestCreateSandboxKataAgentSuccessful(t *testing.T) { } defer kataProxyMock.Stop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -347,7 +347,7 @@ func TestCreateSandboxFailing(t *testing.T) { config := SandboxConfig{} - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p.(*Sandbox) != nil || err == nil { t.Fatal() } @@ -358,7 +358,7 @@ func TestDeleteSandboxNoopAgentSuccessful(t *testing.T) { config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -401,7 +401,7 @@ func TestDeleteSandboxHyperstartAgentSuccessful(t *testing.T) { proxy.Start() defer proxy.Stop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -452,7 +452,7 @@ func TestDeleteSandboxKataAgentSuccessful(t *testing.T) { } defer kataProxyMock.Stop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -751,7 +751,7 @@ func TestRunSandboxNoopAgentSuccessful(t *testing.T) { config := newTestSandboxConfigNoop() - p, err := RunSandbox(config) + p, err := RunSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -787,7 +787,7 @@ func TestRunSandboxHyperstartAgentSuccessful(t *testing.T) { hyperConfig := config.AgentConfig.(HyperConfig) config.AgentConfig = hyperConfig - p, err := RunSandbox(config) + p, err := RunSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -833,7 +833,7 @@ func TestRunSandboxKataAgentSuccessful(t *testing.T) { } defer kataProxyMock.Stop() - p, err := RunSandbox(config) + p, err := RunSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -855,7 +855,7 @@ func TestRunSandboxFailing(t *testing.T) { config := SandboxConfig{} - p, err := RunSandbox(config) + p, err := RunSandbox(config, nil) if p != nil || err == nil { t.Fatal() } @@ -868,7 +868,7 @@ func TestListSandboxSuccessful(t *testing.T) { config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -928,7 +928,7 @@ func TestStatusSandboxSuccessfulStateReady(t *testing.T) { }, } - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -985,7 +985,7 @@ func TestStatusSandboxSuccessfulStateRunning(t *testing.T) { }, } - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1014,7 +1014,7 @@ func TestStatusSandboxFailingFetchSandboxConfig(t *testing.T) { config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1034,7 +1034,7 @@ func TestStatusPodSandboxFailingFetchSandboxState(t *testing.T) { config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1069,7 +1069,7 @@ func TestCreateContainerSuccessful(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1100,7 +1100,7 @@ func TestCreateContainerFailingNoSandbox(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1130,7 +1130,7 @@ func TestDeleteContainerSuccessful(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1184,7 +1184,7 @@ func TestDeleteContainerFailingNoContainer(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1249,7 +1249,7 @@ func TestStartContainerFailingNoContainer(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1272,7 +1272,7 @@ func TestStartContainerFailingSandboxNotStarted(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1493,7 +1493,7 @@ func TestStopContainerFailingNoContainer(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1674,7 +1674,7 @@ func TestEnterContainerFailingNoContainer(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1731,7 +1731,7 @@ func TestStatusContainerSuccessful(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1782,7 +1782,7 @@ func TestStatusContainerStateReady(t *testing.T) { contID := "101" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1845,7 +1845,7 @@ func TestStatusContainerStateRunning(t *testing.T) { contID := "101" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1917,7 +1917,7 @@ func TestStatusContainerFailing(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1940,7 +1940,7 @@ func TestStatsContainerFailing(t *testing.T) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if p == nil || err != nil { t.Fatal(err) } @@ -1973,7 +1973,7 @@ func TestStatsContainer(t *testing.T) { assert.Error(err) config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) assert.NoError(err) assert.NotNil(p) @@ -2023,7 +2023,7 @@ func TestProcessListContainer(t *testing.T) { assert.Error(err) config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) assert.NoError(err) assert.NotNil(p) @@ -2117,7 +2117,7 @@ func createAndStartSandbox(config SandboxConfig) (sandbox VCSandbox, sandboxDir err error) { // Create sandbox - sandbox, err = CreateSandbox(config) + sandbox, err = CreateSandbox(config, nil) if sandbox == nil || err != nil { return nil, "", err } @@ -2158,7 +2158,7 @@ func createStartStopDeleteSandbox(b *testing.B, sandboxConfig SandboxConfig) { func createStartStopDeleteContainers(b *testing.B, sandboxConfig SandboxConfig, contConfigs []ContainerConfig) { // Create sandbox - p, err := CreateSandbox(sandboxConfig) + p, err := CreateSandbox(sandboxConfig, nil) if err != nil { b.Fatalf("Could not create sandbox: %s", err) } @@ -2326,7 +2326,7 @@ func TestFetchSandbox(t *testing.T) { config := newTestSandboxConfigNoop() - s, err := CreateSandbox(config) + s, err := CreateSandbox(config, nil) if s == nil || err != nil { t.Fatal(err) } @@ -2348,7 +2348,7 @@ func TestReleaseSandbox(t *testing.T) { config := newTestSandboxConfigNoop() - s, err := CreateSandbox(config) + s, err := CreateSandbox(config, nil) if s == nil || err != nil { t.Fatal(err) } diff --git a/virtcontainers/example_pod_run_test.go b/virtcontainers/example_pod_run_test.go index 7ac0658cff..55e32e03e8 100644 --- a/virtcontainers/example_pod_run_test.go +++ b/virtcontainers/example_pod_run_test.go @@ -68,7 +68,7 @@ func Example_createAndStartSandbox() { Containers: []vc.ContainerConfig{container}, } - _, err := vc.RunSandbox(sandboxConfig) + _, err := vc.RunSandbox(sandboxConfig, nil) if err != nil { fmt.Printf("Could not run sandbox: %s", err) } diff --git a/virtcontainers/factory.go b/virtcontainers/factory.go index 524bc9f269..ad4f223114 100644 --- a/virtcontainers/factory.go +++ b/virtcontainers/factory.go @@ -5,7 +5,11 @@ package virtcontainers +// Factory controls how a new VM is created. type Factory interface { + // GetVM gets a new VM from the factory. GetVM(config VMConfig) (*VM, error) + + // CloseFactory closes and cleans up the factory. CloseFactory() } diff --git a/virtcontainers/factory/base/base.go b/virtcontainers/factory/base/base.go index ba2221b95b..85c1b4f075 100644 --- a/virtcontainers/factory/base/base.go +++ b/virtcontainers/factory/base/base.go @@ -7,8 +7,14 @@ package base import vc "github.com/kata-containers/runtime/virtcontainers" +// FactoryBase is vm factory's internal base factory interfaces. type FactoryBase interface { + // Config returns base factory config. Config() vc.VMConfig + + // GetBaseVM returns a paused VM created by the base factory. GetBaseVM() (*vc.VM, error) + + // CloseFactory closes the base factory. CloseFactory() } diff --git a/virtcontainers/factory/cache/cache.go b/virtcontainers/factory/cache/cache.go index a0c927c574..9c15465f03 100644 --- a/virtcontainers/factory/cache/cache.go +++ b/virtcontainers/factory/cache/cache.go @@ -23,6 +23,7 @@ type cache struct { closeOnce sync.Once } +// New creates a new cached vm factory. func New(count uint, b base.FactoryBase) base.FactoryBase { if count < 1 { return b @@ -55,10 +56,12 @@ func New(count uint, b base.FactoryBase) base.FactoryBase { return &c } +// Config returns cache vm factory's base factory config. func (c *cache) Config() vc.VMConfig { return c.base.Config() } +// GetBaseVM returns a base VM from cache factory's base factory. func (c *cache) GetBaseVM() (*vc.VM, error) { vm, ok := <-c.cachech if ok { @@ -67,6 +70,7 @@ func (c *cache) GetBaseVM() (*vc.VM, error) { return nil, fmt.Errorf("cache factory is closed") } +// CloseFactory closes the cache factory. func (c *cache) CloseFactory() { c.closeOnce.Do(func() { for len(c.closed) < cap(c.closed) { // send sufficient closed signal diff --git a/virtcontainers/factory/direct/direct.go b/virtcontainers/factory/direct/direct.go index 2866e4f7e5..bc63e2f22c 100644 --- a/virtcontainers/factory/direct/direct.go +++ b/virtcontainers/factory/direct/direct.go @@ -15,14 +15,17 @@ type direct struct { config vc.VMConfig } +// New returns a new direct vm factory. func New(config vc.VMConfig) base.FactoryBase { return &direct{config} } +// Config returns the direct factory's configuration. func (d *direct) Config() vc.VMConfig { return d.config } +// GetBaseVM create a new VM directly. func (d *direct) GetBaseVM() (*vc.VM, error) { vm, err := vc.NewVM(d.config) if err != nil { @@ -38,5 +41,6 @@ func (d *direct) GetBaseVM() (*vc.VM, error) { return vm, nil } +// CloseFactory closes the direct vm factory. func (d *direct) CloseFactory() { } diff --git a/virtcontainers/factory/factory.go b/virtcontainers/factory/factory.go index 3f3e2ab2a8..adb4ee83da 100644 --- a/virtcontainers/factory/factory.go +++ b/virtcontainers/factory/factory.go @@ -18,14 +18,15 @@ import ( var factoryLogger = logrus.FieldLogger(logrus.New()) -type FactoryConfig struct { +// Config is a collection of VM factory configurations. +type Config struct { Template bool Cache uint VMConfig vc.VMConfig } -func (f *FactoryConfig) validate() error { +func (f *Config) validate() error { return f.VMConfig.HypervisorConfig.Valid() } @@ -33,7 +34,8 @@ type factory struct { base base.FactoryBase } -func NewFactory(config FactoryConfig, fetchOnly bool) (vc.Factory, error) { +// NewFactory returns a working factory. +func NewFactory(config Config, fetchOnly bool) (vc.Factory, error) { err := config.validate() if err != nil { return nil, err @@ -77,6 +79,7 @@ func (f *factory) checkConfig(config vc.VMConfig) error { return nil } +// GetVM returns a working blank VM created by the factory. func (f *factory) GetVM(config vc.VMConfig) (*vc.VM, error) { hypervisorConfig := config.HypervisorConfig err := hypervisorConfig.Valid() @@ -139,6 +142,7 @@ func (f *factory) GetVM(config vc.VMConfig) (*vc.VM, error) { return vm, nil } +// CloseFactory closes the factory. func (f *factory) CloseFactory() { f.base.CloseFactory() } diff --git a/virtcontainers/factory/template/template.go b/virtcontainers/factory/template/template.go index 7b3a753912..e0386179ab 100644 --- a/virtcontainers/factory/template/template.go +++ b/virtcontainers/factory/template/template.go @@ -22,7 +22,8 @@ type template struct { config vc.VMConfig } -// TODO: save template metadata and fetch from storage +// Fetch finds and returns a pre-built template factory. +// TODO: save template metadata and fetch from storage. func Fetch(config vc.VMConfig) (base.FactoryBase, error) { statePath := vc.RunVMStoragePath + "/template" t := &template{statePath, config} @@ -35,6 +36,7 @@ func Fetch(config vc.VMConfig) (base.FactoryBase, error) { return t, nil } +// New creates a new VM template factory. func New(config vc.VMConfig) base.FactoryBase { statePath := vc.RunVMStoragePath + "/template" t := &template{statePath, config} @@ -48,14 +50,17 @@ func New(config vc.VMConfig) base.FactoryBase { return t } +// Config returns template factory's configuration. func (t *template) Config() vc.VMConfig { return t.config } +// GetBaseVM creates a new paused VM from the template VM. func (t *template) GetBaseVM() (*vc.VM, error) { return t.createFromTemplateVM() } +// CloseFactory cleans up the template VM. func (t *template) CloseFactory() { syscall.Unmount(t.statePath, 0) os.RemoveAll(t.statePath) @@ -72,13 +77,13 @@ func (t *template) createTemplateVM() error { if err = syscall.Mount("tmpfs", t.statePath, "tmpfs", flags, opts); err != nil { return err } - if f, err := os.Create(t.statePath + "/memory"); err != nil { + f, err := os.Create(t.statePath + "/memory") + if err != nil { return err - } else { - f.Close() } + f.Close() - // create the tempalte vm + // create the template vm config := t.config config.HypervisorConfig.BootToBeTemplate = true config.HypervisorConfig.BootFromTemplate = false diff --git a/virtcontainers/hypervisor.go b/virtcontainers/hypervisor.go index e7c9a5645b..ad15b8642d 100644 --- a/virtcontainers/hypervisor.go +++ b/virtcontainers/hypervisor.go @@ -232,15 +232,7 @@ type HypervisorConfig struct { DevicesStatePath string } -func (conf *HypervisorConfig) Valid() error { - if conf.KernelPath == "" { - return fmt.Errorf("Missing kernel path") - } - - if conf.ImagePath == "" && conf.InitrdPath == "" { - return fmt.Errorf("Missing image and initrd path") - } - +func (conf *HypervisorConfig) checkTemplateConfig() error { if conf.BootToBeTemplate && conf.BootFromTemplate { return fmt.Errorf("Cannot set both tobe and from vm tempate") } @@ -255,6 +247,23 @@ func (conf *HypervisorConfig) Valid() error { } } + return nil +} + +// Valid checks hypervisor config validity and sets the default values. +func (conf *HypervisorConfig) Valid() error { + if conf.KernelPath == "" { + return fmt.Errorf("Missing kernel path") + } + + if conf.ImagePath == "" && conf.InitrdPath == "" { + return fmt.Errorf("Missing image and initrd path") + } + + if err := conf.checkTemplateConfig(); err != nil { + return err + } + if conf.DefaultVCPUs == 0 { conf.DefaultVCPUs = defaultVCPUs } @@ -527,7 +536,7 @@ func RunningOnVMM(cpuInfoPath string) (bool, error) { // hypervisor is the virtcontainers hypervisor interface. // The default hypervisor implementation is Qemu. type hypervisor interface { - init(id string, hypervisorConfig HypervisorConfig, vmConfig Resources, storage resourceStorage) error + init(id string, hypervisorConfig *HypervisorConfig, vmConfig Resources, storage resourceStorage) error createSandbox() error startSandbox() error waitSandbox(timeout int) error diff --git a/virtcontainers/kata_agent.go b/virtcontainers/kata_agent.go index 669c356067..62757b70fe 100644 --- a/virtcontainers/kata_agent.go +++ b/virtcontainers/kata_agent.go @@ -692,35 +692,15 @@ func (k *kataAgent) rollbackFailingContainerCreation(c *Container) { } } -func (k *kataAgent) createContainer(sandbox *Sandbox, c *Container) (p *Process, err error) { - ociSpecJSON, ok := c.config.Annotations[vcAnnotations.ConfigJSONKey] - if !ok { - return nil, errorMissingOCISpec - } - - var ctrStorages []*grpc.Storage - var ctrDevices []*grpc.Device - - // The rootfs storage volume represents the container rootfs - // mount point inside the guest. - // It can be a block based device (when using block based container - // overlay on the host) mount or a 9pfs one (for all other overlay - // implementations). - rootfs := &grpc.Storage{} - - // This is the guest absolute root path for that container. - rootPathParent := filepath.Join(kataGuestSharedDir, c.id) - rootPath := filepath.Join(rootPathParent, rootfsDir) - - // In case the container creation fails, the following defer statement - // takes care of rolling back actions previously performed. - defer func() { - if err != nil { - k.rollbackFailingContainerCreation(c) - } - }() - +func (k *kataAgent) buildContainerRootfs(sandbox *Sandbox, c *Container, rootPathParent string) (*grpc.Storage, error) { if c.state.Fstype != "" { + // The rootfs storage volume represents the container rootfs + // mount point inside the guest. + // It can be a block based device (when using block based container + // overlay on the host) mount or a 9pfs one (for all other overlay + // implementations). + rootfs := &grpc.Storage{} + // This is a block based device rootfs. // Pass a drive name only in case of virtio-blk driver. @@ -746,24 +726,53 @@ func (k *kataAgent) createContainer(sandbox *Sandbox, c *Container) (p *Process, rootfs.Options = []string{"nouuid"} } + return rootfs, nil + } + // This is not a block based device rootfs. + // We are going to bind mount it into the 9pfs + // shared drive between the host and the guest. + // With 9pfs we don't need to ask the agent to + // mount the rootfs as the shared directory + // (kataGuestSharedDir) is already mounted in the + // guest. We only need to mount the rootfs from + // the host and it will show up in the guest. + if err := bindMountContainerRootfs(kataHostSharedDir, sandbox.id, c.id, c.rootFs, false); err != nil { + return nil, err + } + + return nil, nil +} + +func (k *kataAgent) createContainer(sandbox *Sandbox, c *Container) (p *Process, err error) { + ociSpecJSON, ok := c.config.Annotations[vcAnnotations.ConfigJSONKey] + if !ok { + return nil, errorMissingOCISpec + } + + var ctrStorages []*grpc.Storage + var ctrDevices []*grpc.Device + var rootfs *grpc.Storage + + // This is the guest absolute root path for that container. + rootPathParent := filepath.Join(kataGuestSharedDir, c.id) + rootPath := filepath.Join(rootPathParent, rootfsDir) + + // In case the container creation fails, the following defer statement + // takes care of rolling back actions previously performed. + defer func() { + if err != nil { + k.rollbackFailingContainerCreation(c) + } + }() + + if rootfs, err = k.buildContainerRootfs(sandbox, c, rootPathParent); err != nil { + return nil, err + } else if rootfs != nil { // Add rootfs to the list of container storage. // We only need to do this for block based rootfs, as we // want the agent to mount it into the right location // (kataGuestSharedDir/ctrID/ ctrStorages = append(ctrStorages, rootfs) - - } else { - // This is not a block based device rootfs. - // We are going to bind mount it into the 9pfs - // shared drive between the host and the guest. - // With 9pfs we don't need to ask the agent to - // mount the rootfs as the shared directory - // (kataGuestSharedDir) is already mounted in the - // guest. We only need to mount the rootfs from - // the host and it will show up in the guest. - if err = bindMountContainerRootfs(kataHostSharedDir, sandbox.id, c.id, c.rootFs, false); err != nil { - return nil, err - } } ociSpec := &specs.Spec{} diff --git a/virtcontainers/mock_hypervisor.go b/virtcontainers/mock_hypervisor.go index 58d9c62c53..61fef75cc9 100644 --- a/virtcontainers/mock_hypervisor.go +++ b/virtcontainers/mock_hypervisor.go @@ -9,7 +9,7 @@ type mockHypervisor struct { vCPUs uint32 } -func (m *mockHypervisor) init(id string, hypervisorConfig HypervisorConfig, vmConfig Resources, storage resourceStorage) error { +func (m *mockHypervisor) init(id string, hypervisorConfig *HypervisorConfig, vmConfig Resources, storage resourceStorage) error { err := hypervisorConfig.Valid() if err != nil { return err diff --git a/virtcontainers/mock_hypervisor_test.go b/virtcontainers/mock_hypervisor_test.go index 8b19f6c774..fae4b73cab 100644 --- a/virtcontainers/mock_hypervisor_test.go +++ b/virtcontainers/mock_hypervisor_test.go @@ -26,7 +26,7 @@ func TestMockHypervisorInit(t *testing.T) { } // wrong config - if err := m.init(sandbox.config.ID, sandbox.config.HypervisorConfig, sandbox.config.VMConfig, sandbox.storage); err == nil { + if err := m.init(sandbox.config.ID, &sandbox.config.HypervisorConfig, sandbox.config.VMConfig, sandbox.storage); err == nil { t.Fatal() } @@ -37,7 +37,7 @@ func TestMockHypervisorInit(t *testing.T) { } // right config - if err := m.init(sandbox.config.ID, sandbox.config.HypervisorConfig, sandbox.config.VMConfig, sandbox.storage); err != nil { + if err := m.init(sandbox.config.ID, &sandbox.config.HypervisorConfig, sandbox.config.VMConfig, sandbox.storage); err != nil { t.Fatal(err) } } diff --git a/virtcontainers/noop_agent_test.go b/virtcontainers/noop_agent_test.go index f4f076071c..206b92811d 100644 --- a/virtcontainers/noop_agent_test.go +++ b/virtcontainers/noop_agent_test.go @@ -14,7 +14,7 @@ func testCreateNoopContainer() (*Sandbox, *Container, error) { contID := "100" config := newTestSandboxConfigNoop() - p, err := CreateSandbox(config) + p, err := CreateSandbox(config, nil) if err != nil { return nil, nil, err } diff --git a/virtcontainers/pkg/oci/utils.go b/virtcontainers/pkg/oci/utils.go index c8f82619f5..74edafc6e5 100644 --- a/virtcontainers/pkg/oci/utils.go +++ b/virtcontainers/pkg/oci/utils.go @@ -90,7 +90,9 @@ type CompatOCISpec struct { Process *CompatOCIProcess `json:"process,omitempty"` } +// FactoryConfig is a structure to set the VM factory configuration. type FactoryConfig struct { + // Template enables VM templating support in VM factory. Template bool } diff --git a/virtcontainers/pkg/vcmock/mock.go b/virtcontainers/pkg/vcmock/mock.go index a4e0bcab3b..c0413591b8 100644 --- a/virtcontainers/pkg/vcmock/mock.go +++ b/virtcontainers/pkg/vcmock/mock.go @@ -35,6 +35,13 @@ func (m *VCMock) SetLogger(logger logrus.FieldLogger) { } } +// SetFactory implements the VC function of the same name. +func (m *VCMock) SetFactory(factory vc.Factory) { + if m.SetFactoryFunc != nil { + m.SetFactoryFunc(factory) + } +} + // CreateSandbox implements the VC function of the same name. func (m *VCMock) CreateSandbox(sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { if m.CreateSandboxFunc != nil { diff --git a/virtcontainers/pkg/vcmock/types.go b/virtcontainers/pkg/vcmock/types.go index c223526034..83b6546ccf 100644 --- a/virtcontainers/pkg/vcmock/types.go +++ b/virtcontainers/pkg/vcmock/types.go @@ -35,7 +35,8 @@ type Container struct { // VCMock is a type that provides an implementation of the VC interface. // It is used for testing. type VCMock struct { - SetLoggerFunc func(logger logrus.FieldLogger) + SetLoggerFunc func(logger logrus.FieldLogger) + SetFactoryFunc func(factory vc.Factory) CreateSandboxFunc func(sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) DeleteSandboxFunc func(sandboxID string) (vc.VCSandbox, error) diff --git a/virtcontainers/qemu.go b/virtcontainers/qemu.go index aea7efbc49..856c0e13e3 100644 --- a/virtcontainers/qemu.go +++ b/virtcontainers/qemu.go @@ -169,7 +169,7 @@ func (q *qemu) qemuPath() (string, error) { } // init intializes the Qemu structure. -func (q *qemu) init(id string, hypervisorConfig HypervisorConfig, vmConfig Resources, storage resourceStorage) error { +func (q *qemu) init(id string, hypervisorConfig *HypervisorConfig, vmConfig Resources, storage resourceStorage) error { err := hypervisorConfig.Valid() if err != nil { return err @@ -178,7 +178,7 @@ func (q *qemu) init(id string, hypervisorConfig HypervisorConfig, vmConfig Resou q.id = id q.storage = storage q.vmConfig = vmConfig - q.config = hypervisorConfig + q.config = *hypervisorConfig q.arch = newQemuArch(q.config) if err = q.storage.fetchHypervisorState(q.id, &q.state); err != nil { @@ -279,10 +279,83 @@ func (q *qemu) appendImage(devices []govmmQemu.Device) ([]govmmQemu.Device, erro return devices, nil } -// createSandbox is the Hypervisor sandbox creation implementation for govmmQemu. -func (q *qemu) createSandbox() error { +func (q *qemu) createQmpSocket() ([]govmmQemu.QMPSocket, error) { + monitorSockPath, err := q.qmpSocketPath(q.id) + if err != nil { + return nil, err + } + + q.qmpMonitorCh = qmpChannel{ + ctx: context.Background(), + path: monitorSockPath, + } + + err = os.MkdirAll(filepath.Dir(monitorSockPath), dirMode) + if err != nil { + return nil, err + } + + return []govmmQemu.QMPSocket{ + { + Type: "unix", + Name: q.qmpMonitorCh.path, + Server: true, + NoWait: true, + }, + }, nil +} + +func (q *qemu) buildDevices(initrdPath string) ([]govmmQemu.Device, *govmmQemu.IOThread, error) { var devices []govmmQemu.Device + console, err := q.getSandboxConsole(q.id) + if err != nil { + return nil, nil, err + } + + // Add bridges before any other devices. This way we make sure that + // bridge gets the first available PCI address i.e bridgePCIStartAddr + devices = q.arch.appendBridges(devices, q.state.Bridges) + + devices = q.arch.appendConsole(devices, console) + + if initrdPath == "" { + devices, err = q.appendImage(devices) + if err != nil { + return nil, nil, err + } + } + + var ioThread *govmmQemu.IOThread + if q.config.BlockDeviceDriver == VirtioSCSI { + devices, ioThread = q.arch.appendSCSIController(devices, q.config.EnableIOThreads) + } + + return devices, ioThread, nil + +} + +func (q *qemu) setupTemplate(knobs *govmmQemu.Knobs, memory *govmmQemu.Memory) govmmQemu.Incoming { + incoming := govmmQemu.Incoming{} + + if q.config.BootToBeTemplate || q.config.BootFromTemplate { + knobs.FileBackedMem = true + memory.Path = q.config.MemoryPath + + if q.config.BootToBeTemplate { + knobs.FileBackedMemShared = true + } + + if q.config.BootFromTemplate { + incoming.Exec = "cat " + q.config.DevicesStatePath + } + } + + return incoming +} + +// createSandbox is the Hypervisor sandbox creation implementation for govmmQemu. +func (q *qemu) createSandbox() error { machine, err := q.getQemuMachine() if err != nil { return err @@ -322,19 +395,7 @@ func (q *qemu) createSandbox() error { Params: q.kernelParameters(), } - incoming := govmmQemu.Incoming{} - if q.config.BootToBeTemplate || q.config.BootFromTemplate { - knobs.FileBackedMem = true - memory.Path = q.config.MemoryPath - - if q.config.BootToBeTemplate { - knobs.FileBackedMemShared = true - } - - if q.config.BootFromTemplate { - incoming.Exec = "cat " + q.config.DevicesStatePath - } - } + incoming := q.setupTemplate(&knobs, &memory) rtc := govmmQemu.RTC{ Base: "utc", @@ -360,36 +421,14 @@ func (q *qemu) createSandbox() error { return err } - qmpSockets := []govmmQemu.QMPSocket{ - { - Type: "unix", - Name: q.qmpMonitorCh.path, - Server: true, - NoWait: true, - }, - } - - // Add bridges before any other devices. This way we make sure that - // bridge gets the first available PCI address i.e bridgePCIStartAddr - devices = q.arch.appendBridges(devices, q.state.Bridges) - - console, err := q.getSandboxConsole(q.id) + qmpSockets, err := q.createQmpSocket() if err != nil { return err } - devices = q.arch.appendConsole(devices, console) - - if initrdPath == "" { - devices, err = q.appendImage(devices) - if err != nil { - return err - } - } - - var ioThread *govmmQemu.IOThread - if q.config.BlockDeviceDriver == VirtioSCSI { - devices, ioThread = q.arch.appendSCSIController(devices, q.config.EnableIOThreads) + devices, ioThread, err := q.buildDevices(initrdPath) + if err != nil { + return err } cpuModel := q.arch.cpuModel() diff --git a/virtcontainers/qemu_test.go b/virtcontainers/qemu_test.go index d56c22a368..b21a268e5d 100644 --- a/virtcontainers/qemu_test.go +++ b/virtcontainers/qemu_test.go @@ -86,7 +86,7 @@ func TestQemuInit(t *testing.T) { t.Fatalf("Could not create parent directory %s: %v", parentDir, err) } - if err := q.init(sandbox.id, sandbox.config.HypervisorConfig, sandbox.config.VMConfig, sandbox.storage); err != nil { + if err := q.init(sandbox.id, &sandbox.config.HypervisorConfig, sandbox.config.VMConfig, sandbox.storage); err != nil { t.Fatal(err) } @@ -117,8 +117,8 @@ func TestQemuInitMissingParentDirFail(t *testing.T) { t.Fatal(err) } - if err := q.init(sandbox.id, sandbox.config.HypervisorConfig, sandbox.config.VMConfig, sandbox.storage); err == nil { - t.Fatal("Qemu init() expected to fail because of missing parent directory for storage") + if err := q.init(sandbox.id, &sandbox.config.HypervisorConfig, sandbox.config.VMConfig, sandbox.storage); err != nil { + t.Fatalf("Qemu init() is not expected to fail because of missing parent directory for storage: %v", err) } } @@ -249,7 +249,7 @@ func TestQemuAddDeviceSerialPortDev(t *testing.T) { func TestQemuGetSandboxConsole(t *testing.T) { q := &qemu{} sandboxID := "testSandboxID" - expected := filepath.Join(runStoragePath, sandboxID, consoleSocket) + expected := filepath.Join(RunVMStoragePath, sandboxID, consoleSocket) result, err := q.getSandboxConsole(sandboxID) if err != nil { diff --git a/virtcontainers/sandbox.go b/virtcontainers/sandbox.go index bd052e3e3e..bff5c57ad8 100644 --- a/virtcontainers/sandbox.go +++ b/virtcontainers/sandbox.go @@ -752,7 +752,7 @@ func newSandbox(sandboxConfig SandboxConfig, factory Factory) (*Sandbox, error) } }() - if err = s.hypervisor.init(s.id, sandboxConfig.HypervisorConfig, sandboxConfig.VMConfig, s.storage); err != nil { + if err = s.hypervisor.init(s.id, &sandboxConfig.HypervisorConfig, sandboxConfig.VMConfig, s.storage); err != nil { return nil, err } @@ -941,9 +941,9 @@ func (s *Sandbox) startVM() error { // FIXME: factory vm needs network hotplug to add Nics. s.networkNS.NetNsPath = "" return nil - } else { - return s.hypervisor.startSandbox() } + + return s.hypervisor.startSandbox() }); err != nil { return err } diff --git a/virtcontainers/sandbox_test.go b/virtcontainers/sandbox_test.go index eec5a0d2aa..d2f3517428 100644 --- a/virtcontainers/sandbox_test.go +++ b/virtcontainers/sandbox_test.go @@ -52,7 +52,7 @@ func testCreateSandbox(t *testing.T, id string, Containers: containers, } - sandbox, err := createSandbox(sconfig) + sandbox, err := createSandbox(sconfig, nil) if err != nil { return nil, fmt.Errorf("Could not create sandbox: %s", err) } diff --git a/virtcontainers/vm.go b/virtcontainers/vm.go index ec2e495ea5..5af5481f74 100644 --- a/virtcontainers/vm.go +++ b/virtcontainers/vm.go @@ -13,6 +13,7 @@ import ( "github.com/sirupsen/logrus" ) +// VM is abstraction of a virtual machine. type VM struct { id string @@ -25,6 +26,7 @@ type VM struct { cpuDelta uint32 } +// VMConfig is a collection of all info that a new blackbox VM needs. type VMConfig struct { HypervisorType HypervisorType HypervisorConfig HypervisorConfig @@ -33,10 +35,12 @@ type VMConfig struct { AgentConfig interface{} } +// Valid check VMConfig validity. func (c *VMConfig) Valid() error { return c.HypervisorConfig.Valid() } +// NewVM creates a new VM based on provided VMConfig. func NewVM(config VMConfig) (*VM, error) { hypervisor, err := newHypervisor(config.HypervisorType) if err != nil { @@ -58,7 +62,7 @@ func NewVM(config VMConfig) (*VM, error) { } }() - err = hypervisor.init(id, config.HypervisorConfig, Resources{}, &filesystem{}) + err = hypervisor.init(id, &config.HypervisorConfig, Resources{}, &filesystem{}) if err != nil { return nil, err } @@ -92,13 +96,13 @@ func NewVM(config VMConfig) (*VM, error) { } }() - err = hypervisor.waitSandbox(vmStartTimeout) - if err != nil { - return nil, err - } - // VMs booted from template are paused, do not check if !config.HypervisorConfig.BootFromTemplate { + err = hypervisor.waitSandbox(vmStartTimeout) + if err != nil { + return nil, err + } + virtLog.WithField("vm id", id).Info("check agent status") err = agent.check() if err != nil { @@ -123,31 +127,37 @@ func (v *VM) logger() logrus.FieldLogger { return virtLog.WithField("vm id", v.id) } +// Pause pauses a VM. func (v *VM) Pause() error { v.logger().Info("pause vm") return v.hypervisor.pauseSandbox() } +// Save saves a VM to persistent disk. func (v *VM) Save() error { v.logger().Info("save vm") return v.hypervisor.saveSandbox() } +// Resume resumes a paused VM. func (v *VM) Resume() error { v.logger().Info("resume vm") return v.hypervisor.resumeSandbox() } +// Start kicks off a configured VM. func (v *VM) Start() error { v.logger().Info("start vm") return v.hypervisor.startSandbox() } +// Kill stops a VM process. func (v *VM) Kill() error { v.logger().Info("kill vm") return v.hypervisor.stopSandbox() } +// AddCPUs adds num of CPUs to the VM. func (v *VM) AddCPUs(num uint32) error { if num > 0 { v.logger().Info("hot adding %d vCPUs", num) @@ -161,6 +171,7 @@ func (v *VM) AddCPUs(num uint32) error { return nil } +// AddMemory adds numMB of memory to the VM. func (v *VM) AddMemory(numMB uint32) error { if numMB > 0 { v.logger().Info("hot adding %d MB memory", numMB) @@ -173,6 +184,7 @@ func (v *VM) AddMemory(numMB uint32) error { return nil } +// OnlineCPUMemory puts the hotplugged CPU and memory online. func (v *VM) OnlineCPUMemory() error { v.logger().Info("online CPU %d and memory", v.cpuDelta) err := v.agent.onlineCPUMem(v.cpuDelta)