1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
9
"github.com/juju/errors"
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"
17
type manualProvider struct {
18
environProviderCredentials
21
// Verify that we conform to the interface.
22
var _ environs.EnvironProvider = (*manualProvider)(nil)
24
var initUbuntuUser = manual.InitUbuntuUser
26
func ensureBootstrapUbuntuUser(ctx environs.BootstrapContext, host string, cfg *environConfig) error {
27
err := initUbuntuUser(host, cfg.bootstrapUser(), cfg.AuthorizedKeys(), ctx.GetStdin(), ctx.GetStdout())
29
logger.Errorf("initializing ubuntu user: %v", err)
32
logger.Infof("initialized ubuntu user")
36
// RestrictedConfigAttributes is specified in the EnvironProvider interface.
37
func (p manualProvider) RestrictedConfigAttributes() []string {
38
return []string{"bootstrap-user"}
41
// DetectRegions is specified in the environs.CloudRegionDetector interface.
42
func (p manualProvider) DetectRegions() ([]cloud.Region, error) {
43
return nil, errors.NotFoundf("regions")
46
// PrepareConfig is specified in the EnvironProvider interface.
47
func (p manualProvider) PrepareConfig(args environs.PrepareConfigParams) (*config.Config, error) {
48
if args.Cloud.Endpoint == "" {
49
return nil, errors.Errorf(
50
"missing address of host to bootstrap: " +
51
`please specify "juju bootstrap manual/<host>"`,
54
envConfig, err := p.validate(args.Config, nil)
58
return args.Config.Apply(envConfig.attrs)
61
func (p manualProvider) Open(args environs.OpenParams) (environs.Environ, error) {
62
if err := validateCloudSpec(args.Cloud); err != nil {
63
return nil, errors.Trace(err)
65
_, err := p.validate(args.Config, nil)
69
// validate adds missing manual-specific config attributes
70
// with their defaults in the result; we don't wnat that in
72
envConfig := newModelConfig(args.Config, args.Config.UnknownAttrs())
73
return p.open(args.Cloud.Endpoint, envConfig)
76
func validateCloudSpec(spec environs.CloudSpec) error {
77
if spec.Endpoint == "" {
79
"missing address of host to bootstrap: " +
80
`please specify "juju bootstrap manual/<host>"`,
86
func (p manualProvider) open(host string, cfg *environConfig) (environs.Environ, error) {
87
env := &manualEnviron{host: host, cfg: cfg}
88
// Need to call SetConfig to initialise storage.
89
if err := env.SetConfig(cfg.Config); err != nil {
95
func checkImmutableString(cfg, old *environConfig, key string) error {
96
if old.attrs[key] != cfg.attrs[key] {
97
return fmt.Errorf("cannot change %s from %q to %q", key, old.attrs[key], cfg.attrs[key])
102
func (p manualProvider) validate(cfg, old *config.Config) (*environConfig, error) {
103
// Check for valid changes for the base config values.
104
if err := config.Validate(cfg, old); err != nil {
107
validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
111
envConfig := newModelConfig(cfg, validated)
112
// Check various immutable attributes.
114
oldEnvConfig, err := p.validate(old, nil)
118
for _, key := range [...]string{
121
if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil {
127
// If the user hasn't already specified a value, set it to the
129
defineIfNot := func(keyName string, value interface{}) {
130
if _, defined := cfg.AllAttrs()[keyName]; !defined {
131
logger.Infof("%s was not defined. Defaulting to %v.", keyName, value)
132
envConfig.attrs[keyName] = value
136
// If the user hasn't specified a value, refresh the
137
// available updates, but don't upgrade.
138
defineIfNot("enable-os-refresh-update", true)
139
defineIfNot("enable-os-upgrade", false)
141
return envConfig, nil
144
func (p manualProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
145
envConfig, err := p.validate(cfg, old)
149
return cfg.Apply(envConfig.attrs)
152
func (p manualProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
153
attrs := make(map[string]string)