github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/lxd/environ_instance.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package lxd 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/version" 9 10 "github.com/juju/juju/container/lxd" 11 "github.com/juju/juju/core/instance" 12 "github.com/juju/juju/environs" 13 "github.com/juju/juju/environs/context" 14 "github.com/juju/juju/environs/instances" 15 "github.com/juju/juju/environs/tags" 16 "github.com/juju/juju/provider/common" 17 ) 18 19 // Instances returns the available instances in the environment that 20 // match the provided instance IDs. For IDs that did not match any 21 // instances, the result at the corresponding index will be nil. In that 22 // case the error will be environs.ErrPartialInstances (or 23 // ErrNoInstances if none of the IDs match an instance). 24 func (env *environ) Instances(ctx context.ProviderCallContext, ids []instance.Id) ([]instances.Instance, error) { 25 if len(ids) == 0 { 26 return nil, environs.ErrNoInstances 27 } 28 29 all, err := env.allInstances() 30 if err != nil { 31 // We don't return the error since we need to pack one instance 32 // for each ID into the result. If there is a problem then we 33 // will return either ErrPartialInstances or ErrNoInstances. 34 // TODO(ericsnow) Skip returning here only for certain errors? 35 logger.Errorf("failed to get instances from LXD: %v", err) 36 common.HandleCredentialError(IsAuthorisationFailure, err, ctx) 37 err = errors.Trace(err) 38 } 39 40 // Build the result, matching the provided instance IDs. 41 numFound := 0 // This will never be greater than len(ids). 42 results := make([]instances.Instance, len(ids)) 43 for i, id := range ids { 44 inst := findInst(id, all) 45 if inst != nil { 46 numFound++ 47 } 48 results[i] = inst 49 } 50 51 if numFound == 0 { 52 if err == nil { 53 err = environs.ErrNoInstances 54 } 55 } else if numFound != len(ids) { 56 err = environs.ErrPartialInstances 57 } 58 return results, err 59 } 60 61 func findInst(id instance.Id, instances []*environInstance) instances.Instance { 62 for _, inst := range instances { 63 if id == inst.Id() { 64 return inst 65 } 66 } 67 return nil 68 } 69 70 // instances returns a list of all "alive" instances in the environment. 71 // We match machine names to the pattern "juju-<model-UUID>-machine-*" 72 // to ensure that only machines for the environment are returned. This 73 // is necessary to isolate multiple models within the same LXD. 74 func (env *environ) allInstances() ([]*environInstance, error) { 75 prefix := env.namespace.Prefix() 76 insts, err := env.prefixedInstances(prefix) 77 return insts, errors.Trace(err) 78 } 79 80 // prefixedInstances returns instances with the specified prefix. 81 func (env *environ) prefixedInstances(prefix string) ([]*environInstance, error) { 82 containers, err := env.server.AliveContainers(prefix) 83 err = errors.Trace(err) 84 85 // Turn lxd.Container values into *environInstance values, 86 // whether or not we got an error. 87 var results []*environInstance 88 for _, c := range containers { 89 c := c 90 inst := newInstance(&c, env) 91 results = append(results, inst) 92 } 93 return results, err 94 } 95 96 // ControllerInstances returns the IDs of the instances corresponding 97 // to juju controllers. 98 func (env *environ) ControllerInstances(ctx context.ProviderCallContext, controllerUUID string) ([]instance.Id, error) { 99 containers, err := env.server.AliveContainers("juju-") 100 if err != nil { 101 common.HandleCredentialError(IsAuthorisationFailure, err, ctx) 102 return nil, errors.Trace(err) 103 } 104 105 var results []instance.Id 106 for _, c := range containers { 107 if c.Metadata(tags.JujuController) != controllerUUID { 108 continue 109 } 110 if c.Metadata(tags.JujuIsController) == "true" { 111 results = append(results, instance.Id(c.Name)) 112 } 113 } 114 if len(results) == 0 { 115 return nil, environs.ErrNotBootstrapped 116 } 117 return results, nil 118 } 119 120 // AdoptResources updates the controller tags on all instances to have the 121 // new controller id. It's part of the Environ interface. 122 func (env *environ) AdoptResources(ctx context.ProviderCallContext, controllerUUID string, fromVersion version.Number) error { 123 instances, err := env.AllInstances(ctx) 124 if err != nil { 125 common.HandleCredentialError(IsAuthorisationFailure, err, ctx) 126 return errors.Annotate(err, "all instances") 127 } 128 129 var failed []instance.Id 130 qualifiedKey := lxd.UserNamespacePrefix + tags.JujuController 131 for _, instance := range instances { 132 id := instance.Id() 133 // TODO (manadart 2018-06-27) This is a smell. 134 // Everywhere else, we update the container config on a container and then call WriteContainer. 135 // If we added a method directly to environInstance to do this, we wouldn't need this 136 // implementation of UpdateContainerConfig at all, and the container representation we are 137 // holding would be consistent with that on the server. 138 err := env.server.UpdateContainerConfig(string(id), map[string]string{qualifiedKey: controllerUUID}) 139 if err != nil { 140 logger.Errorf("error setting controller uuid tag for %q: %v", id, err) 141 failed = append(failed, id) 142 } 143 } 144 if len(failed) != 0 { 145 return errors.Errorf("failed to update controller for some instances: %v", failed) 146 } 147 return nil 148 }