func maybeReleaseContainerAddresses( api APICalls, instanceID instance.Id, namespace string, log loggo.Logger, ) { if environs.AddressAllocationEnabled() { // The addresser worker will take care of the addresses. return } // If we're not using addressable containers, we might still have used MAAS // 1.8+ device to register the container when provisioning. In that case we // need to attempt releasing the device, but ignore a NotSupported error // (when we're not using MAAS 1.8+). namespacePrefix := fmt.Sprintf("%s-", namespace) tagString := strings.TrimPrefix(string(instanceID), namespacePrefix) containerTag, err := names.ParseMachineTag(tagString) if err != nil { // Not a reason to cause StopInstances to fail though.. log.Warningf("unexpected container tag %q: %v", instanceID, err) return } err = api.ReleaseContainerAddresses(containerTag) switch { case err == nil: log.Infof("released all addresses for container %q", containerTag.Id()) case errors.IsNotSupported(err): log.Warningf("not releasing all addresses for container %q: %v", containerTag.Id(), err) default: log.Warningf( "unexpected error trying to release container %q addreses: %v", containerTag.Id(), err, ) } }
// Run implements Command.Run. func (c *addCommand) Run(ctx *cmd.Context) error { return c.RunWithAPI(ctx, func(api SpaceAPI, ctx *cmd.Context) error { // Prepare a nicer message and proper arguments to use in case // there are not CIDRs given. var subnetIds []string msgSuffix := "no subnets" if !c.CIDRs.IsEmpty() { subnetIds = c.CIDRs.SortedValues() msgSuffix = fmt.Sprintf("subnets %s", strings.Join(subnetIds, ", ")) } // Add the new space. // TODO(dimitern): Accept --public|--private and pass it here. err := api.AddSpace(c.Name, subnetIds, true) if err != nil { if errors.IsNotSupported(err) { ctx.Infof("cannot add space %q: %v", c.Name, err) } if params.IsCodeUnauthorized(err) { common.PermissionsMessage(ctx.Stderr, "add a space") } return errors.Annotatef(err, "cannot add space %q", c.Name) } ctx.Infof("added space %q with %s", c.Name, msgSuffix) return nil }) }
func (r *restrictedRootSuite) TestFindDisallowedMethod(c *gc.C) { caller, err := r.root.FindMethod("Client", 0, "Status") c.Assert(err, gc.ErrorMatches, `logged in to server, no environment, "Client" not supported`) c.Assert(errors.IsNotSupported(err), jc.IsTrue) c.Assert(caller, gc.IsNil) }
func (api *MachinerAPI) getOneMachineProviderNetworkConfig(m *state.Machine) ([]params.NetworkConfig, error) { instId, err := m.InstanceId() if err != nil { return nil, errors.Trace(err) } netEnviron, err := networkingcommon.NetworkingEnvironFromModelConfig( stateenvirons.EnvironConfigGetter{api.st}, ) if errors.IsNotSupported(err) { logger.Infof("not updating provider network config: %v", err) return nil, nil } else if err != nil { return nil, errors.Annotate(err, "cannot get provider network config") } interfaceInfos, err := netEnviron.NetworkInterfaces(instId) if err != nil { return nil, errors.Annotatef(err, "cannot get network interfaces of %q", instId) } if len(interfaceInfos) == 0 { logger.Infof("not updating provider network config: no interfaces returned") return nil, nil } providerConfig := networkingcommon.NetworkConfigFromInterfaceInfo(interfaceInfos) logger.Tracef("provider network config instance %q: %+v", instId, providerConfig) return providerConfig, nil }
// MaybeReleaseAddresses releases any addresses that have been // allocated to this machine by the provider (if the provider supports // that). func (u *Undertaker) MaybeReleaseAddresses(machine names.MachineTag) error { if u.Releaser == nil { // This environ doesn't support releasing addresses. return nil } if !names.IsContainerMachine(machine.Id()) { // At the moment, only containers need their addresses releasing. return nil } interfaceInfos, err := u.API.GetProviderInterfaceInfo(machine) if err != nil { return errors.Trace(err) } if len(interfaceInfos) == 0 { logger.Debugf("%s has no addresses to release", machine) return nil } err = u.Releaser.ReleaseContainerAddresses(interfaceInfos) // Some providers say they support networking but don't // actually support container addressing; don't freak out // about those. if errors.IsNotSupported(err) { logger.Debugf("%s has addresses but provider doesn't support releasing them", machine) } else if err != nil { return errors.Trace(err) } return nil }
// ServerError returns an error suitable for returning to an API // client, with an error code suitable for various kinds of errors // generated in packages outside the API. func ServerError(err error) *params.Error { if err == nil { return nil } logger.Tracef("server RPC error %v", errors.Details(err)) msg := err.Error() // Skip past annotations when looking for the code. err = errors.Cause(err) code, ok := singletonCode(err) var info *params.ErrorInfo switch { case ok: case errors.IsUnauthorized(err): code = params.CodeUnauthorized case errors.IsNotFound(err): code = params.CodeNotFound case errors.IsUserNotFound(err): code = params.CodeUserNotFound case errors.IsAlreadyExists(err): code = params.CodeAlreadyExists case errors.IsNotAssigned(err): code = params.CodeNotAssigned case state.IsHasAssignedUnitsError(err): code = params.CodeHasAssignedUnits case state.IsHasHostedModelsError(err): code = params.CodeHasHostedModels case isNoAddressSetError(err): code = params.CodeNoAddressSet case errors.IsNotProvisioned(err): code = params.CodeNotProvisioned case IsUpgradeInProgressError(err): code = params.CodeUpgradeInProgress case state.IsHasAttachmentsError(err): code = params.CodeMachineHasAttachedStorage case isUnknownModelError(err): code = params.CodeModelNotFound case errors.IsNotSupported(err): code = params.CodeNotSupported case errors.IsBadRequest(err): code = params.CodeBadRequest case errors.IsMethodNotAllowed(err): code = params.CodeMethodNotAllowed default: if err, ok := err.(*DischargeRequiredError); ok { code = params.CodeDischargeRequired info = ¶ms.ErrorInfo{ Macaroon: err.Macaroon, // One macaroon fits all. MacaroonPath: "/", } break } code = params.ErrCode(err) } return ¶ms.Error{ Message: msg, Code: code, Info: info, } }
func releaseContainerAddresses( api APICalls, instanceID instance.Id, namespace instance.Namespace, log loggo.Logger, ) { containerTag, err := namespace.MachineTag(string(instanceID)) if err != nil { // Not a reason to cause StopInstances to fail though.. log.Warningf("unexpected container tag %q: %v", instanceID, err) return } err = api.ReleaseContainerAddresses(containerTag) switch { case err == nil: log.Infof("released all addresses for container %q", containerTag.Id()) case errors.IsNotSupported(err): log.Warningf("not releasing all addresses for container %q: %v", containerTag.Id(), err) default: log.Warningf( "unexpected error trying to release container %q addreses: %v", containerTag.Id(), err, ) } }
// StorageProviderTypes implements storage.ProviderRegistry. func (env *Environ) StorageProviderTypes() ([]storage.ProviderType, error) { var types []storage.ProviderType if _, err := env.cinderProvider(); err == nil { types = append(types, CinderProviderType) } else if !errors.IsNotSupported(err) { return nil, errors.Trace(err) } return types, nil }
// SupportsSpaces checks if the environment implements NetworkingEnviron // and also if it supports spaces. func SupportsSpaces(env Environ) bool { netEnv, ok := supportsNetworking(env) if !ok { return false } ok, err := netEnv.SupportsSpaces() if err != nil { if !errors.IsNotSupported(err) { logger.Errorf("checking model spaces support failed with: %v", err) } return false } return ok }
// AddAvailabilityZoneToInstanceData sets the AvailZone field on // instanceData docs that don't have it already. func AddAvailabilityZoneToInstanceData(st *State, azFunc func(*State, instance.Id) (string, error)) error { err := st.ResumeTransactions() if err != nil { return errors.Trace(err) } instDatas, closer := st.getRawCollection(instanceDataC) defer closer() var ops []txn.Op // Using bson.M instead of a struct is important because we need to // know if the "availzone" key is set on the raw doc. var doc bson.M iter := instDatas.Find(nil).Iter() defer iter.Close() for iter.Next(&doc) { zone, ok := doc["availzone"] if ok || ParentId(doc["machineid"].(string)) != "" { continue } zone, err := azFunc(st, instance.Id(doc["instanceid"].(string))) if err != nil { if errors.IsNotFound(err) { continue } if !errors.IsNotSupported(err) { return errors.Trace(err) } zone = "" } // Set AvailZone. ops = append(ops, txn.Op{ C: instanceDataC, Id: doc["_id"].(string), Assert: txn.DocExists, Update: bson.D{ {"$set", bson.D{{"availzone", zone}}}, }, }) } if err = iter.Err(); err != nil { return errors.Trace(err) } err = st.runTransaction(ops) return errors.Trace(err) }
func (mi *maasInstance) Addresses() ([]network.Address, error) { interfaceAddresses, err := mi.interfaceAddresses() if errors.IsNotSupported(err) { logger.Warningf( "using legacy approach to get instance addresses as %q API capability is not supported: %v", capNetworkDeploymentUbuntu, err, ) return mi.legacyAddresses() } else if err != nil { return nil, errors.Annotate(err, "getting node interfaces") } logger.Debugf("instance %q has interface addresses: %+v", mi.Id(), interfaceAddresses) return interfaceAddresses, nil }
func (s *UpgradeSuite) TestUpgradeStepsStateServer(c *gc.C) { coretesting.SkipIfI386(c, "lp:1444576") coretesting.SkipIfPPC64EL(c, "lp:1444576") coretesting.SkipIfWindowsBug(c, "lp:1446885") s.setInstantRetryStrategy(c) // Upload tools to provider storage, so they can be migrated to environment storage. stor, err := environs.LegacyStorage(s.State) if !errors.IsNotSupported(err) { c.Assert(err, jc.ErrorIsNil) envtesting.AssertUploadFakeToolsVersions( c, stor, "releases", s.Environ.Config().AgentStream(), s.oldVersion) } s.assertUpgradeSteps(c, state.JobManageEnviron) s.assertStateServerUpgrades(c) }
// environmentDataSources returns simplestreams datasources for the environment // by calling the functions registered in RegisterImageDataSourceFunc. // The datasources returned will be in the same order the functions were registered. func environmentDataSources(env Environ) ([]simplestreams.DataSource, error) { datasourceFuncsMu.RLock() defer datasourceFuncsMu.RUnlock() var datasources []simplestreams.DataSource for _, f := range datasourceFuncs { datasource, err := f.f(env) if err != nil { if errors.IsNotSupported(err) { continue } return nil, err } datasources = append(datasources, datasource) } return datasources, nil }
// environmentDataSources returns simplestreams datasources for the environment // by calling the functions registered in RegisterToolsDataSourceFunc. // The datasources returned will be in the same order the functions were registered. func environmentDataSources(env environs.Environ) ([]simplestreams.DataSource, error) { toolsDatasourceFuncsMu.RLock() defer toolsDatasourceFuncsMu.RUnlock() var datasources []simplestreams.DataSource for _, f := range toolsDatasourceFuncs { logger.Debugf("trying datasource %q", f.id) datasource, err := f.f(env) if err != nil { if errors.IsNotSupported(err) { continue } return nil, err } datasources = append(datasources, datasource) } return datasources, nil }
// Run retrieves the debug log via the API. func (c *DebugLogCommand) Run(ctx *cmd.Context) (err error) { client, err := getDebugLogAPI(c.EnvName) if err != nil { return err } defer client.Close() debugLog, err := client.WatchDebugLog(c.params) if err != nil { if errors.IsNotSupported(err) { return c.watchDebugLog1dot18(ctx) } return err } defer debugLog.Close() _, err = io.Copy(ctx.Stdout, debugLog) return err }
// migrateCustomImageMetadata copies uploaded image metadata from provider // storage to environment storage, preserving paths. func migrateCustomImageMetadata(st *state.State, agentConfig agent.Config) error { logger.Debugf("migrating custom image metadata to environment storage") estor := newStateStorage(st.EnvironUUID(), st.MongoSession()) // Local and manual provider host storage on the state server's // filesystem, and serve via HTTP storage. The storage worker // doesn't run yet, so we just open the files directly. var pstor storage.StorageReader providerType := agentConfig.Value(agent.ProviderType) if providerType == provider.Local || provider.IsManual(providerType) { storageDir := agentConfig.Value(agent.StorageDir) var err error pstor, err = filestorage.NewFileStorageReader(storageDir) if err != nil { return errors.Annotate(err, "cannot get local filesystem storage reader") } } else { var err error pstor, err = environs.LegacyStorage(st) if errors.IsNotSupported(err) { return nil } else if err != nil { return errors.Annotate(err, "cannot get provider storage") } } paths, err := pstor.List(storage.BaseImagesPath) if err != nil { return err } for _, path := range paths { logger.Infof("migrating image metadata at path %q", path) data, err := readImageMetadata(pstor, path) if err != nil { return errors.Annotate(err, "failed to read image metadata") } err = estor.Put(path, bytes.NewReader(data), int64(len(data))) if err != nil { return errors.Annotate(err, "failed to write image metadata") } } return nil }
// getNetworkingEnviron checks if the environment implements NetworkingEnviron // and also if it supports IP address allocation. func (api *AddresserAPI) getNetworkingEnviron() (environs.NetworkingEnviron, bool, error) { config, err := api.st.EnvironConfig() if err != nil { return nil, false, errors.Annotate(err, "getting environment config") } env, err := environs.New(config) if err != nil { return nil, false, errors.Annotate(err, "validating environment config") } netEnv, ok := environs.SupportsNetworking(env) if !ok { return nil, false, nil } ok, err = netEnv.SupportsAddressAllocation(network.AnySubnet) if err != nil && !errors.IsNotSupported(err) { return nil, false, errors.Annotate(err, "checking allocation support") } return netEnv, ok, nil }
// ServerError returns an error suitable for returning to an API // client, with an error code suitable for various kinds of errors // generated in packages outside the API. func ServerError(err error) *params.Error { if err == nil { return nil } msg := err.Error() // Skip past annotations when looking for the code. err = errors.Cause(err) code, ok := singletonCode(err) switch { case ok: case errors.IsUnauthorized(err): code = params.CodeUnauthorized case errors.IsNotFound(err): code = params.CodeNotFound case errors.IsAlreadyExists(err): code = params.CodeAlreadyExists case errors.IsNotAssigned(err): code = params.CodeNotAssigned case state.IsHasAssignedUnitsError(err): code = params.CodeHasAssignedUnits case IsNoAddressSetError(err): code = params.CodeNoAddressSet case errors.IsNotProvisioned(err): code = params.CodeNotProvisioned case state.IsUpgradeInProgressError(err): code = params.CodeUpgradeInProgress case state.IsHasAttachmentsError(err): code = params.CodeMachineHasAttachedStorage case IsUnknownEnviromentError(err): code = params.CodeNotFound case errors.IsNotSupported(err): code = params.CodeNotSupported default: code = params.ErrCode(err) } return ¶ms.Error{ Message: msg, Code: code, } }
// SupportsSpaces checks if the environment implements NetworkingEnviron // and also if it supports spaces. func SupportsSpaces(backing NetworkBacking) error { config, err := backing.ModelConfig() if err != nil { return errors.Annotate(err, "getting model config") } env, err := environs.New(config) if err != nil { return errors.Annotate(err, "validating model config") } netEnv, ok := environs.SupportsNetworking(env) if !ok { return errors.NotSupportedf("networking") } ok, err = netEnv.SupportsSpaces() if !ok { if err != nil && !errors.IsNotSupported(err) { logger.Warningf("checking model spaces support failed with: %v", err) } return errors.NotSupportedf("spaces") } return nil }
// destroyControllerManagedEnvirons destroys all environments managed by this // environment's controller. func (e *Environ) destroyControllerManagedEnvirons() error { // Terminate all instances managed by the controller. insts, err := e.allControllerManagedInstances(nil, false) if err != nil { return errors.Annotate(err, "listing instances") } instIds := make([]instance.Id, len(insts)) for i, inst := range insts { instIds[i] = inst.Id() } if err := e.terminateInstances(instIds); err != nil { return errors.Annotate(err, "terminating instances") } // Delete all volumes managed by the controller. cfg := e.Config() storageAdapter, err := newOpenstackStorageAdapter(cfg) if err == nil { volIds, err := allControllerManagedVolumes(storageAdapter, cfg.ControllerUUID()) if err != nil { return errors.Annotate(err, "listing volumes") } errs := destroyVolumes(storageAdapter, volIds) for i, err := range errs { if err == nil { continue } return errors.Annotatef(err, "destroying volume %q", volIds[i], err) } } else if !errors.IsNotSupported(err) { return errors.Trace(err) } // Security groups for hosted models are destroyed by the // DeleteAllGroups method call from Destroy(). return nil }
func (s *restrictModelSuite) TestBlocked(c *gc.C) { caller, err := s.root.FindMethod("ModelManager", 2, "ListModels") c.Assert(err, gc.ErrorMatches, `facade "ModelManager" not supported for model API connection`) c.Assert(errors.IsNotSupported(err), jc.IsTrue) c.Assert(caller, gc.IsNil) }
// Run implements Command.Run. func (c *listCommand) Run(ctx *cmd.Context) error { return c.RunWithAPI(ctx, func(api SpaceAPI, ctx *cmd.Context) error { spaces, err := api.ListSpaces() if err != nil { if errors.IsNotSupported(err) { ctx.Infof("cannot list spaces: %v", err) } return errors.Annotate(err, "cannot list spaces") } if len(spaces) == 0 { ctx.Infof("no spaces to display") return c.out.Write(ctx, nil) } if c.Short { result := formattedShortList{} for _, space := range spaces { result.Spaces = append(result.Spaces, space.Name) } return c.out.Write(ctx, result) } // Construct the output list for displaying with the chosen // format. result := formattedList{ Spaces: make(map[string]map[string]formattedSubnet), } for _, space := range spaces { result.Spaces[space.Name] = make(map[string]formattedSubnet) for _, subnet := range space.Subnets { subResult := formattedSubnet{ Type: typeUnknown, ProviderId: subnet.ProviderId, Zones: subnet.Zones, } // Display correct status according to the life cycle value. // // TODO(dimitern): Do this on the apiserver side, also // do the same for params.Space, so in case of an // error it can be displayed. switch subnet.Life { case params.Alive: subResult.Status = statusInUse case params.Dying, params.Dead: subResult.Status = statusTerminating } // Use the CIDR to determine the subnet type. // TODO(dimitern): Do this on the apiserver side. if ip, _, err := net.ParseCIDR(subnet.CIDR); err != nil { // This should never happen as subnets will be // validated before saving in state. msg := fmt.Sprintf("error: invalid subnet CIDR: %s", subnet.CIDR) subResult.Status = msg } else if ip.To4() != nil { subResult.Type = typeIPv4 } else if ip.To16() != nil { subResult.Type = typeIPv6 } result.Spaces[space.Name][subnet.CIDR] = subResult } } return c.out.Write(ctx, result) }) }
// migrateToolsStorage copies tools from provider storage to // environment storage. func migrateToolsStorage(st *state.State, agentConfig agent.Config) error { logger.Debugf("migrating tools to environment storage") tstor, err := stateToolsStorage(st) if err != nil { return errors.Annotate(err, "cannot get tools storage") } defer tstor.Close() // Local and manual provider host storage on the state server's // filesystem, and serve via HTTP storage. The storage worker // doesn't run yet, so we just open the files directly. var stor storage.StorageReader providerType := agentConfig.Value(agent.ProviderType) if providerType == provider.Local || provider.IsManual(providerType) { storageDir := agentConfig.Value(agent.StorageDir) var err error stor, err = filestorage.NewFileStorageReader(storageDir) if err != nil { return errors.Annotate(err, "cannot get local filesystem storage reader") } } else { var err error stor, err = environs.LegacyStorage(st) if errors.IsNotSupported(err) { return nil } else if err != nil { return errors.Annotate(err, "cannot get provider storage") } } // Search provider storage for tools. datasource := storage.NewStorageSimpleStreamsDataSource("provider storage", stor, storage.BaseToolsPath) toolsList, err := envtools.FindToolsForCloud( []simplestreams.DataSource{datasource}, simplestreams.CloudSpec{}, envtools.ReleasedStream, -1, -1, tools.Filter{}) switch err { case nil: break case tools.ErrNoMatches, envtools.ErrNoTools: // No tools in provider storage: nothing to do. return nil default: return errors.Annotate(err, "cannot find tools in provider storage") } for _, agentTools := range toolsList { logger.Infof("migrating %v tools to environment storage", agentTools.Version) data, err := fetchToolsArchive(stor, envtools.LegacyReleaseDirectory, agentTools) if err != nil { return errors.Annotatef(err, "failed to fetch %v tools", agentTools.Version) } err = tstor.AddTools(bytes.NewReader(data), toolstorage.Metadata{ Version: agentTools.Version, Size: agentTools.Size, SHA256: agentTools.SHA256, }) if err != nil { return errors.Annotatef(err, "failed to add %v tools to environment storage", agentTools.Version) } } return nil }
func (s *restrictControllerSuite) TestNotAllowed(c *gc.C) { caller, err := s.root.FindMethod("Client", 1, "FullStatus") c.Assert(err, gc.ErrorMatches, `facade "Client" not supported for controller API connection`) c.Assert(errors.IsNotSupported(err), jc.IsTrue) c.Assert(caller, gc.IsNil) }
func prepareOrGetContainerInterfaceInfo( api APICalls, machineID string, bridgeDevice string, allocateOrMaintain bool, enableNAT bool, startingNetworkInfo []network.InterfaceInfo, log loggo.Logger, providerType string, ) ([]network.InterfaceInfo, error) { maintain := !allocateOrMaintain if environs.AddressAllocationEnabled(providerType) { if maintain { log.Debugf("running maintenance for container %q", machineID) } else { log.Debugf("trying to allocate static IP for container %q", machineID) } allocatedInfo, err := configureContainerNetwork( machineID, bridgeDevice, api, startingNetworkInfo, allocateOrMaintain, enableNAT, ) if err != nil && !maintain { log.Infof("not allocating static IP for container %q: %v", machineID, err) } return allocatedInfo, err } if maintain { log.Debugf("address allocation disabled: Not running maintenance for machine %q", machineID) return nil, nil } log.Debugf("address allocation feature flag not enabled; using multi-bridge networking for container %q", machineID) // In case we're running on MAAS 1.8+ with devices support, we'll still // call PrepareContainerInterfaceInfo(), but we'll ignore a NotSupported // error if we get it (which means we're not using MAAS 1.8+). containerTag := names.NewMachineTag(machineID) preparedInfo, err := api.PrepareContainerInterfaceInfo(containerTag) if err != nil && errors.IsNotSupported(err) { log.Warningf("new container %q not registered as device: not running on MAAS 1.8+", machineID) return nil, nil } else if err != nil { return nil, errors.Trace(err) } log.Tracef("PrepareContainerInterfaceInfo returned %+v", preparedInfo) dnsServersFound := false for _, info := range preparedInfo { if len(info.DNSServers) > 0 { dnsServersFound = true break } } if !dnsServersFound { logger.Warningf("no DNS settings found, discovering the host settings") dnsServers, searchDomain, err := localDNSServers() if err != nil { return nil, errors.Trace(err) } // Since the result is sorted, the first entry is the primary NIC. preparedInfo[0].DNSServers = dnsServers preparedInfo[0].DNSSearchDomains = []string{searchDomain} logger.Debugf( "setting DNS servers %+v and domains %+v on container interface %q", preparedInfo[0].DNSServers, preparedInfo[0].DNSSearchDomains, preparedInfo[0].InterfaceName, ) } return preparedInfo, nil }
func prepareOrGetContainerInterfaceInfo( api APICalls, machineID string, bridgeDevice string, allocateOrMaintain bool, enableNAT bool, startingNetworkInfo []network.InterfaceInfo, log loggo.Logger, ) ([]network.InterfaceInfo, error) { maintain := !allocateOrMaintain if environs.AddressAllocationEnabled() { if maintain { log.Debugf("running maintenance for container %q", machineID) } else { log.Debugf("trying to allocate static IP for container %q", machineID) } allocatedInfo, err := configureContainerNetwork( machineID, bridgeDevice, api, startingNetworkInfo, allocateOrMaintain, enableNAT, ) if err != nil && !maintain { log.Infof("not allocating static IP for container %q: %v", machineID, err) } return allocatedInfo, err } if maintain { log.Debugf("address allocation disabled: Not running maintenance for machine %q", machineID) return nil, nil } log.Debugf("address allocation feature flag not enabled; using DHCP for container %q", machineID) // In case we're running on MAAS 1.8+ with devices support, we'll still // call PrepareContainerInterfaceInfo(), but we'll ignore a NotSupported // error if we get it (which means we're not using MAAS 1.8+). containerTag := names.NewMachineTag(machineID) preparedInfo, err := api.PrepareContainerInterfaceInfo(containerTag) if err != nil && errors.IsNotSupported(err) { log.Warningf("new container %q not registered as device: not running on MAAS 1.8+", machineID) return nil, nil } else if err != nil { return nil, errors.Trace(err) } dnsServers, searchDomain, dnsErr := localDNSServers() if dnsErr != nil { return nil, errors.Trace(dnsErr) } for i, _ := range preparedInfo { preparedInfo[i].DNSServers = dnsServers preparedInfo[i].DNSSearch = searchDomain } log.Tracef("PrepareContainerInterfaceInfo returned %#v", preparedInfo) // Most likely there will be only one item in the list, but check // all of them for forward compatibility. macAddresses := set.NewStrings() for _, prepInfo := range preparedInfo { macAddresses.Add(prepInfo.MACAddress) } log.Infof( "new container %q registered as a MAAS device with MAC address(es) %v", machineID, macAddresses.SortedValues(), ) return preparedInfo, nil }