gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/factory/factory.go (about) 1 // Copyright (c) 2018 HyperHQ Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package factory 7 8 import ( 9 "context" 10 "fmt" 11 12 pb "github.com/kata-containers/runtime/protocols/cache" 13 vc "github.com/kata-containers/runtime/virtcontainers" 14 "github.com/kata-containers/runtime/virtcontainers/factory/base" 15 "github.com/kata-containers/runtime/virtcontainers/factory/cache" 16 "github.com/kata-containers/runtime/virtcontainers/factory/direct" 17 "github.com/kata-containers/runtime/virtcontainers/factory/grpccache" 18 "github.com/kata-containers/runtime/virtcontainers/factory/template" 19 "github.com/kata-containers/runtime/virtcontainers/utils" 20 opentracing "github.com/opentracing/opentracing-go" 21 "github.com/sirupsen/logrus" 22 ) 23 24 var factoryLogger = logrus.FieldLogger(logrus.New()) 25 26 // Config is a collection of VM factory configurations. 27 type Config struct { 28 Template bool 29 VMCache bool 30 Cache uint 31 TemplatePath string 32 VMCacheEndpoint string 33 34 VMConfig vc.VMConfig 35 } 36 37 type factory struct { 38 base base.FactoryBase 39 } 40 41 func trace(parent context.Context, name string) (opentracing.Span, context.Context) { 42 span, ctx := opentracing.StartSpanFromContext(parent, name) 43 44 span.SetTag("subsystem", "factory") 45 46 return span, ctx 47 } 48 49 // NewFactory returns a working factory. 50 func NewFactory(ctx context.Context, config Config, fetchOnly bool) (vc.Factory, error) { 51 span, _ := trace(ctx, "NewFactory") 52 defer span.Finish() 53 54 err := config.VMConfig.Valid() 55 if err != nil { 56 return nil, err 57 } 58 59 if fetchOnly && config.Cache > 0 { 60 return nil, fmt.Errorf("cache factory does not support fetch") 61 } 62 63 var b base.FactoryBase 64 if config.VMCache && config.Cache == 0 { 65 // For VMCache client 66 b, err = grpccache.New(ctx, config.VMCacheEndpoint) 67 if err != nil { 68 return nil, err 69 } 70 } else { 71 if config.Template { 72 if fetchOnly { 73 b, err = template.Fetch(config.VMConfig, config.TemplatePath) 74 if err != nil { 75 return nil, err 76 } 77 } else { 78 b, err = template.New(ctx, config.VMConfig, config.TemplatePath) 79 if err != nil { 80 return nil, err 81 } 82 } 83 } else { 84 b = direct.New(ctx, config.VMConfig) 85 } 86 87 if config.Cache > 0 { 88 b = cache.New(ctx, config.Cache, b) 89 } 90 } 91 92 return &factory{b}, nil 93 } 94 95 // SetLogger sets the logger for the factory. 96 func SetLogger(ctx context.Context, logger logrus.FieldLogger) { 97 fields := logrus.Fields{ 98 "source": "virtcontainers", 99 } 100 101 factoryLogger = logger.WithFields(fields) 102 } 103 104 func (f *factory) log() *logrus.Entry { 105 return factoryLogger.WithField("subsystem", "factory") 106 } 107 108 func resetHypervisorConfig(config *vc.VMConfig) { 109 config.HypervisorConfig.NumVCPUs = 0 110 config.HypervisorConfig.MemorySize = 0 111 config.HypervisorConfig.BootToBeTemplate = false 112 config.HypervisorConfig.BootFromTemplate = false 113 config.HypervisorConfig.MemoryPath = "" 114 config.HypervisorConfig.DevicesStatePath = "" 115 config.ProxyConfig = vc.ProxyConfig{} 116 } 117 118 // It's important that baseConfig and newConfig are passed by value! 119 func checkVMConfig(config1, config2 vc.VMConfig) error { 120 if config1.HypervisorType != config2.HypervisorType { 121 return fmt.Errorf("hypervisor type does not match: %s vs. %s", config1.HypervisorType, config2.HypervisorType) 122 } 123 124 if config1.AgentType != config2.AgentType { 125 return fmt.Errorf("agent type does not match: %s vs. %s", config1.AgentType, config2.AgentType) 126 } 127 128 // check hypervisor config details 129 resetHypervisorConfig(&config1) 130 resetHypervisorConfig(&config2) 131 132 if !utils.DeepCompare(config1, config2) { 133 return fmt.Errorf("hypervisor config does not match, base: %+v. new: %+v", config1, config2) 134 } 135 136 return nil 137 } 138 139 func (f *factory) checkConfig(config vc.VMConfig) error { 140 baseConfig := f.base.Config() 141 142 return checkVMConfig(baseConfig, config) 143 } 144 145 func (f *factory) validateNewVMConfig(config vc.VMConfig) error { 146 if len(config.AgentType.String()) == 0 { 147 return fmt.Errorf("Missing agent type") 148 } 149 150 if len(config.ProxyType.String()) == 0 { 151 return fmt.Errorf("Missing proxy type") 152 } 153 154 return config.Valid() 155 } 156 157 // GetVM returns a working blank VM created by the factory. 158 func (f *factory) GetVM(ctx context.Context, config vc.VMConfig) (*vc.VM, error) { 159 span, _ := trace(ctx, "GetVM") 160 defer span.Finish() 161 162 hypervisorConfig := config.HypervisorConfig 163 err := f.validateNewVMConfig(config) 164 if err != nil { 165 f.log().WithError(err).Error("invalid hypervisor config") 166 return nil, err 167 } 168 169 err = f.checkConfig(config) 170 if err != nil { 171 f.log().WithError(err).Info("fallback to direct factory vm") 172 return direct.New(ctx, config).GetBaseVM(ctx, config) 173 } 174 175 f.log().Info("get base VM") 176 vm, err := f.base.GetBaseVM(ctx, config) 177 if err != nil { 178 f.log().WithError(err).Error("failed to get base VM") 179 return nil, err 180 } 181 182 // cleanup upon error 183 defer func() { 184 if err != nil { 185 f.log().WithError(err).Error("clean up vm") 186 vm.Stop() 187 } 188 }() 189 190 err = vm.Resume() 191 if err != nil { 192 return nil, err 193 } 194 195 // reseed RNG so that shared memory VMs do not generate same random numbers. 196 err = vm.ReseedRNG() 197 if err != nil { 198 return nil, err 199 } 200 201 // sync guest time since we might have paused it for a long time. 202 err = vm.SyncTime() 203 if err != nil { 204 return nil, err 205 } 206 207 online := false 208 baseConfig := f.base.Config().HypervisorConfig 209 if baseConfig.NumVCPUs < hypervisorConfig.NumVCPUs { 210 err = vm.AddCPUs(hypervisorConfig.NumVCPUs - baseConfig.NumVCPUs) 211 if err != nil { 212 return nil, err 213 } 214 online = true 215 } 216 217 if baseConfig.MemorySize < hypervisorConfig.MemorySize { 218 err = vm.AddMemory(hypervisorConfig.MemorySize - baseConfig.MemorySize) 219 if err != nil { 220 return nil, err 221 } 222 online = true 223 } 224 225 if online { 226 err = vm.OnlineCPUMemory() 227 if err != nil { 228 return nil, err 229 } 230 } 231 232 return vm, nil 233 } 234 235 // Config returns base factory config. 236 func (f *factory) Config() vc.VMConfig { 237 return f.base.Config() 238 } 239 240 // GetVMStatus returns the status of the paused VM created by the base factory. 241 func (f *factory) GetVMStatus() []*pb.GrpcVMStatus { 242 return f.base.GetVMStatus() 243 } 244 245 // GetBaseVM returns a paused VM created by the base factory. 246 func (f *factory) GetBaseVM(ctx context.Context, config vc.VMConfig) (*vc.VM, error) { 247 return f.base.GetBaseVM(ctx, config) 248 } 249 250 // CloseFactory closes the factory. 251 func (f *factory) CloseFactory(ctx context.Context) { 252 f.base.CloseFactory(ctx) 253 }