github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/provider/cloudsigma/provider.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Juju provider for CloudSigma
     5  
     6  package cloudsigma
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/utils"
    14  
    15  	"github.com/juju/juju/cloud"
    16  	"github.com/juju/juju/environs"
    17  	"github.com/juju/juju/environs/config"
    18  	"github.com/juju/juju/environs/simplestreams"
    19  	"github.com/juju/juju/storage/provider/registry"
    20  )
    21  
    22  var logger = loggo.GetLogger("juju.provider.cloudsigma")
    23  
    24  const (
    25  	providerType = "cloudsigma"
    26  )
    27  
    28  func getImageSource(env environs.Environ) (simplestreams.DataSource, error) {
    29  	e, ok := env.(*environ)
    30  	if !ok {
    31  		return nil, errors.NotSupportedf("non-cloudsigma model")
    32  	}
    33  	return simplestreams.NewURLDataSource("cloud images", fmt.Sprintf(CloudsigmaCloudImagesURLTemplate, e.ecfg.region()), utils.VerifySSLHostnames, simplestreams.SPECIFIC_CLOUD_DATA, false), nil
    34  }
    35  
    36  type environProvider struct {
    37  	environProviderCredentials
    38  }
    39  
    40  var providerInstance = environProvider{}
    41  
    42  // check the provider implements environs.EnvironProvider interface
    43  var _ environs.EnvironProvider = (*environProvider)(nil)
    44  
    45  func init() {
    46  	// This will only happen in binaries that actually import this provider
    47  	// somewhere. To enable a provider, import it in the "providers/all"
    48  	// package; please do *not* import individual providers anywhere else,
    49  	// except in direct tests for that provider.
    50  	environs.RegisterProvider("cloudsigma", providerInstance)
    51  	environs.RegisterImageDataSourceFunc("cloud sigma image source", getImageSource)
    52  	registry.RegisterEnvironStorageProviders(providerType)
    53  }
    54  
    55  // Open opens the environment and returns it.
    56  // The configuration must have come from a previously
    57  // prepared environment.
    58  func (environProvider) Open(cfg *config.Config) (environs.Environ, error) {
    59  	logger.Infof("opening model %q", cfg.Name())
    60  
    61  	env := &environ{name: cfg.Name()}
    62  	if err := env.SetConfig(cfg); err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	return env, nil
    67  }
    68  
    69  // RestrictedConfigAttributes are provider specific attributes stored in
    70  // the config that really cannot or should not be changed across
    71  // environments running inside a single juju server.
    72  func (environProvider) RestrictedConfigAttributes() []string {
    73  	return []string{"region"}
    74  }
    75  
    76  // PrepareForCreateEnvironment prepares an environment for creation. Any
    77  // additional configuration attributes are added to the config passed in
    78  // and returned.  This allows providers to add additional required config
    79  // for new environments that may be created in an existing juju server.
    80  func (environProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) {
    81  	// Not even sure if this will ever make sense.
    82  	return nil, errors.NotImplementedf("PrepareForCreateEnvironment")
    83  }
    84  
    85  // BootstrapConfig is defined by EnvironProvider.
    86  func (environProvider) BootstrapConfig(args environs.BootstrapConfigParams) (*config.Config, error) {
    87  	cfg := args.Config
    88  	switch authType := args.Credentials.AuthType(); authType {
    89  	case cloud.UserPassAuthType:
    90  		var err error
    91  		credentialAttributes := args.Credentials.Attributes()
    92  		cfg, err = cfg.Apply(map[string]interface{}{
    93  			"username": credentialAttributes["username"],
    94  			"password": credentialAttributes["password"],
    95  			"region":   args.CloudRegion,
    96  			"endpoint": args.CloudEndpoint,
    97  		})
    98  		if err != nil {
    99  			return nil, errors.Trace(err)
   100  		}
   101  	default:
   102  		return nil, errors.NotSupportedf("%q auth-type", authType)
   103  	}
   104  	return cfg, nil
   105  }
   106  
   107  // PrepareForBootstrap is defined by EnvironProvider.
   108  func (environProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) {
   109  	logger.Infof("preparing model %q", cfg.Name())
   110  	return providerInstance.Open(cfg)
   111  }
   112  
   113  // Validate ensures that config is a valid configuration for this
   114  // provider, applying changes to it if necessary, and returns the
   115  // validated configuration.
   116  // If old is not nil, it holds the previous environment configuration
   117  // for consideration when validating changes.
   118  func (environProvider) Validate(cfg, old *config.Config) (*config.Config, error) {
   119  	logger.Infof("validating model %q", cfg.Name())
   120  
   121  	// You should almost certainly not change this method; if you need to change
   122  	// how configs are validated, you should edit validateConfig itself, to ensure
   123  	// that your checks are always applied.
   124  	newEcfg, err := validateConfig(cfg, nil)
   125  	if err != nil {
   126  		return nil, errors.Errorf("invalid config: %v", err)
   127  	}
   128  	if old != nil {
   129  		oldEcfg, err := validateConfig(old, nil)
   130  		if err != nil {
   131  			return nil, errors.Errorf("invalid base config: %v", err)
   132  		}
   133  		if newEcfg, err = validateConfig(cfg, oldEcfg); err != nil {
   134  			return nil, errors.Errorf("invalid config change: %v", err)
   135  		}
   136  	}
   137  
   138  	return newEcfg.Config, nil
   139  }
   140  
   141  // SecretAttrs filters the supplied configuration returning only values
   142  // which are considered sensitive. All of the values of these secret
   143  // attributes need to be strings.
   144  func (environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
   145  	logger.Infof("filtering secret attributes for model %q", cfg.Name())
   146  
   147  	// If you keep configSecretFields up to date, this method should Just Work.
   148  	ecfg, err := validateConfig(cfg, nil)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	secretAttrs := map[string]string{}
   153  	for _, field := range configSecretFields {
   154  		if value, ok := ecfg.attrs[field]; ok {
   155  			if stringValue, ok := value.(string); ok {
   156  				secretAttrs[field] = stringValue
   157  			} else {
   158  				// All your secret attributes must be strings at the moment. Sorry.
   159  				// It's an expedient and hopefully temporary measure that helps us
   160  				// plug a security hole in the API.
   161  				return nil, errors.Errorf(
   162  					"secret %q field must have a string value; got %v",
   163  					field, value,
   164  				)
   165  			}
   166  		}
   167  	}
   168  
   169  	return secretAttrs, nil
   170  }