github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/provisioner/broker.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/utils/arch" 10 "github.com/juju/version" 11 "gopkg.in/juju/names.v2" 12 13 apiprovisioner "github.com/juju/juju/api/provisioner" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/cloudconfig" 16 "github.com/juju/juju/container" 17 "github.com/juju/juju/core/instance" 18 "github.com/juju/juju/network" 19 "github.com/juju/juju/tools" 20 ) 21 22 //go:generate mockgen -package mocks -destination mocks/apicalls_mock.go github.com/juju/juju/worker/provisioner APICalls 23 type APICalls interface { 24 ContainerConfig() (params.ContainerConfig, error) 25 PrepareContainerInterfaceInfo(names.MachineTag) ([]network.InterfaceInfo, error) 26 GetContainerInterfaceInfo(names.MachineTag) ([]network.InterfaceInfo, error) 27 GetContainerProfileInfo(names.MachineTag) ([]*apiprovisioner.LXDProfileResult, error) 28 ReleaseContainerAddresses(names.MachineTag) error 29 SetHostMachineNetworkConfig(names.MachineTag, []params.NetworkConfig) error 30 HostChangesForContainer(containerTag names.MachineTag) ([]network.DeviceToBridge, int, error) 31 } 32 33 type hostArchToolsFinder struct { 34 f ToolsFinder 35 } 36 37 // FindTools is defined on the ToolsFinder interface. 38 func (h hostArchToolsFinder) FindTools(v version.Number, series, _ string) (tools.List, error) { 39 // Override the arch constraint with the arch of the host. 40 return h.f.FindTools(v, series, arch.HostArch()) 41 } 42 43 // resolvConf contains the full path to common resolv.conf files on the local 44 // system. Defined here so it can be overridden for testing. 45 var resolvConfFiles = []string{"/etc/resolv.conf", "/etc/systemd/resolved.conf", "/run/systemd/resolve/resolv.conf"} 46 47 func prepareOrGetContainerInterfaceInfo( 48 api APICalls, 49 machineID string, 50 allocateOrMaintain bool, 51 log loggo.Logger, 52 ) ([]network.InterfaceInfo, error) { 53 maintain := !allocateOrMaintain 54 55 if maintain { 56 // TODO(jam): 2016-12-14 The function is called 57 // 'prepareOrGet', but the only time we would handle the 'Get' 58 // side, we explicitly abort. Something seems wrong. 59 log.Debugf("not running maintenance for machine %q", machineID) 60 return nil, nil 61 } 62 63 log.Debugf("using multi-bridge networking for container %q", machineID) 64 65 containerTag := names.NewMachineTag(machineID) 66 preparedInfo, err := api.PrepareContainerInterfaceInfo(containerTag) 67 if err != nil { 68 return nil, errors.Trace(err) 69 } 70 log.Tracef("PrepareContainerInterfaceInfo returned %+v", preparedInfo) 71 72 return preparedInfo, nil 73 } 74 75 // finishNetworkConfig populates the ParentInterfaceName, DNSServers, and 76 // DNSSearchDomains fields on each element, when they are not set. The given 77 // bridgeDevice is used for ParentInterfaceName, while the DNS config is 78 // discovered using network.ParseResolvConf(). If interfaces has zero length, 79 // container.FallbackInterfaceInfo() is used as fallback. 80 func finishNetworkConfig(bridgeDevice string, interfaces []network.InterfaceInfo) ([]network.InterfaceInfo, error) { 81 haveNameservers, haveSearchDomains := false, false 82 if len(interfaces) == 0 { 83 // Use the fallback network config as a last resort. 84 interfaces = container.FallbackInterfaceInfo() 85 } 86 87 results := make([]network.InterfaceInfo, len(interfaces)) 88 for i, info := range interfaces { 89 if info.ParentInterfaceName == "" { 90 info.ParentInterfaceName = bridgeDevice 91 } 92 93 if len(info.DNSServers) > 0 { 94 haveNameservers = true 95 } 96 97 if len(info.DNSSearchDomains) > 0 { 98 haveSearchDomains = true 99 } 100 results[i] = info 101 } 102 103 if !haveNameservers || !haveSearchDomains { 104 warnMissing := func(s string) { logger.Warningf("no %s supplied by provider, using host's %s.", s, s) } 105 if !haveNameservers { 106 warnMissing("name servers") 107 } 108 if !haveSearchDomains { 109 warnMissing("search domains") 110 } 111 112 logger.Warningf("incomplete DNS config found, discovering host's DNS config") 113 dnsConfig, err := findDNSServerConfig() 114 if err != nil { 115 return nil, errors.Trace(err) 116 } 117 118 // Since the result is sorted, the first entry is the primary NIC. Also, 119 // results always contains at least one element. 120 results[0].DNSServers = dnsConfig.Nameservers 121 results[0].DNSSearchDomains = dnsConfig.SearchDomains 122 logger.Debugf( 123 "setting DNS servers %+v and domains %+v on container interface %q", 124 results[0].DNSServers, results[0].DNSSearchDomains, results[0].InterfaceName, 125 ) 126 } 127 128 return results, nil 129 } 130 131 // findDNSServerConfig is a heuristic method to find an adequate DNS 132 // configuration. Currently the only rule that is implemented is that common 133 // configuration files are parsed until a configuration is found that is not a 134 // loopback address (i.e systemd/resolved stub address). 135 func findDNSServerConfig() (*network.DNSConfig, error) { 136 for _, dnsConfigFile := range resolvConfFiles { 137 dnsConfig, err := network.ParseResolvConf(dnsConfigFile) 138 if err != nil { 139 return nil, errors.Trace(err) 140 } 141 for _, nameServer := range dnsConfig.Nameservers { 142 if nameServer.Scope != network.ScopeMachineLocal { 143 logger.Debugf("The DNS configuration from %s has been selected for use", dnsConfigFile) 144 return dnsConfig, nil 145 } 146 } 147 } 148 return nil, errors.New("A DNS configuration could not be found.") 149 } 150 151 func releaseContainerAddresses( 152 api APICalls, 153 instanceID instance.Id, 154 namespace instance.Namespace, 155 log loggo.Logger, 156 ) { 157 containerTag, err := namespace.MachineTag(string(instanceID)) 158 if err != nil { 159 // Not a reason to cause StopInstances to fail though.. 160 log.Warningf("unexpected container tag %q: %v", instanceID, err) 161 return 162 } 163 err = api.ReleaseContainerAddresses(containerTag) 164 switch { 165 case err == nil: 166 log.Infof("released all addresses for container %q", containerTag.Id()) 167 case errors.IsNotSupported(err): 168 log.Warningf("not releasing all addresses for container %q: %v", containerTag.Id(), err) 169 default: 170 log.Warningf( 171 "unexpected error trying to release container %q addreses: %v", 172 containerTag.Id(), err, 173 ) 174 } 175 } 176 177 // matchHostArchTools filters the given list of tools to the host architecture. 178 func matchHostArchTools(allTools tools.List) (tools.List, error) { 179 arch := arch.HostArch() 180 archTools, err := allTools.Match(tools.Filter{Arch: arch}) 181 if err == tools.ErrNoMatches { 182 return nil, errors.Errorf( 183 "need agent binaries for arch %s, only found %s", 184 arch, allTools.Arches(), 185 ) 186 } else if err != nil { 187 return nil, errors.Trace(err) 188 } 189 return archTools, nil 190 } 191 192 // GetMachineCloudInitData is for testing purposes. 193 var GetMachineCloudInitData = cloudconfig.GetMachineCloudInitData 194 195 // combinedCloudInitData returns a combined map of the given cloudInitData 196 // and instance cloud init properties provided. 197 func combinedCloudInitData( 198 cloudInitData map[string]interface{}, 199 containerInheritProperties, series string, 200 log loggo.Logger, 201 ) (map[string]interface{}, error) { 202 if containerInheritProperties == "" { 203 return cloudInitData, nil 204 } 205 machineData, err := GetMachineCloudInitData(series) 206 if err != nil { 207 return nil, err 208 } 209 if machineData == nil { 210 return cloudInitData, nil 211 } 212 213 if cloudInitData == nil { 214 cloudInitData = make(map[string]interface{}) 215 } 216 217 resultsMap := cloudconfig.CloudConfigByVersionFunc(series)(containerInheritProperties, machineData, log) 218 for k, v := range resultsMap { 219 cloudInitData[k] = v 220 } 221 222 return cloudInitData, nil 223 }