github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/cloud.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/utils/set"
     9  	"gopkg.in/juju/names.v2"
    10  	"gopkg.in/mgo.v2"
    11  	"gopkg.in/mgo.v2/txn"
    12  
    13  	"github.com/juju/juju/cloud"
    14  )
    15  
    16  // cloudGlobalKey returns the global database key for the specified cloud.
    17  func cloudGlobalKey(name string) string {
    18  	return "cloud#" + name
    19  }
    20  
    21  // cloudDoc records information about the cloud that the controller operates in.
    22  type cloudDoc struct {
    23  	DocID            string                       `bson:"_id"`
    24  	Name             string                       `bson:"name"`
    25  	Type             string                       `bson:"type"`
    26  	AuthTypes        []string                     `bson:"auth-types"`
    27  	Endpoint         string                       `bson:"endpoint"`
    28  	IdentityEndpoint string                       `bson:"identity-endpoint,omitempty"`
    29  	StorageEndpoint  string                       `bson:"storage-endpoint,omitempty"`
    30  	Regions          map[string]cloudRegionSubdoc `bson:"regions,omitempty"`
    31  }
    32  
    33  // cloudRegionSubdoc records information about cloud regions.
    34  type cloudRegionSubdoc struct {
    35  	Endpoint         string `bson:"endpoint,omitempty"`
    36  	IdentityEndpoint string `bson:"identity-endpoint,omitempty"`
    37  	StorageEndpoint  string `bson:"storage-endpoint,omitempty"`
    38  }
    39  
    40  // createCloudOp returns a list of txn.Ops that will initialize
    41  // the cloud definition for the controller.
    42  func createCloudOp(cloud cloud.Cloud, cloudName string) txn.Op {
    43  	authTypes := make([]string, len(cloud.AuthTypes))
    44  	for i, authType := range cloud.AuthTypes {
    45  		authTypes[i] = string(authType)
    46  	}
    47  	regions := make(map[string]cloudRegionSubdoc)
    48  	for _, region := range cloud.Regions {
    49  		regions[region.Name] = cloudRegionSubdoc{
    50  			region.Endpoint,
    51  			region.IdentityEndpoint,
    52  			region.StorageEndpoint,
    53  		}
    54  	}
    55  	return txn.Op{
    56  		C:      cloudsC,
    57  		Id:     cloudName,
    58  		Assert: txn.DocMissing,
    59  		Insert: &cloudDoc{
    60  			Name:             cloudName,
    61  			Type:             cloud.Type,
    62  			AuthTypes:        authTypes,
    63  			Endpoint:         cloud.Endpoint,
    64  			IdentityEndpoint: cloud.IdentityEndpoint,
    65  			StorageEndpoint:  cloud.StorageEndpoint,
    66  			Regions:          regions,
    67  		},
    68  	}
    69  }
    70  
    71  func (d cloudDoc) toCloud() cloud.Cloud {
    72  	authTypes := make([]cloud.AuthType, len(d.AuthTypes))
    73  	for i, authType := range d.AuthTypes {
    74  		authTypes[i] = cloud.AuthType(authType)
    75  	}
    76  	regionNames := make(set.Strings)
    77  	for name := range d.Regions {
    78  		regionNames.Add(name)
    79  	}
    80  	regions := make([]cloud.Region, len(d.Regions))
    81  	for i, name := range regionNames.SortedValues() {
    82  		region := d.Regions[name]
    83  		regions[i] = cloud.Region{
    84  			name,
    85  			region.Endpoint,
    86  			region.IdentityEndpoint,
    87  			region.StorageEndpoint,
    88  		}
    89  	}
    90  	return cloud.Cloud{
    91  		Type:             d.Type,
    92  		AuthTypes:        authTypes,
    93  		Endpoint:         d.Endpoint,
    94  		IdentityEndpoint: d.IdentityEndpoint,
    95  		StorageEndpoint:  d.StorageEndpoint,
    96  		Regions:          regions,
    97  	}
    98  }
    99  
   100  // Clouds returns the definitions for all clouds in the controller.
   101  func (st *State) Clouds() (map[names.CloudTag]cloud.Cloud, error) {
   102  	coll, cleanup := st.getCollection(cloudsC)
   103  	defer cleanup()
   104  
   105  	var doc cloudDoc
   106  	clouds := make(map[names.CloudTag]cloud.Cloud)
   107  	iter := coll.Find(nil).Iter()
   108  	for iter.Next(&doc) {
   109  		clouds[names.NewCloudTag(doc.Name)] = doc.toCloud()
   110  	}
   111  	if err := iter.Err(); err != nil {
   112  		return nil, errors.Annotate(err, "getting clouds")
   113  	}
   114  	return clouds, nil
   115  }
   116  
   117  // Cloud returns the controller's cloud definition.
   118  func (st *State) Cloud(name string) (cloud.Cloud, error) {
   119  	coll, cleanup := st.getCollection(cloudsC)
   120  	defer cleanup()
   121  
   122  	var doc cloudDoc
   123  	err := coll.FindId(name).One(&doc)
   124  	if err == mgo.ErrNotFound {
   125  		return cloud.Cloud{}, errors.NotFoundf("cloud %q", name)
   126  	}
   127  	if err != nil {
   128  		return cloud.Cloud{}, errors.Annotatef(err, "cannot get cloud %q", name)
   129  	}
   130  	return doc.toCloud(), nil
   131  }
   132  
   133  // AddCloud creates a cloud with the given name and details.
   134  // Note that the Config is deliberately ignored - it's only
   135  // relevant when bootstrapping.
   136  func (st *State) AddCloud(name string, c cloud.Cloud) error {
   137  	if err := validateCloud(c); err != nil {
   138  		return errors.Annotate(err, "invalid cloud")
   139  	}
   140  	ops := []txn.Op{createCloudOp(c, name)}
   141  	if err := st.runTransaction(ops); err != nil {
   142  		if err == txn.ErrAborted {
   143  			err = errors.AlreadyExistsf("cloud %q", name)
   144  		}
   145  		return err
   146  	}
   147  	return nil
   148  }
   149  
   150  // validateCloud checks that the supplied cloud is valid.
   151  func validateCloud(cloud cloud.Cloud) error {
   152  	if cloud.Type == "" {
   153  		return errors.NotValidf("empty Type")
   154  	}
   155  	if len(cloud.AuthTypes) == 0 {
   156  		return errors.NotValidf("empty auth-types")
   157  	}
   158  	// TODO(axw) we should ensure that the cloud auth-types is a subset
   159  	// of the auth-types supported by the provider. To do that, we'll
   160  	// need a new "policy".
   161  	return nil
   162  }
   163  
   164  // regionSettingsGlobalKey concatenates the cloud a hash and the region string.
   165  func regionSettingsGlobalKey(cloud, region string) string {
   166  	return cloud + "#" + region
   167  }