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 }