github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "fmt" 8 9 "github.com/juju/errors" 10 11 "github.com/juju/juju/cloud" 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 environProviderCredentials 19 } 20 21 // Verify that we conform to the interface. 22 var _ environs.EnvironProvider = (*manualProvider)(nil) 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 // RestrictedConfigAttributes is specified in the EnvironProvider interface. 39 func (p manualProvider) RestrictedConfigAttributes() []string { 40 return []string{"bootstrap-host", "bootstrap-user"} 41 } 42 43 // DetectRegions is specified in the environs.CloudRegionDetector interface. 44 func (p manualProvider) DetectRegions() ([]cloud.Region, error) { 45 return nil, errors.NotFoundf("regions") 46 } 47 48 // PrepareForCreateEnvironment is specified in the EnvironProvider interface. 49 func (p manualProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { 50 return cfg, nil 51 } 52 53 // BootstrapConfig is specified in the EnvironProvider interface. 54 func (p manualProvider) BootstrapConfig(args environs.BootstrapConfigParams) (*config.Config, error) { 55 var bootstrapHost string 56 switch { 57 case args.CloudEndpoint != "": 58 // If an endpoint is specified, then we expect that the user 59 // has specified in their clouds.yaml a region with the 60 // bootstrap host as the endpoint. 61 bootstrapHost = args.CloudEndpoint 62 case args.CloudRegion != "": 63 // If only a region is specified, then we expect that the user 64 // has run "juju bootstrap manual/<host>", and treat the region 65 // name as the name of the bootstrap machine. 66 bootstrapHost = args.CloudRegion 67 default: 68 return nil, errors.Errorf( 69 "missing address of host to bootstrap: " + 70 `please specify "juju bootstrap manual/<host>"`, 71 ) 72 } 73 cfg, err := args.Config.Apply(map[string]interface{}{ 74 "bootstrap-host": bootstrapHost, 75 }) 76 if err != nil { 77 return nil, errors.Trace(err) 78 } 79 envConfig, err := p.validate(cfg, nil) 80 if err != nil { 81 return nil, err 82 } 83 return cfg.Apply(envConfig.attrs) 84 } 85 86 // PrepareForBootstrap is specified in the EnvironProvider interface. 87 func (p manualProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) { 88 if _, err := p.validate(cfg, nil); err != nil { 89 return nil, err 90 } 91 envConfig := newModelConfig(cfg, cfg.UnknownAttrs()) 92 if err := ensureBootstrapUbuntuUser(ctx, envConfig); err != nil { 93 return nil, err 94 } 95 return p.open(envConfig) 96 } 97 98 func (p manualProvider) Open(cfg *config.Config) (environs.Environ, error) { 99 _, err := p.validate(cfg, nil) 100 if err != nil { 101 return nil, err 102 } 103 // validate adds missing manual-specific config attributes 104 // with their defaults in the result; we don't wnat that in 105 // Open. 106 envConfig := newModelConfig(cfg, cfg.UnknownAttrs()) 107 return p.open(envConfig) 108 } 109 110 func (p manualProvider) open(cfg *environConfig) (environs.Environ, error) { 111 env := &manualEnviron{cfg: cfg} 112 // Need to call SetConfig to initialise storage. 113 if err := env.SetConfig(cfg.Config); err != nil { 114 return nil, err 115 } 116 return env, nil 117 } 118 119 func checkImmutableString(cfg, old *environConfig, key string) error { 120 if old.attrs[key] != cfg.attrs[key] { 121 return fmt.Errorf("cannot change %s from %q to %q", key, old.attrs[key], cfg.attrs[key]) 122 } 123 return nil 124 } 125 126 func (p manualProvider) validate(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 validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 132 if err != nil { 133 return nil, err 134 } 135 envConfig := newModelConfig(cfg, validated) 136 if envConfig.bootstrapHost() == "" { 137 return nil, errNoBootstrapHost 138 } 139 // Check various immutable attributes. 140 if old != nil { 141 oldEnvConfig, err := p.validate(old, nil) 142 if err != nil { 143 return nil, err 144 } 145 for _, key := range [...]string{ 146 "bootstrap-user", 147 "bootstrap-host", 148 } { 149 if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil { 150 return nil, err 151 } 152 } 153 } 154 155 // If the user hasn't already specified a value, set it to the 156 // given value. 157 defineIfNot := func(keyName string, value interface{}) { 158 if _, defined := cfg.AllAttrs()[keyName]; !defined { 159 logger.Infof("%s was not defined. Defaulting to %v.", keyName, value) 160 envConfig.attrs[keyName] = value 161 } 162 } 163 164 // If the user hasn't specified a value, refresh the 165 // available updates, but don't upgrade. 166 defineIfNot("enable-os-refresh-update", true) 167 defineIfNot("enable-os-upgrade", false) 168 169 return envConfig, nil 170 } 171 172 func (p manualProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 173 envConfig, err := p.validate(cfg, old) 174 if err != nil { 175 return nil, err 176 } 177 return cfg.Apply(envConfig.attrs) 178 } 179 180 func (p manualProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { 181 attrs := make(map[string]string) 182 return attrs, nil 183 }