mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-05 07:24:20 +01:00
Now, function NewFactory will return nil even create template does't complete. As for this, it will tell user that factory has been initialized no matter whether the template is created or not. This patch correct it by adding another return value of error in NewFactory. Testing initFactoryCommand when enable template will need root privilege to mount tmpfs. So skip it for no-root user. Testing initFactoryCommand func will create template, but no proxy type assigned to VMconfig which will using katabuiltinProxy instead. this will lead to failure for this type of proxy will check proxyparams which contains many null value. This commit fix it by substitute katabuiltinProxy as noopProxy when for test purpose. Fixes: #1333 Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
171 lines
4.1 KiB
Go
171 lines
4.1 KiB
Go
// Copyright (c) 2018 HyperHQ Inc.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// template implements base vm factory with vm templating.
|
|
|
|
package template
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"syscall"
|
|
"time"
|
|
|
|
vc "github.com/kata-containers/runtime/virtcontainers"
|
|
"github.com/kata-containers/runtime/virtcontainers/factory/base"
|
|
"github.com/kata-containers/runtime/virtcontainers/store"
|
|
)
|
|
|
|
type template struct {
|
|
statePath string
|
|
config vc.VMConfig
|
|
}
|
|
|
|
var templateProxyType = vc.KataBuiltInProxyType
|
|
var templateWaitForAgent = 2 * time.Second
|
|
|
|
// 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 := store.RunVMStoragePath + "/template"
|
|
t := &template{statePath, config}
|
|
|
|
err := t.checkTemplateVM()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return t, nil
|
|
}
|
|
|
|
// New creates a new VM template factory.
|
|
func New(ctx context.Context, config vc.VMConfig) (base.FactoryBase, error) {
|
|
statePath := store.RunVMStoragePath + "/template"
|
|
t := &template{statePath, config}
|
|
|
|
err := t.prepareTemplateFiles()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
t.close()
|
|
}
|
|
}()
|
|
|
|
err = t.createTemplateVM(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return t, nil
|
|
}
|
|
|
|
// 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(ctx context.Context, config vc.VMConfig) (*vc.VM, error) {
|
|
return t.createFromTemplateVM(ctx, config)
|
|
}
|
|
|
|
// CloseFactory cleans up the template VM.
|
|
func (t *template) CloseFactory(ctx context.Context) {
|
|
t.close()
|
|
}
|
|
|
|
func (t *template) close() {
|
|
syscall.Unmount(t.statePath, 0)
|
|
os.RemoveAll(t.statePath)
|
|
}
|
|
|
|
func (t *template) prepareTemplateFiles() error {
|
|
// create and mount tmpfs for the shared memory file
|
|
err := os.MkdirAll(t.statePath, 0700)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
flags := uintptr(syscall.MS_NOSUID | syscall.MS_NODEV)
|
|
opts := fmt.Sprintf("size=%dM", t.config.HypervisorConfig.MemorySize+8)
|
|
if err = syscall.Mount("tmpfs", t.statePath, "tmpfs", flags, opts); err != nil {
|
|
t.close()
|
|
return err
|
|
}
|
|
f, err := os.Create(t.statePath + "/memory")
|
|
if err != nil {
|
|
t.close()
|
|
return err
|
|
}
|
|
f.Close()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *template) createTemplateVM(ctx context.Context) error {
|
|
// create the template vm
|
|
config := t.config
|
|
config.HypervisorConfig.BootToBeTemplate = true
|
|
config.HypervisorConfig.BootFromTemplate = false
|
|
config.HypervisorConfig.MemoryPath = t.statePath + "/memory"
|
|
config.HypervisorConfig.DevicesStatePath = t.statePath + "/state"
|
|
// template vm uses builtin proxy
|
|
if config.ProxyType != "noopProxy" {
|
|
config.ProxyType = templateProxyType
|
|
}
|
|
|
|
vm, err := vc.NewVM(ctx, config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer vm.Stop()
|
|
|
|
if err = vm.Disconnect(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Sleep a bit to let the agent grpc server clean up
|
|
// When we close connection to the agent, it needs sometime to cleanup
|
|
// and restart listening on the communication( serial or vsock) port.
|
|
// That time can be saved if we sleep a bit to wait for the agent to
|
|
// come around and start listening again. The sleep is only done when
|
|
// creating new vm templates and saves time for every new vm that are
|
|
// created from template, so it worth the invest.
|
|
time.Sleep(templateWaitForAgent)
|
|
|
|
if err = vm.Pause(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = vm.Save(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *template) createFromTemplateVM(ctx context.Context, c vc.VMConfig) (*vc.VM, error) {
|
|
config := t.config
|
|
config.HypervisorConfig.BootToBeTemplate = false
|
|
config.HypervisorConfig.BootFromTemplate = true
|
|
config.HypervisorConfig.MemoryPath = t.statePath + "/memory"
|
|
config.HypervisorConfig.DevicesStatePath = t.statePath + "/state"
|
|
config.ProxyType = c.ProxyType
|
|
config.ProxyConfig = c.ProxyConfig
|
|
|
|
return vc.NewVM(ctx, config)
|
|
}
|
|
|
|
func (t *template) checkTemplateVM() error {
|
|
_, err := os.Stat(t.statePath + "/memory")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = os.Stat(t.statePath + "/state")
|
|
return err
|
|
}
|