mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-27 02:54:27 +01:00
Then we can remove the arbitrary sleep waiting for migration completion when creating a tempalte vm. Fixes: #728 Signed-off-by: Peng Tao <bergwolf@gmail.com>
171 lines
4.2 KiB
Go
171 lines
4.2 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/factory/direct"
|
|
)
|
|
|
|
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 := vc.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 {
|
|
statePath := vc.RunVMStoragePath + "/template"
|
|
t := &template{statePath, config}
|
|
|
|
err := t.prepareTemplateFiles()
|
|
if err != nil {
|
|
// fallback to direct factory if template is not supported.
|
|
return direct.New(ctx, config)
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
t.close()
|
|
}
|
|
}()
|
|
|
|
err = t.createTemplateVM(ctx)
|
|
if err != nil {
|
|
// fallback to direct factory if template is not supported.
|
|
return direct.New(ctx, config)
|
|
}
|
|
|
|
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(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
|
|
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
|
|
}
|