github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/provisioner/container_initialisation.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names/v5"
    11  
    12  	"github.com/juju/juju/agent"
    13  	apiprovisioner "github.com/juju/juju/api/agent/provisioner"
    14  	"github.com/juju/juju/container"
    15  	"github.com/juju/juju/container/broker"
    16  	"github.com/juju/juju/container/kvm"
    17  	"github.com/juju/juju/container/lxd"
    18  	"github.com/juju/juju/core/instance"
    19  	"github.com/juju/juju/core/machinelock"
    20  	"github.com/juju/juju/core/network"
    21  	"github.com/juju/juju/environs/config"
    22  	"github.com/juju/juju/rpc/params"
    23  	workercommon "github.com/juju/juju/worker/common"
    24  )
    25  
    26  // ContainerSetup sets up the machine to be able to create containers
    27  // and start a suitable provisioner. Work is triggered by the
    28  // ContainerSetupAndProvisioner.
    29  type ContainerSetup struct {
    30  	logger        Logger
    31  	containerType instance.ContainerType
    32  	provisioner   *apiprovisioner.State
    33  	mTag          names.MachineTag
    34  	machineZone   broker.AvailabilityZoner
    35  	config        agent.Config
    36  	machineLock   machinelock.Lock
    37  	managerConfig container.ManagerConfig
    38  
    39  	credentialAPI workercommon.CredentialAPI
    40  	getNetConfig  func(network.ConfigSource) (network.InterfaceInfos, error)
    41  }
    42  
    43  // ContainerSetupParams are used to initialise a container setup worker.
    44  type ContainerSetupParams struct {
    45  	Logger        Logger
    46  	ContainerType instance.ContainerType
    47  	MTag          names.MachineTag
    48  	MachineZone   broker.AvailabilityZoner
    49  	Provisioner   *apiprovisioner.State
    50  	Config        agent.Config
    51  	MachineLock   machinelock.Lock
    52  	CredentialAPI workercommon.CredentialAPI
    53  	GetNetConfig  func(network.ConfigSource) (network.InterfaceInfos, error)
    54  }
    55  
    56  // NewContainerSetup returns a ContainerSetup to start the container
    57  // provisioner workers.
    58  func NewContainerSetup(params ContainerSetupParams) *ContainerSetup {
    59  	return &ContainerSetup{
    60  		logger:        params.Logger,
    61  		mTag:          params.MTag,
    62  		machineZone:   params.MachineZone,
    63  		containerType: params.ContainerType,
    64  		provisioner:   params.Provisioner,
    65  		config:        params.Config,
    66  		machineLock:   params.MachineLock,
    67  		credentialAPI: params.CredentialAPI,
    68  		getNetConfig:  params.GetNetConfig,
    69  	}
    70  }
    71  
    72  func (cs *ContainerSetup) initialiseContainers(abort <-chan struct{}) error {
    73  	cs.logger.Debugf("setup for %s containers", cs.containerType)
    74  	managerConfig, err := containerManagerConfig(cs.containerType, cs.provisioner)
    75  	if err != nil {
    76  		return errors.Annotate(err, "generating container manager config")
    77  	}
    78  	cs.managerConfig = managerConfig
    79  	err = cs.initContainerDependencies(abort, managerConfig)
    80  	return errors.Annotate(err, "setting up container dependencies on host machine")
    81  }
    82  
    83  // initContainerDependencies ensures that the host machine is set-up to manage
    84  // containers of the input type.
    85  func (cs *ContainerSetup) initContainerDependencies(abort <-chan struct{}, managerCfg container.ManagerConfig) error {
    86  	snapChannels := map[string]string{
    87  		"lxd": managerCfg.PopValue(config.LXDSnapChannel),
    88  	}
    89  	initialiser := getContainerInitialiser(
    90  		cs.containerType,
    91  		snapChannels,
    92  		managerCfg.PopValue(config.ContainerNetworkingMethod),
    93  	)
    94  
    95  	releaser, err := cs.acquireLock(abort, fmt.Sprintf("%s container initialisation", cs.containerType))
    96  	if err != nil {
    97  		return errors.Annotate(err, "failed to acquire initialization lock")
    98  	}
    99  	defer releaser()
   100  
   101  	if err := initialiser.Initialise(); err != nil {
   102  		return errors.Trace(err)
   103  	}
   104  
   105  	// At this point, Initialiser likely has changed host network information,
   106  	// so re-probe to have an accurate view.
   107  	observedConfig, err := cs.observeNetwork()
   108  	if err != nil {
   109  		return errors.Annotate(err, "cannot discover observed network config")
   110  	}
   111  	if len(observedConfig) > 0 {
   112  		machineTag := cs.mTag
   113  		cs.logger.Tracef("updating observed network config for %q %s containers to %#v",
   114  			machineTag, cs.containerType, observedConfig)
   115  		if err := cs.provisioner.SetHostMachineNetworkConfig(machineTag, observedConfig); err != nil {
   116  			return errors.Trace(err)
   117  		}
   118  	}
   119  	return nil
   120  }
   121  
   122  func (cs *ContainerSetup) observeNetwork() ([]params.NetworkConfig, error) {
   123  	interfaceInfos, err := cs.getNetConfig(network.DefaultConfigSource())
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	return params.NetworkConfigFromInterfaceInfo(interfaceInfos), nil
   128  }
   129  
   130  func (cs *ContainerSetup) acquireLock(abort <-chan struct{}, comment string) (func(), error) {
   131  	spec := machinelock.Spec{
   132  		Cancel:  abort,
   133  		Worker:  "container-provisioner",
   134  		Comment: comment,
   135  	}
   136  	return cs.machineLock.Acquire(spec)
   137  }
   138  
   139  // getContainerInitialiser exists to patch out in tests.
   140  var getContainerInitialiser = func(
   141  	ct instance.ContainerType,
   142  	snapChannels map[string]string,
   143  	containerNetworkingMethod string,
   144  ) container.Initialiser {
   145  
   146  	if ct == instance.LXD {
   147  		return lxd.NewContainerInitialiser(snapChannels["lxd"], containerNetworkingMethod)
   148  	}
   149  	return kvm.NewContainerInitialiser()
   150  }
   151  
   152  func (cs *ContainerSetup) initialiseContainerProvisioner() (Provisioner, error) {
   153  	cs.logger.Debugf("setup provisioner for %s containers", cs.containerType)
   154  	if cs.managerConfig == nil {
   155  		return nil, errors.NotValidf("Programming error, manager config not setup")
   156  	}
   157  	managerConfigWithZones, err := broker.ConfigureAvailabilityZone(cs.managerConfig, cs.machineZone)
   158  	if err != nil {
   159  		return nil, errors.Annotate(err, "configuring availability zones")
   160  	}
   161  
   162  	instanceBroker, err := broker.New(broker.Config{
   163  		Name:          fmt.Sprintf("%s-provisioner", string(cs.containerType)),
   164  		ContainerType: cs.containerType,
   165  		ManagerConfig: managerConfigWithZones,
   166  		APICaller:     cs.provisioner,
   167  		AgentConfig:   cs.config,
   168  		MachineTag:    cs.mTag,
   169  		MachineLock:   cs.machineLock,
   170  		GetNetConfig:  cs.getNetConfig,
   171  	})
   172  	if err != nil {
   173  		return nil, errors.Annotate(err, "initialising container infrastructure on host machine")
   174  	}
   175  
   176  	toolsFinder := getToolsFinder(cs.provisioner)
   177  	w, err := NewContainerProvisioner(
   178  		cs.containerType,
   179  		cs.provisioner,
   180  		cs.logger,
   181  		cs.config,
   182  		instanceBroker,
   183  		toolsFinder,
   184  		getDistributionGroupFinder(cs.provisioner),
   185  		cs.credentialAPI,
   186  	)
   187  	if err != nil {
   188  		return nil, errors.Trace(err)
   189  	}
   190  	return w, nil
   191  }
   192  
   193  func containerManagerConfig(
   194  	containerType instance.ContainerType, configGetter ContainerManagerConfigGetter,
   195  ) (container.ManagerConfig, error) {
   196  	// Ask the configGetter for the container manager configuration.
   197  	managerConfigResult, err := configGetter.ContainerManagerConfig(
   198  		params.ContainerManagerConfigParams{Type: containerType},
   199  	)
   200  	if err != nil {
   201  		return nil, errors.Trace(err)
   202  	}
   203  	managerConfig := container.ManagerConfig(managerConfigResult.ManagerConfig)
   204  	return managerConfig, nil
   205  }
   206  
   207  type ContainerManagerConfigGetter interface {
   208  	ContainerManagerConfig(params.ContainerManagerConfigParams) (params.ContainerManagerConfig, error)
   209  }