github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/provider/manual/provider.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package manual 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/juju/utils" 11 12 "github.com/juju/juju/environs" 13 "github.com/juju/juju/environs/config" 14 "github.com/juju/juju/environs/manual" 15 ) 16 17 type manualProvider struct{} 18 19 func init() { 20 p := manualProvider{} 21 environs.RegisterProvider("manual", p, "null") 22 } 23 24 var errNoBootstrapHost = errors.New("bootstrap-host must be specified") 25 26 var initUbuntuUser = manual.InitUbuntuUser 27 28 func ensureBootstrapUbuntuUser(ctx environs.BootstrapContext, cfg *environConfig) error { 29 err := initUbuntuUser(cfg.bootstrapHost(), cfg.bootstrapUser(), cfg.AuthorizedKeys(), ctx.GetStdin(), ctx.GetStdout()) 30 if err != nil { 31 logger.Errorf("initializing ubuntu user: %v", err) 32 return err 33 } 34 logger.Infof("initialized ubuntu user") 35 return nil 36 } 37 38 func (p manualProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { 39 if _, ok := cfg.UnknownAttrs()["storage-auth-key"]; !ok { 40 uuid, err := utils.NewUUID() 41 if err != nil { 42 return nil, err 43 } 44 cfg, err = cfg.Apply(map[string]interface{}{ 45 "storage-auth-key": uuid.String(), 46 }) 47 if err != nil { 48 return nil, err 49 } 50 } 51 if use, ok := cfg.UnknownAttrs()["use-sshstorage"].(bool); ok && !use { 52 return nil, fmt.Errorf("use-sshstorage must not be specified") 53 } 54 envConfig, err := p.validate(cfg, nil) 55 if err != nil { 56 return nil, err 57 } 58 if err := ensureBootstrapUbuntuUser(ctx, envConfig); err != nil { 59 return nil, err 60 } 61 return p.open(envConfig) 62 } 63 64 func (p manualProvider) Open(cfg *config.Config) (environs.Environ, error) { 65 envConfig, err := p.validate(cfg, nil) 66 if err != nil { 67 return nil, err 68 } 69 return p.open(envConfig) 70 } 71 72 func (p manualProvider) open(cfg *environConfig) (environs.Environ, error) { 73 env := &manualEnviron{cfg: cfg} 74 // Need to call SetConfig to initialise storage. 75 if err := env.SetConfig(cfg.Config); err != nil { 76 return nil, err 77 } 78 return env, nil 79 } 80 81 func checkImmutableString(cfg, old *environConfig, key string) error { 82 if old.attrs[key] != cfg.attrs[key] { 83 return fmt.Errorf("cannot change %s from %q to %q", key, old.attrs[key], cfg.attrs[key]) 84 } 85 return nil 86 } 87 88 func (p manualProvider) validate(cfg, old *config.Config) (*environConfig, error) { 89 // Check for valid changes for the base config values. 90 if err := config.Validate(cfg, old); err != nil { 91 return nil, err 92 } 93 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 94 if err != nil { 95 return nil, err 96 } 97 envConfig := newEnvironConfig(cfg, validated) 98 if envConfig.bootstrapHost() == "" { 99 return nil, errNoBootstrapHost 100 } 101 // Check various immutable attributes. 102 if old != nil { 103 oldEnvConfig, err := p.validate(old, nil) 104 if err != nil { 105 return nil, err 106 } 107 for _, key := range [...]string{ 108 "bootstrap-user", 109 "bootstrap-host", 110 "storage-listen-ip", 111 } { 112 if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil { 113 return nil, err 114 } 115 } 116 oldPort, newPort := oldEnvConfig.storagePort(), envConfig.storagePort() 117 if oldPort != newPort { 118 return nil, fmt.Errorf("cannot change storage-port from %q to %q", oldPort, newPort) 119 } 120 oldUseSSHStorage, newUseSSHStorage := oldEnvConfig.useSSHStorage(), envConfig.useSSHStorage() 121 if oldUseSSHStorage != newUseSSHStorage && newUseSSHStorage == true { 122 return nil, fmt.Errorf("cannot change use-sshstorage from %v to %v", oldUseSSHStorage, newUseSSHStorage) 123 } 124 } 125 return envConfig, nil 126 } 127 128 func (p manualProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 129 envConfig, err := p.validate(cfg, old) 130 if err != nil { 131 return nil, err 132 } 133 return cfg.Apply(envConfig.attrs) 134 } 135 136 func (_ manualProvider) BoilerplateConfig() string { 137 return ` 138 manual: 139 type: manual 140 # bootstrap-host holds the host name of the machine where the 141 # bootstrap machine agent will be started. 142 bootstrap-host: somehost.example.com 143 144 # bootstrap-user specifies the user to authenticate as when 145 # connecting to the bootstrap machine. It defaults to 146 # the current user. 147 # bootstrap-user: joebloggs 148 149 # storage-listen-ip specifies the IP address that the 150 # bootstrap machine's Juju storage server will listen 151 # on. By default, storage will be served on all 152 # network interfaces. 153 # storage-listen-ip: 154 155 # storage-port specifes the TCP port that the 156 # bootstrap machine's Juju storage server will listen 157 # on. It defaults to ` + fmt.Sprint(defaultStoragePort) + ` 158 # storage-port: ` + fmt.Sprint(defaultStoragePort) + ` 159 160 161 `[1:] 162 } 163 164 func (p manualProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { 165 envConfig, err := p.validate(cfg, nil) 166 if err != nil { 167 return nil, err 168 } 169 attrs := make(map[string]string) 170 attrs["storage-auth-key"] = envConfig.storageAuthKey() 171 return attrs, nil 172 }