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 }