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  }