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