github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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  
    50  const (
    51  	SdcAccount          = "SDC_ACCOUNT"
    52  	SdcKeyId            = "SDC_KEY_ID"
    53  	SdcUrl              = "SDC_URL"
    54  	MantaUser           = "MANTA_USER"
    55  	MantaKeyId          = "MANTA_KEY_ID"
    56  	MantaUrl            = "MANTA_URL"
    57  	MantaPrivateKeyFile = "MANTA_PRIVATE_KEY_FILE"
    58  	DefaultPrivateKey   = "~/.ssh/id_rsa"
    59  )
    60  
    61  var environmentVariables = map[string]string{
    62  	"sdc-user":         SdcAccount,
    63  	"sdc-key-id":       SdcKeyId,
    64  	"sdc-url":          SdcUrl,
    65  	"manta-user":       MantaUser,
    66  	"manta-key-id":     MantaKeyId,
    67  	"manta-url":        MantaUrl,
    68  	"private-key-path": MantaPrivateKeyFile,
    69  }
    70  
    71  var configFields = schema.Fields{
    72  	"sdc-user":         schema.String(),
    73  	"sdc-key-id":       schema.String(),
    74  	"sdc-url":          schema.String(),
    75  	"manta-user":       schema.String(),
    76  	"manta-key-id":     schema.String(),
    77  	"manta-url":        schema.String(),
    78  	"private-key-path": schema.String(),
    79  	"algorithm":        schema.String(),
    80  	"control-dir":      schema.String(),
    81  	"private-key":      schema.String(),
    82  }
    83  
    84  var configDefaults = schema.Defaults{
    85  	"sdc-url":          "https://us-west-1.api.joyentcloud.com",
    86  	"manta-url":        "https://us-east.manta.joyent.com",
    87  	"algorithm":        "rsa-sha256",
    88  	"private-key-path": schema.Omit,
    89  	"sdc-user":         schema.Omit,
    90  	"sdc-key-id":       schema.Omit,
    91  	"manta-user":       schema.Omit,
    92  	"manta-key-id":     schema.Omit,
    93  	"private-key":      schema.Omit,
    94  }
    95  
    96  var configSecretFields = []string{
    97  	"sdc-user",
    98  	"sdc-key-id",
    99  	"manta-user",
   100  	"manta-key-id",
   101  	"private-key",
   102  }
   103  
   104  var configImmutableFields = []string{
   105  	"sdc-url",
   106  	"manta-url",
   107  	"private-key-path",
   108  	"private-key",
   109  	"algorithm",
   110  }
   111  
   112  func prepareConfig(cfg *config.Config) (*config.Config, error) {
   113  	// Turn an incomplete config into a valid one, if possible.
   114  	attrs := cfg.UnknownAttrs()
   115  
   116  	if _, ok := attrs["control-dir"]; !ok {
   117  		uuid, err := utils.NewUUID()
   118  		if err != nil {
   119  			return nil, err
   120  		}
   121  		attrs["control-dir"] = fmt.Sprintf("%x", uuid.Raw())
   122  	}
   123  	return cfg.Apply(attrs)
   124  }
   125  
   126  func validateConfig(cfg, old *config.Config) (*environConfig, error) {
   127  	// Check for valid changes for the base config values.
   128  	if err := config.Validate(cfg, old); err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	envConfig := &environConfig{cfg, newAttrs}
   137  	// If an old config was supplied, check any immutable fields have not changed.
   138  	if old != nil {
   139  		oldEnvConfig, err := validateConfig(old, nil)
   140  		if err != nil {
   141  			return nil, err
   142  		}
   143  		for _, field := range configImmutableFields {
   144  			if oldEnvConfig.attrs[field] != envConfig.attrs[field] {
   145  				return nil, fmt.Errorf(
   146  					"%s: cannot change from %v to %v",
   147  					field, oldEnvConfig.attrs[field], envConfig.attrs[field],
   148  				)
   149  			}
   150  		}
   151  	}
   152  
   153  	// Read env variables to fill in any missing fields.
   154  	for field, envVar := range environmentVariables {
   155  		// If field is not set, get it from env variables
   156  		if fieldValue, ok := envConfig.attrs[field]; !ok || fieldValue == "" {
   157  			localEnvVariable := os.Getenv(envVar)
   158  			if localEnvVariable != "" {
   159  				envConfig.attrs[field] = localEnvVariable
   160  			} else {
   161  				if field != "private-key-path" {
   162  					return nil, fmt.Errorf("cannot get %s value from environment variable %s", field, envVar)
   163  				}
   164  			}
   165  		}
   166  	}
   167  
   168  	// Ensure private-key-path is set - if it's not in config or an env var, use a default value.
   169  	if v, ok := envConfig.attrs["private-key-path"]; !ok || v == "" {
   170  		v = os.Getenv(environmentVariables["private-key-path"])
   171  		if v == "" {
   172  			v = DefaultPrivateKey
   173  		}
   174  		envConfig.attrs["private-key-path"] = v
   175  	}
   176  	// Now that we've ensured private-key-path is properly set, we go back and set
   177  	// up the private key - this is used to sign requests.
   178  	if fieldValue, ok := envConfig.attrs["private-key"]; !ok || fieldValue == "" {
   179  		keyFile, err := utils.NormalizePath(envConfig.attrs["private-key-path"].(string))
   180  		if err != nil {
   181  			return nil, err
   182  		}
   183  		privateKey, err := ioutil.ReadFile(keyFile)
   184  		if err != nil {
   185  			return nil, err
   186  		}
   187  		envConfig.attrs["private-key"] = string(privateKey)
   188  	}
   189  
   190  	// Check for missing fields.
   191  	for field := range configFields {
   192  		if envConfig.attrs[field] == "" {
   193  			return nil, fmt.Errorf("%s: must not be empty", field)
   194  		}
   195  	}
   196  	return envConfig, nil
   197  }
   198  
   199  type environConfig struct {
   200  	*config.Config
   201  	attrs map[string]interface{}
   202  }
   203  
   204  func (ecfg *environConfig) GetAttrs() map[string]interface{} {
   205  	return ecfg.attrs
   206  }
   207  
   208  func (ecfg *environConfig) sdcUrl() string {
   209  	return ecfg.attrs["sdc-url"].(string)
   210  }
   211  
   212  func (ecfg *environConfig) sdcUser() string {
   213  	return ecfg.attrs["sdc-user"].(string)
   214  }
   215  
   216  func (ecfg *environConfig) sdcKeyId() string {
   217  	return ecfg.attrs["sdc-key-id"].(string)
   218  }
   219  
   220  func (ecfg *environConfig) mantaUrl() string {
   221  	return ecfg.attrs["manta-url"].(string)
   222  }
   223  
   224  func (ecfg *environConfig) mantaUser() string {
   225  	return ecfg.attrs["manta-user"].(string)
   226  }
   227  
   228  func (ecfg *environConfig) mantaKeyId() string {
   229  	return ecfg.attrs["manta-key-id"].(string)
   230  }
   231  
   232  func (ecfg *environConfig) privateKey() string {
   233  	if v, ok := ecfg.attrs["private-key"]; ok {
   234  		return v.(string)
   235  	}
   236  	return ""
   237  }
   238  
   239  func (ecfg *environConfig) algorithm() string {
   240  	return ecfg.attrs["algorithm"].(string)
   241  }
   242  
   243  func (c *environConfig) controlDir() string {
   244  	return c.attrs["control-dir"].(string)
   245  }
   246  
   247  func (c *environConfig) ControlDir() string {
   248  	return c.controlDir()
   249  }
   250  
   251  func (ecfg *environConfig) SdcUrl() string {
   252  	return ecfg.sdcUrl()
   253  }
   254  
   255  func (ecfg *environConfig) Region() string {
   256  	sdcUrl := ecfg.sdcUrl()
   257  	// Check if running against local services
   258  	if isLocalhost(sdcUrl) {
   259  		return "some-region"
   260  	}
   261  	return sdcUrl[strings.LastIndex(sdcUrl, "/")+1 : strings.Index(sdcUrl, ".")]
   262  }
   263  
   264  func isLocalhost(u string) bool {
   265  	parsedUrl, err := url.Parse(u)
   266  	if err != nil {
   267  		return false
   268  	}
   269  	if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.") {
   270  		return true
   271  	}
   272  
   273  	return false
   274  }