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  }