github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/gce/environ.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package gce 5 6 import ( 7 "strings" 8 "sync" 9 10 "github.com/juju/errors" 11 12 jujucloud "github.com/juju/juju/cloud" 13 "github.com/juju/juju/environs" 14 "github.com/juju/juju/environs/config" 15 "github.com/juju/juju/environs/simplestreams" 16 "github.com/juju/juju/instance" 17 "github.com/juju/juju/network" 18 "github.com/juju/juju/provider/common" 19 "github.com/juju/juju/provider/gce/google" 20 ) 21 22 type gceConnection interface { 23 VerifyCredentials() error 24 25 // Instance gets the up-to-date info about the given instance 26 // and returns it. 27 Instance(id, zone string) (google.Instance, error) 28 Instances(prefix string, statuses ...string) ([]google.Instance, error) 29 AddInstance(spec google.InstanceSpec, zones ...string) (*google.Instance, error) 30 RemoveInstances(prefix string, ids ...string) error 31 32 Ports(fwname string) ([]network.PortRange, error) 33 OpenPorts(fwname string, ports ...network.PortRange) error 34 ClosePorts(fwname string, ports ...network.PortRange) error 35 36 AvailabilityZones(region string) ([]google.AvailabilityZone, error) 37 38 // Storage related methods. 39 40 // CreateDisks will attempt to create the disks described by <disks> spec and 41 // return a slice of Disk representing the created disks or error if one of them failed. 42 CreateDisks(zone string, disks []google.DiskSpec) ([]*google.Disk, error) 43 // Disks will return a list of Disk found the passed <zone>. 44 Disks(zone string) ([]*google.Disk, error) 45 // Disk will return a Disk representing the disk identified by the 46 // passed <name> or error. 47 Disk(zone, id string) (*google.Disk, error) 48 // RemoveDisk will destroy the disk identified by <name> in <zone>. 49 RemoveDisk(zone, id string) error 50 // AttachDisk will attach the volume identified by <volumeName> into the instance 51 // <instanceId> and return an AttachedDisk representing it or error. 52 AttachDisk(zone, volumeName, instanceId string, mode google.DiskMode) (*google.AttachedDisk, error) 53 // DetachDisk will detach <volumeName> disk from <instanceId> if possible 54 // and return error. 55 DetachDisk(zone, instanceId, volumeName string) error 56 // InstanceDisks returns a list of the disks attached to the passed instance. 57 InstanceDisks(zone, instanceId string) ([]*google.AttachedDisk, error) 58 } 59 60 type environ struct { 61 name string 62 uuid string 63 cloud environs.CloudSpec 64 gce gceConnection 65 66 lock sync.Mutex // lock protects access to ecfg 67 ecfg *environConfig 68 69 // namespace is used to create the machine and device hostnames. 70 namespace instance.Namespace 71 } 72 73 // Function entry points defined as variables so they can be overridden 74 // for testing purposes. 75 var ( 76 newConnection = func(conn google.ConnectionConfig, creds *google.Credentials) (gceConnection, error) { 77 return google.Connect(conn, creds) 78 } 79 destroyEnv = common.Destroy 80 bootstrap = common.Bootstrap 81 ) 82 83 func newEnviron(cloud environs.CloudSpec, cfg *config.Config) (*environ, error) { 84 ecfg, err := newConfig(cfg, nil) 85 if err != nil { 86 return nil, errors.Annotate(err, "invalid config") 87 } 88 89 credAttrs := cloud.Credential.Attributes() 90 if cloud.Credential.AuthType() == jujucloud.JSONFileAuthType { 91 contents := credAttrs[credAttrFile] 92 credential, err := parseJSONAuthFile(strings.NewReader(contents)) 93 if err != nil { 94 return nil, errors.Trace(err) 95 } 96 credAttrs = credential.Attributes() 97 } 98 99 credential := &google.Credentials{ 100 ClientID: credAttrs[credAttrClientID], 101 ProjectID: credAttrs[credAttrProjectID], 102 ClientEmail: credAttrs[credAttrClientEmail], 103 PrivateKey: []byte(credAttrs[credAttrPrivateKey]), 104 } 105 connectionConfig := google.ConnectionConfig{ 106 Region: cloud.Region, 107 ProjectID: credential.ProjectID, 108 } 109 110 // Connect and authenticate. 111 conn, err := newConnection(connectionConfig, credential) 112 if err != nil { 113 return nil, errors.Trace(err) 114 } 115 namespace, err := instance.NewNamespace(cfg.UUID()) 116 if err != nil { 117 return nil, errors.Trace(err) 118 } 119 120 return &environ{ 121 name: ecfg.config.Name(), 122 uuid: ecfg.config.UUID(), 123 cloud: cloud, 124 ecfg: ecfg, 125 gce: conn, 126 namespace: namespace, 127 }, nil 128 } 129 130 // Name returns the name of the environment. 131 func (env *environ) Name() string { 132 return env.name 133 } 134 135 // Provider returns the environment provider that created this env. 136 func (*environ) Provider() environs.EnvironProvider { 137 return providerInstance 138 } 139 140 // Region returns the CloudSpec to use for the provider, as configured. 141 func (env *environ) Region() (simplestreams.CloudSpec, error) { 142 return simplestreams.CloudSpec{ 143 Region: env.cloud.Region, 144 Endpoint: env.cloud.Endpoint, 145 }, nil 146 } 147 148 // SetConfig updates the env's configuration. 149 func (env *environ) SetConfig(cfg *config.Config) error { 150 env.lock.Lock() 151 defer env.lock.Unlock() 152 153 ecfg, err := newConfig(cfg, env.ecfg.config) 154 if err != nil { 155 return errors.Annotate(err, "invalid config change") 156 } 157 env.ecfg = ecfg 158 return nil 159 } 160 161 // Config returns the configuration data with which the env was created. 162 func (env *environ) Config() *config.Config { 163 env.lock.Lock() 164 defer env.lock.Unlock() 165 return env.ecfg.config 166 } 167 168 // PrepareForBootstrap implements environs.Environ. 169 func (env *environ) PrepareForBootstrap(ctx environs.BootstrapContext) error { 170 if ctx.ShouldVerifyCredentials() { 171 if err := env.gce.VerifyCredentials(); err != nil { 172 return errors.Trace(err) 173 } 174 } 175 return nil 176 } 177 178 // Create implements environs.Environ. 179 func (env *environ) Create(environs.CreateParams) error { 180 if err := env.gce.VerifyCredentials(); err != nil { 181 return errors.Trace(err) 182 } 183 return nil 184 } 185 186 // Bootstrap creates a new instance, chosing the series and arch out of 187 // available tools. The series and arch are returned along with a func 188 // that must be called to finalize the bootstrap process by transferring 189 // the tools and installing the initial juju controller. 190 func (env *environ) Bootstrap(ctx environs.BootstrapContext, params environs.BootstrapParams) (*environs.BootstrapResult, error) { 191 // Ensure the API server port is open (globally for all instances 192 // on the network, not just for the specific node of the state 193 // server). See LP bug #1436191 for details. 194 ports := network.PortRange{ 195 FromPort: params.ControllerConfig.APIPort(), 196 ToPort: params.ControllerConfig.APIPort(), 197 Protocol: "tcp", 198 } 199 if err := env.gce.OpenPorts(env.globalFirewallName(), ports); err != nil { 200 return nil, errors.Trace(err) 201 } 202 return bootstrap(ctx, env, params) 203 } 204 205 // Destroy shuts down all known machines and destroys the rest of the 206 // known environment. 207 func (env *environ) Destroy() error { 208 ports, err := env.Ports() 209 if err != nil { 210 return errors.Trace(err) 211 } 212 213 if len(ports) > 0 { 214 if err := env.ClosePorts(ports); err != nil { 215 return errors.Trace(err) 216 } 217 } 218 219 return destroyEnv(env) 220 } 221 222 // DestroyController implements the Environ interface. 223 func (env *environ) DestroyController(controllerUUID string) error { 224 // TODO(wallyworld): destroy hosted model resources 225 return env.Destroy() 226 }