github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/provider/joyent/config.go (about)

     1  // Copyright 2013 Joyent Inc.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package joyent
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net/url"
    10  	"os"
    11  	"strings"
    12  
    13  	"github.com/juju/schema"
    14  	"github.com/juju/utils"
    15  
    16  	"github.com/juju/juju/environs/config"
    17  )
    18  
    19  // boilerplateConfig will be shown in help output, so please keep it up to
    20  // date when you change environment configuration below.
    21  const boilerplateConfig = `joyent:
    22    type: joyent
    23  
    24    # SDC config
    25    # Can be set via env variables, or specified here
    26    # sdc-user: <secret>
    27    # Can be set via env variables, or specified here
    28    # sdc-key-id: <secret>
    29    # url defaults to us-west-1 DC, override if required
    30    # sdc-url: https://us-west-1.api.joyentcloud.com
    31  
    32    # Manta config
    33    # Can be set via env variables, or specified here
    34    # manta-user: <secret>
    35    # Can be set via env variables, or specified here
    36    # manta-key-id: <secret>
    37    # url defaults to us-east DC, override if required
    38    # manta-url: https://us-east.manta.joyent.com
    39  
    40    # Auth config
    41    # private-key-path is the private key used to sign Joyent requests.
    42    # Defaults to ~/.ssh/id_rsa, override if a different ssh key is used.
    43    # Alternatively, you can supply "private-key" with the content of the private
    44    # key instead supplying the path to a file.
    45    # private-key-path: ~/.ssh/id_rsa
    46    # algorithm defaults to rsa-sha256, override if required
    47    # algorithm: rsa-sha256
    48  
    49    # Whether or not to refresh the list of available updates for an
    50    # OS. The default option of true is recommended for use in
    51    # production systems, but disabling this can speed up local
    52    # deployments for development or testing.
    53    #
    54    # enable-os-refresh-update: true
    55  
    56    # Whether or not to perform OS upgrades when machines are
    57    # provisioned. The default option of true is recommended for use
    58    # in production systems, but disabling this can speed up local
    59    # deployments for development or testing.
    60    #
    61    # enable-os-upgrade: true
    62  
    63  `
    64  
    65  const (
    66  	SdcAccount          = "SDC_ACCOUNT"
    67  	SdcKeyId            = "SDC_KEY_ID"
    68  	SdcUrl              = "SDC_URL"
    69  	MantaUser           = "MANTA_USER"
    70  	MantaKeyId          = "MANTA_KEY_ID"
    71  	MantaUrl            = "MANTA_URL"
    72  	MantaPrivateKeyFile = "MANTA_PRIVATE_KEY_FILE"
    73  	DefaultPrivateKey   = "~/.ssh/id_rsa"
    74  )
    75  
    76  var environmentVariables = map[string]string{
    77  	"sdc-user":         SdcAccount,
    78  	"sdc-key-id":       SdcKeyId,
    79  	"sdc-url":          SdcUrl,
    80  	"manta-user":       MantaUser,
    81  	"manta-key-id":     MantaKeyId,
    82  	"manta-url":        MantaUrl,
    83  	"private-key-path": MantaPrivateKeyFile,
    84  }
    85  
    86  var configFields = schema.Fields{
    87  	"sdc-user":         schema.String(),
    88  	"sdc-key-id":       schema.String(),
    89  	"sdc-url":          schema.String(),
    90  	"manta-user":       schema.String(),
    91  	"manta-key-id":     schema.String(),
    92  	"manta-url":        schema.String(),
    93  	"private-key-path": schema.String(),
    94  	"algorithm":        schema.String(),
    95  	"control-dir":      schema.String(),
    96  	"private-key":      schema.String(),
    97  }
    98  
    99  var configDefaults = schema.Defaults{
   100  	"sdc-url":          "https://us-west-1.api.joyentcloud.com",
   101  	"manta-url":        "https://us-east.manta.joyent.com",
   102  	"algorithm":        "rsa-sha256",
   103  	"private-key-path": schema.Omit,
   104  	"sdc-user":         schema.Omit,
   105  	"sdc-key-id":       schema.Omit,
   106  	"manta-user":       schema.Omit,
   107  	"manta-key-id":     schema.Omit,
   108  	"private-key":      schema.Omit,
   109  }
   110  
   111  var configSecretFields = []string{
   112  	"sdc-user",
   113  	"sdc-key-id",
   114  	"manta-user",
   115  	"manta-key-id",
   116  	"private-key",
   117  }
   118  
   119  var configImmutableFields = []string{
   120  	"sdc-url",
   121  	"manta-url",
   122  	"private-key-path",
   123  	"private-key",
   124  	"algorithm",
   125  }
   126  
   127  func prepareConfig(cfg *config.Config) (*config.Config, error) {
   128  	// Turn an incomplete config into a valid one, if possible.
   129  	attrs := cfg.UnknownAttrs()
   130  
   131  	if _, ok := attrs["control-dir"]; !ok {
   132  		uuid, err := utils.NewUUID()
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  		attrs["control-dir"] = fmt.Sprintf("%x", uuid.Raw())
   137  	}
   138  	return cfg.Apply(attrs)
   139  }
   140  
   141  func validateConfig(cfg, old *config.Config) (*environConfig, error) {
   142  	// Check for valid changes for the base config values.
   143  	if err := config.Validate(cfg, old); err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	envConfig := &environConfig{cfg, newAttrs}
   152  	// If an old config was supplied, check any immutable fields have not changed.
   153  	if old != nil {
   154  		oldEnvConfig, err := validateConfig(old, nil)
   155  		if err != nil {
   156  			return nil, err
   157  		}
   158  		for _, field := range configImmutableFields {
   159  			if oldEnvConfig.attrs[field] != envConfig.attrs[field] {
   160  				return nil, fmt.Errorf(
   161  					"%s: cannot change from %v to %v",
   162  					field, oldEnvConfig.attrs[field], envConfig.attrs[field],
   163  				)
   164  			}
   165  		}
   166  	}
   167  
   168  	// Read env variables to fill in any missing fields.
   169  	for field, envVar := range environmentVariables {
   170  		// If field is not set, get it from env variables
   171  		if fieldValue, ok := envConfig.attrs[field]; !ok || fieldValue == "" {
   172  			localEnvVariable := os.Getenv(envVar)
   173  			if localEnvVariable != "" {
   174  				envConfig.attrs[field] = localEnvVariable
   175  			} else {
   176  				if field != "private-key-path" {
   177  					return nil, fmt.Errorf("cannot get %s value from environment variable %s", field, envVar)
   178  				}
   179  			}
   180  		}
   181  	}
   182  
   183  	// Ensure private-key-path is set - if it's not in config or an env var, use a default value.
   184  	if v, ok := envConfig.attrs["private-key-path"]; !ok || v == "" {
   185  		v = os.Getenv(environmentVariables["private-key-path"])
   186  		if v == "" {
   187  			v = DefaultPrivateKey
   188  		}
   189  		envConfig.attrs["private-key-path"] = v
   190  	}
   191  	// Now that we've ensured private-key-path is properly set, we go back and set
   192  	// up the private key - this is used to sign requests.
   193  	if fieldValue, ok := envConfig.attrs["private-key"]; !ok || fieldValue == "" {
   194  		keyFile, err := utils.NormalizePath(envConfig.attrs["private-key-path"].(string))
   195  		if err != nil {
   196  			return nil, err
   197  		}
   198  		privateKey, err := ioutil.ReadFile(keyFile)
   199  		if err != nil {
   200  			return nil, err
   201  		}
   202  		envConfig.attrs["private-key"] = string(privateKey)
   203  	}
   204  
   205  	// Check for missing fields.
   206  	for field := range configFields {
   207  		if envConfig.attrs[field] == "" {
   208  			return nil, fmt.Errorf("%s: must not be empty", field)
   209  		}
   210  	}
   211  	return envConfig, nil
   212  }
   213  
   214  type environConfig struct {
   215  	*config.Config
   216  	attrs map[string]interface{}
   217  }
   218  
   219  func (ecfg *environConfig) GetAttrs() map[string]interface{} {
   220  	return ecfg.attrs
   221  }
   222  
   223  func (ecfg *environConfig) sdcUrl() string {
   224  	return ecfg.attrs["sdc-url"].(string)
   225  }
   226  
   227  func (ecfg *environConfig) sdcUser() string {
   228  	return ecfg.attrs["sdc-user"].(string)
   229  }
   230  
   231  func (ecfg *environConfig) sdcKeyId() string {
   232  	return ecfg.attrs["sdc-key-id"].(string)
   233  }
   234  
   235  func (ecfg *environConfig) mantaUrl() string {
   236  	return ecfg.attrs["manta-url"].(string)
   237  }
   238  
   239  func (ecfg *environConfig) mantaUser() string {
   240  	return ecfg.attrs["manta-user"].(string)
   241  }
   242  
   243  func (ecfg *environConfig) mantaKeyId() string {
   244  	return ecfg.attrs["manta-key-id"].(string)
   245  }
   246  
   247  func (ecfg *environConfig) privateKey() string {
   248  	if v, ok := ecfg.attrs["private-key"]; ok {
   249  		return v.(string)
   250  	}
   251  	return ""
   252  }
   253  
   254  func (ecfg *environConfig) algorithm() string {
   255  	return ecfg.attrs["algorithm"].(string)
   256  }
   257  
   258  func (c *environConfig) controlDir() string {
   259  	return c.attrs["control-dir"].(string)
   260  }
   261  
   262  func (c *environConfig) ControlDir() string {
   263  	return c.controlDir()
   264  }
   265  
   266  func (ecfg *environConfig) SdcUrl() string {
   267  	return ecfg.sdcUrl()
   268  }
   269  
   270  func (ecfg *environConfig) Region() string {
   271  	sdcUrl := ecfg.sdcUrl()
   272  	// Check if running against local services
   273  	if isLocalhost(sdcUrl) {
   274  		return "some-region"
   275  	}
   276  	return sdcUrl[strings.LastIndex(sdcUrl, "/")+1 : strings.Index(sdcUrl, ".")]
   277  }
   278  
   279  func isLocalhost(u string) bool {
   280  	parsedUrl, err := url.Parse(u)
   281  	if err != nil {
   282  		return false
   283  	}
   284  	if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.") {
   285  		return true
   286  	}
   287  
   288  	return false
   289  }