github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/provider/cloudsigma/environinstance.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cloudsigma
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  
    10  	"github.com/juju/juju/cloudconfig/instancecfg"
    11  	"github.com/juju/juju/cloudconfig/providerinit"
    12  	"github.com/juju/juju/environs"
    13  	"github.com/juju/juju/environs/imagemetadata"
    14  	"github.com/juju/juju/environs/simplestreams"
    15  	"github.com/juju/juju/instance"
    16  	"github.com/juju/juju/network"
    17  	"github.com/juju/juju/tools"
    18  )
    19  
    20  //
    21  // Imlementation of InstanceBroker: methods for starting and stopping instances.
    22  //
    23  
    24  var findInstanceImage = func(env *environ, ic *imagemetadata.ImageConstraint) (*imagemetadata.ImageMetadata, error) {
    25  
    26  	sources, err := environs.ImageMetadataSources(env)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  
    31  	matchingImages, _, err := imagemetadata.Fetch(sources, ic, false)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	if len(matchingImages) == 0 {
    36  		return nil, errors.New("no matching image meta data")
    37  	}
    38  
    39  	return matchingImages[0], nil
    40  }
    41  
    42  // MaintainInstance is specified in the InstanceBroker interface.
    43  func (*environ) MaintainInstance(args environs.StartInstanceParams) error {
    44  	return nil
    45  }
    46  
    47  // StartInstance asks for a new instance to be created, associated with
    48  // the provided config in machineConfig. The given config describes the juju
    49  // state for the new instance to connect to. The config MachineNonce, which must be
    50  // unique within an environment, is used by juju to protect against the
    51  // consequences of multiple instances being started with the same machine id.
    52  func (env *environ) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) {
    53  	logger.Infof("sigmaEnviron.StartInstance...")
    54  
    55  	if args.InstanceConfig == nil {
    56  		return nil, errors.New("instance configuration is nil")
    57  	}
    58  
    59  	if args.InstanceConfig.HasNetworks() {
    60  		return nil, errors.New("starting instances with networks is not supported yet")
    61  	}
    62  
    63  	if len(args.Tools) == 0 {
    64  		return nil, errors.New("tools not found")
    65  	}
    66  
    67  	region, _ := env.Region()
    68  	img, err := findInstanceImage(env, imagemetadata.NewImageConstraint(simplestreams.LookupParams{
    69  		CloudSpec: region,
    70  		Series:    args.Tools.AllSeries(),
    71  		Arches:    args.Tools.Arches(),
    72  		Stream:    env.Config().ImageStream(),
    73  	}))
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	tools, err := args.Tools.Match(tools.Filter{Arch: img.Arch})
    79  	if err != nil {
    80  		return nil, errors.Errorf("chosen architecture %v not present in %v", img.Arch, args.Tools.Arches())
    81  	}
    82  
    83  	args.InstanceConfig.Tools = tools[0]
    84  	if err := instancecfg.FinishInstanceConfig(args.InstanceConfig, env.Config()); err != nil {
    85  		return nil, err
    86  	}
    87  	userData, err := providerinit.ComposeUserData(args.InstanceConfig, nil)
    88  	if err != nil {
    89  		return nil, errors.Annotate(err, "cannot make user data")
    90  	}
    91  
    92  	logger.Debugf("cloudsigma user data; %d bytes", len(userData))
    93  
    94  	client := env.client
    95  	server, rootdrive, arch, err := client.newInstance(args, img, userData)
    96  	if err != nil {
    97  		return nil, errors.Errorf("failed start instance: %v", err)
    98  	}
    99  
   100  	inst := &sigmaInstance{server: server}
   101  
   102  	// prepare hardware characteristics
   103  	hwch, err := inst.hardware(arch, rootdrive.Size())
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	logger.Debugf("hardware: %v", hwch)
   109  	return &environs.StartInstanceResult{
   110  		Instance: inst,
   111  		Hardware: hwch,
   112  	}, nil
   113  }
   114  
   115  // AllInstances returns all instances currently known to the broker.
   116  func (env *environ) AllInstances() ([]instance.Instance, error) {
   117  	// Please note that this must *not* return instances that have not been
   118  	// allocated as part of this environment -- if it does, juju will see they
   119  	// are not tracked in state, assume they're stale/rogue, and shut them down.
   120  
   121  	logger.Tracef("environ.AllInstances...")
   122  
   123  	servers, err := env.client.instances()
   124  	if err != nil {
   125  		logger.Tracef("environ.AllInstances failed: %v", err)
   126  		return nil, err
   127  	}
   128  
   129  	instances := make([]instance.Instance, 0, len(servers))
   130  	for _, server := range servers {
   131  		instance := sigmaInstance{server: server}
   132  		instances = append(instances, instance)
   133  	}
   134  
   135  	if logger.LogLevel() <= loggo.TRACE {
   136  		logger.Tracef("All instances, len = %d:", len(instances))
   137  		for _, instance := range instances {
   138  			logger.Tracef("... id: %q, status: %q", instance.Id(), instance.Status())
   139  		}
   140  	}
   141  
   142  	return instances, nil
   143  }
   144  
   145  // Instances returns a slice of instances corresponding to the
   146  // given instance ids.  If no instances were found, but there
   147  // was no other error, it will return ErrNoInstances.  If
   148  // some but not all the instances were found, the returned slice
   149  // will have some nil slots, and an ErrPartialInstances error
   150  // will be returned.
   151  func (env *environ) Instances(ids []instance.Id) ([]instance.Instance, error) {
   152  	logger.Tracef("environ.Instances %#v", ids)
   153  	// Please note that this must *not* return instances that have not been
   154  	// allocated as part of this environment -- if it does, juju will see they
   155  	// are not tracked in state, assume they're stale/rogue, and shut them down.
   156  	// This advice applies even if an instance id passed in corresponds to a
   157  	// real instance that's not part of the environment -- the Environ should
   158  	// treat that no differently to a request for one that does not exist.
   159  
   160  	m, err := env.client.instanceMap()
   161  	if err != nil {
   162  		logger.Warningf("environ.Instances failed: %v", err)
   163  		return nil, err
   164  	}
   165  
   166  	var found int
   167  	r := make([]instance.Instance, len(ids))
   168  	for i, id := range ids {
   169  		if s, ok := m[string(id)]; ok {
   170  			r[i] = sigmaInstance{server: s}
   171  			found++
   172  		}
   173  	}
   174  
   175  	if found == 0 {
   176  		err = environs.ErrNoInstances
   177  	} else if found != len(ids) {
   178  		err = environs.ErrPartialInstances
   179  	}
   180  
   181  	return r, err
   182  }
   183  
   184  // StopInstances shuts down the given instances.
   185  func (env *environ) StopInstances(instances ...instance.Id) error {
   186  	logger.Debugf("stop instances %+v", instances)
   187  
   188  	var err error
   189  
   190  	for _, instance := range instances {
   191  		if e := env.client.stopInstance(instance); e != nil {
   192  			err = e
   193  		}
   194  	}
   195  
   196  	return err
   197  }
   198  
   199  // AllocateAddress requests a new address to be allocated for the
   200  // given instance on the given network.
   201  func (env *environ) AllocateAddress(instID instance.Id, netID network.Id, addr network.Address, macAddress, hostname string) error {
   202  	return errors.NotSupportedf("AllocateAddress")
   203  }
   204  func (env *environ) ReleaseAddress(instId instance.Id, netId network.Id, addr network.Address, macAddress string) error {
   205  	return errors.NotSupportedf("ReleaseAddress")
   206  }
   207  func (env *environ) Subnets(inst instance.Id) ([]network.SubnetInfo, error) {
   208  	return nil, errors.NotSupportedf("Subnets")
   209  }
   210  
   211  // ListNetworks returns basic information about all networks known
   212  // by the provider for the environment. They may be unknown to juju
   213  // yet (i.e. when called initially or when a new network was created).
   214  func (env *environ) ListNetworks() ([]network.SubnetInfo, error) {
   215  	return nil, errors.NotImplementedf("ListNetworks")
   216  }