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 }