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  }