gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/factory/template/template.go (about)

     1  // Copyright (c) 2018 HyperHQ Inc.
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  // template implements base vm factory with vm templating.
     6  
     7  package template
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"os"
    13  	"syscall"
    14  	"time"
    15  
    16  	pb "github.com/kata-containers/runtime/protocols/cache"
    17  	vc "github.com/kata-containers/runtime/virtcontainers"
    18  	"github.com/kata-containers/runtime/virtcontainers/factory/base"
    19  )
    20  
    21  type template struct {
    22  	statePath string
    23  	config    vc.VMConfig
    24  }
    25  
    26  var templateWaitForAgent = 2 * time.Second
    27  
    28  // Fetch finds and returns a pre-built template factory.
    29  // TODO: save template metadata and fetch from storage.
    30  func Fetch(config vc.VMConfig, templatePath string) (base.FactoryBase, error) {
    31  	t := &template{templatePath, config}
    32  
    33  	err := t.checkTemplateVM()
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	return t, nil
    39  }
    40  
    41  // New creates a new VM template factory.
    42  func New(ctx context.Context, config vc.VMConfig, templatePath string) (base.FactoryBase, error) {
    43  	t := &template{templatePath, config}
    44  
    45  	err := t.checkTemplateVM()
    46  	if err == nil {
    47  		return nil, fmt.Errorf("There is already a VM template in %s", templatePath)
    48  	}
    49  
    50  	err = t.prepareTemplateFiles()
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	defer func() {
    55  		if err != nil {
    56  			t.close()
    57  		}
    58  	}()
    59  
    60  	err = t.createTemplateVM(ctx)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	return t, nil
    66  }
    67  
    68  // Config returns template factory's configuration.
    69  func (t *template) Config() vc.VMConfig {
    70  	return t.config
    71  }
    72  
    73  // GetBaseVM creates a new paused VM from the template VM.
    74  func (t *template) GetBaseVM(ctx context.Context, config vc.VMConfig) (*vc.VM, error) {
    75  	return t.createFromTemplateVM(ctx, config)
    76  }
    77  
    78  // CloseFactory cleans up the template VM.
    79  func (t *template) CloseFactory(ctx context.Context) {
    80  	t.close()
    81  }
    82  
    83  // GetVMStatus is not supported
    84  func (t *template) GetVMStatus() []*pb.GrpcVMStatus {
    85  	panic("ERROR: package template does not support GetVMStatus")
    86  }
    87  
    88  func (t *template) close() {
    89  	syscall.Unmount(t.statePath, 0)
    90  	os.RemoveAll(t.statePath)
    91  }
    92  
    93  func (t *template) prepareTemplateFiles() error {
    94  	// create and mount tmpfs for the shared memory file
    95  	err := os.MkdirAll(t.statePath, 0700)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	flags := uintptr(syscall.MS_NOSUID | syscall.MS_NODEV)
   100  	opts := fmt.Sprintf("size=%dM", t.config.HypervisorConfig.MemorySize+templateDeviceStateSize)
   101  	if err = syscall.Mount("tmpfs", t.statePath, "tmpfs", flags, opts); err != nil {
   102  		t.close()
   103  		return err
   104  	}
   105  	f, err := os.Create(t.statePath + "/memory")
   106  	if err != nil {
   107  		t.close()
   108  		return err
   109  	}
   110  	f.Close()
   111  
   112  	return nil
   113  }
   114  
   115  func (t *template) createTemplateVM(ctx context.Context) error {
   116  	// create the template vm
   117  	config := t.config
   118  	config.HypervisorConfig.BootToBeTemplate = true
   119  	config.HypervisorConfig.BootFromTemplate = false
   120  	config.HypervisorConfig.MemoryPath = t.statePath + "/memory"
   121  	config.HypervisorConfig.DevicesStatePath = t.statePath + "/state"
   122  
   123  	vm, err := vc.NewVM(ctx, config)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	defer vm.Stop()
   128  
   129  	if err = vm.Disconnect(); err != nil {
   130  		return err
   131  	}
   132  
   133  	// Sleep a bit to let the agent grpc server clean up
   134  	// When we close connection to the agent, it needs sometime to cleanup
   135  	// and restart listening on the communication( serial or vsock) port.
   136  	// That time can be saved if we sleep a bit to wait for the agent to
   137  	// come around and start listening again. The sleep is only done when
   138  	// creating new vm templates and saves time for every new vm that are
   139  	// created from template, so it worth the invest.
   140  	time.Sleep(templateWaitForAgent)
   141  
   142  	if err = vm.Pause(); err != nil {
   143  		return err
   144  	}
   145  
   146  	if err = vm.Save(); err != nil {
   147  		return err
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  func (t *template) createFromTemplateVM(ctx context.Context, c vc.VMConfig) (*vc.VM, error) {
   154  	config := t.config
   155  	config.HypervisorConfig.BootToBeTemplate = false
   156  	config.HypervisorConfig.BootFromTemplate = true
   157  	config.HypervisorConfig.MemoryPath = t.statePath + "/memory"
   158  	config.HypervisorConfig.DevicesStatePath = t.statePath + "/state"
   159  	config.ProxyType = c.ProxyType
   160  	config.ProxyConfig = c.ProxyConfig
   161  
   162  	return vc.NewVM(ctx, config)
   163  }
   164  
   165  func (t *template) checkTemplateVM() error {
   166  	_, err := os.Stat(t.statePath + "/memory")
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	_, err = os.Stat(t.statePath + "/state")
   172  	return err
   173  }