github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/provider/azure/config.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package azure
     5  
     6  import (
     7  	"encoding/pem"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"os"
    12  
    13  	"github.com/juju/schema"
    14  
    15  	"github.com/juju/juju/environs/config"
    16  )
    17  
    18  var configFields = schema.Fields{
    19  	"location":                    schema.String(),
    20  	"management-subscription-id":  schema.String(),
    21  	"management-certificate-path": schema.String(),
    22  	"management-certificate":      schema.String(),
    23  	"storage-account-name":        schema.String(),
    24  	"force-image-name":            schema.String(),
    25  	"availability-sets-enabled":   schema.Bool(),
    26  }
    27  var configDefaults = schema.Defaults{
    28  	"location":                    "",
    29  	"management-certificate":      "",
    30  	"management-certificate-path": "",
    31  	"force-image-name":            "",
    32  	// availability-sets-enabled is set to Omit (equivalent
    33  	// to false) for backwards compatibility.
    34  	"availability-sets-enabled": schema.Omit,
    35  }
    36  
    37  type azureEnvironConfig struct {
    38  	*config.Config
    39  	attrs map[string]interface{}
    40  }
    41  
    42  func (cfg *azureEnvironConfig) location() string {
    43  	return cfg.attrs["location"].(string)
    44  }
    45  
    46  func (cfg *azureEnvironConfig) managementSubscriptionId() string {
    47  	return cfg.attrs["management-subscription-id"].(string)
    48  }
    49  
    50  func (cfg *azureEnvironConfig) managementCertificate() string {
    51  	return cfg.attrs["management-certificate"].(string)
    52  }
    53  
    54  func (cfg *azureEnvironConfig) storageAccountName() string {
    55  	return cfg.attrs["storage-account-name"].(string)
    56  }
    57  
    58  func (cfg *azureEnvironConfig) forceImageName() string {
    59  	return cfg.attrs["force-image-name"].(string)
    60  }
    61  
    62  func (cfg *azureEnvironConfig) availabilitySetsEnabled() bool {
    63  	enabled, _ := cfg.attrs["availability-sets-enabled"].(bool)
    64  	return enabled
    65  }
    66  
    67  func (prov azureEnvironProvider) newConfig(cfg *config.Config) (*azureEnvironConfig, error) {
    68  	validCfg, err := prov.Validate(cfg, nil)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	result := new(azureEnvironConfig)
    73  	result.Config = validCfg
    74  	result.attrs = validCfg.UnknownAttrs()
    75  	return result, nil
    76  }
    77  
    78  // Validate ensures that config is a valid configuration for this
    79  // provider like specified in the EnvironProvider interface.
    80  func (prov azureEnvironProvider) Validate(cfg, oldCfg *config.Config) (*config.Config, error) {
    81  	// Validate base configuration change before validating Azure specifics.
    82  	err := config.Validate(cfg, oldCfg)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	// User cannot change availability-sets-enabled after environment is prepared.
    88  	if oldCfg != nil {
    89  		if oldCfg.AllAttrs()["availability-sets-enabled"] != cfg.AllAttrs()["availability-sets-enabled"] {
    90  			return nil, fmt.Errorf("cannot change availability-sets-enabled")
    91  		}
    92  	}
    93  
    94  	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	envCfg := new(azureEnvironConfig)
    99  	envCfg.Config = cfg
   100  	envCfg.attrs = validated
   101  
   102  	if _, ok := cfg.StorageDefaultBlockSource(); !ok {
   103  		// Default volume source not specified; set
   104  		// it to the azure storage provider.
   105  		envCfg.attrs[config.StorageDefaultBlockSourceKey] = storageProviderType
   106  	}
   107  
   108  	cert := envCfg.managementCertificate()
   109  	if cert == "" {
   110  		certPath := envCfg.attrs["management-certificate-path"].(string)
   111  		pemData, err := readPEMFile(certPath)
   112  		if err != nil {
   113  			return nil, fmt.Errorf("invalid management-certificate-path: %s", err)
   114  		}
   115  		envCfg.attrs["management-certificate"] = string(pemData)
   116  	} else {
   117  		if block, _ := pem.Decode([]byte(cert)); block == nil {
   118  			return nil, fmt.Errorf("invalid management-certificate: not a PEM encoded certificate")
   119  		}
   120  	}
   121  	delete(envCfg.attrs, "management-certificate-path")
   122  
   123  	if envCfg.location() == "" {
   124  		return nil, fmt.Errorf("environment has no location; you need to set one.  E.g. 'West US'")
   125  	}
   126  	return cfg.Apply(envCfg.attrs)
   127  }
   128  
   129  func readPEMFile(path string) ([]byte, error) {
   130  	f, err := os.Open(path)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	defer f.Close()
   135  
   136  	// 640K ought to be enough for anybody.
   137  	data, err := ioutil.ReadAll(io.LimitReader(f, 1024*640))
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	block, _ := pem.Decode(data)
   142  	if block == nil {
   143  		return nil, fmt.Errorf("%q is not a PEM encoded certificate file", path)
   144  	}
   145  	return data, nil
   146  }
   147  
   148  var boilerplateYAML = `
   149  # https://juju.ubuntu.com/docs/config-azure.html
   150  azure:
   151      type: azure
   152  
   153      # location specifies the place where instances will be started,
   154      # for example: West US, North Europe.
   155      #
   156      location: West US
   157  
   158      # The following attributes specify Windows Azure Management
   159      # information. See:
   160      # http://msdn.microsoft.com/en-us/library/windowsazure
   161      # for details.
   162      #
   163      management-subscription-id: 00000000-0000-0000-0000-000000000000
   164      management-certificate-path: /home/me/azure.pem
   165  
   166      # storage-account-name holds Windows Azure Storage info.
   167      #
   168      storage-account-name: abcdefghijkl
   169  
   170      # force-image-name overrides the OS image selection to use a fixed
   171      # image for all deployments. Most useful for developers.
   172      #
   173      # force-image-name: b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-13_10-amd64-server-DEVELOPMENT-20130713-Juju_ALPHA-en-us-30GB
   174  
   175      # image-stream chooses a simplestreams stream from which to select
   176      # OS images, for example daily or released images (or any other stream
   177      # available on simplestreams).
   178      #
   179      # image-stream: "released"
   180  
   181      # agent-stream chooses a simplestreams stream from which to select tools,
   182      # for example released or proposed tools (or any other stream available
   183      # on simplestreams).
   184      #
   185      # agent-stream: "released"
   186  
   187      # Whether or not to refresh the list of available updates for an
   188      # OS. The default option of true is recommended for use in
   189      # production systems, but disabling this can speed up local
   190      # deployments for development or testing.
   191      #
   192      # enable-os-refresh-update: true
   193  
   194      # Whether or not to perform OS upgrades when machines are
   195      # provisioned. The default option of true is recommended for use
   196      # in production systems, but disabling this can speed up local
   197      # deployments for development or testing.
   198      #
   199      # enable-os-upgrade: true
   200  
   201  `[1:]
   202  
   203  func (prov azureEnvironProvider) BoilerplateConfig() string {
   204  	return boilerplateYAML
   205  }
   206  
   207  // SecretAttrs is specified in the EnvironProvider interface.
   208  func (prov azureEnvironProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
   209  	secretAttrs := make(map[string]string)
   210  	azureCfg, err := prov.newConfig(cfg)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	secretAttrs["management-certificate"] = azureCfg.managementCertificate()
   215  	return secretAttrs, nil
   216  }