github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/joyent/environ.go (about) 1 // Copyright 2013 Joyent Inc. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package joyent 5 6 import ( 7 "fmt" 8 "strings" 9 "sync" 10 11 "github.com/joyent/gosdc/cloudapi" 12 "github.com/juju/errors" 13 14 "github.com/juju/juju/constraints" 15 "github.com/juju/juju/environs" 16 "github.com/juju/juju/environs/config" 17 "github.com/juju/juju/environs/imagemetadata" 18 "github.com/juju/juju/environs/simplestreams" 19 "github.com/juju/juju/environs/tags" 20 "github.com/juju/juju/instance" 21 "github.com/juju/juju/provider/common" 22 ) 23 24 // This file contains the core of the Joyent Environ implementation. 25 26 type joyentEnviron struct { 27 common.SupportsUnitPlacementPolicy 28 29 name string 30 31 compute *joyentCompute 32 33 // supportedArchitectures caches the architectures 34 // for which images can be instantiated. 35 archLock sync.Mutex 36 supportedArchitectures []string 37 38 lock sync.Mutex // protects ecfg 39 ecfg *environConfig 40 } 41 42 // newEnviron create a new Joyent environ instance from config. 43 func newEnviron(cfg *config.Config) (*joyentEnviron, error) { 44 env := new(joyentEnviron) 45 if err := env.SetConfig(cfg); err != nil { 46 return nil, err 47 } 48 env.name = cfg.Name() 49 var err error 50 env.compute, err = newCompute(env.ecfg) 51 if err != nil { 52 return nil, err 53 } 54 return env, nil 55 } 56 57 func (env *joyentEnviron) SetName(envName string) { 58 env.name = envName 59 } 60 61 func (*joyentEnviron) Provider() environs.EnvironProvider { 62 return providerInstance 63 } 64 65 // PrecheckInstance is defined on the state.Prechecker interface. 66 func (env *joyentEnviron) PrecheckInstance(series string, cons constraints.Value, placement string) error { 67 if placement != "" { 68 return fmt.Errorf("unknown placement directive: %s", placement) 69 } 70 if !cons.HasInstanceType() { 71 return nil 72 } 73 // Constraint has an instance-type constraint so let's see if it is valid. 74 instanceTypes, err := env.listInstanceTypes() 75 if err != nil { 76 return err 77 } 78 for _, instanceType := range instanceTypes { 79 if instanceType.Name == *cons.InstanceType { 80 return nil 81 } 82 } 83 return fmt.Errorf("invalid Joyent instance %q specified", *cons.InstanceType) 84 } 85 86 // SupportedArchitectures is specified on the EnvironCapability interface. 87 func (env *joyentEnviron) SupportedArchitectures() ([]string, error) { 88 env.archLock.Lock() 89 defer env.archLock.Unlock() 90 if env.supportedArchitectures != nil { 91 return env.supportedArchitectures, nil 92 } 93 cfg := env.Ecfg() 94 // Create a filter to get all images from our region and for the correct stream. 95 cloudSpec := simplestreams.CloudSpec{ 96 Region: cfg.Region(), 97 Endpoint: cfg.SdcUrl(), 98 } 99 imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ 100 CloudSpec: cloudSpec, 101 Stream: cfg.ImageStream(), 102 }) 103 var err error 104 env.supportedArchitectures, err = common.SupportedArchitectures(env, imageConstraint) 105 return env.supportedArchitectures, err 106 } 107 108 func (env *joyentEnviron) SetConfig(cfg *config.Config) error { 109 env.lock.Lock() 110 defer env.lock.Unlock() 111 ecfg, err := providerInstance.newConfig(cfg) 112 if err != nil { 113 return err 114 } 115 env.ecfg = ecfg 116 return nil 117 } 118 119 func (env *joyentEnviron) Config() *config.Config { 120 return env.Ecfg().Config 121 } 122 123 func (env *joyentEnviron) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) (*environs.BootstrapResult, error) { 124 return common.Bootstrap(ctx, env, args) 125 } 126 127 func (env *joyentEnviron) ControllerInstances() ([]instance.Id, error) { 128 instanceIds := []instance.Id{} 129 130 filter := cloudapi.NewFilter() 131 filter.Set(tagKey("group"), "juju") 132 filter.Set(tagKey("model"), env.Config().Name()) 133 filter.Set(tagKey(tags.JujuModel), env.Config().UUID()) 134 filter.Set(tagKey(tags.JujuIsController), "true") 135 136 machines, err := env.compute.cloudapi.ListMachines(filter) 137 if err != nil || len(machines) == 0 { 138 return nil, environs.ErrNotBootstrapped 139 } 140 141 for _, m := range machines { 142 if strings.EqualFold(m.State, "provisioning") || strings.EqualFold(m.State, "running") { 143 copy := m 144 ji := &joyentInstance{machine: ©, env: env} 145 instanceIds = append(instanceIds, ji.Id()) 146 } 147 } 148 149 return instanceIds, nil 150 } 151 152 func (env *joyentEnviron) Destroy() error { 153 return errors.Trace(common.Destroy(env)) 154 } 155 156 func (env *joyentEnviron) Ecfg() *environConfig { 157 env.lock.Lock() 158 defer env.lock.Unlock() 159 return env.ecfg 160 } 161 162 // MetadataLookupParams returns parameters which are used to query simplestreams metadata. 163 func (env *joyentEnviron) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { 164 if region == "" { 165 region = env.Ecfg().Region() 166 } 167 return &simplestreams.MetadataLookupParams{ 168 Series: config.PreferredSeries(env.Ecfg()), 169 Region: region, 170 Endpoint: env.Ecfg().sdcUrl(), 171 Architectures: []string{"amd64", "armhf"}, 172 }, nil 173 } 174 175 // Region is specified in the HasRegion interface. 176 func (env *joyentEnviron) Region() (simplestreams.CloudSpec, error) { 177 return simplestreams.CloudSpec{ 178 Region: env.Ecfg().Region(), 179 Endpoint: env.Ecfg().sdcUrl(), 180 }, nil 181 }