// prepare is the internal version of Prepare - it prepares the // environment but does not open it. func (p *environProvider) prepare(cfg *config.Config) (*config.Config, error) { ecfg, err := p.newConfig(cfg) if err != nil { return nil, err } p.mu.Lock() defer p.mu.Unlock() name := cfg.Name() if ecfg.stateId() != noStateId { return cfg, nil } if ecfg.stateServer() && len(p.state) != 0 { for _, old := range p.state { panic(fmt.Errorf("cannot share a state between two dummy environs; old %q; new %q", old.name, name)) } } // The environment has not been prepared, // so create it and set its state identifier accordingly. state := newState(name, p.ops, p.statePolicy) p.maxStateId++ state.id = p.maxStateId p.state[state.id] = state attrs := map[string]interface{}{"state-id": fmt.Sprint(state.id)} if ecfg.stateServer() { attrs["api-port"] = state.listenAPI() } return cfg.Apply(attrs) }
// decorateAndWriteInfo decorates the info struct with information // from the given cfg, and the writes that out to the filesystem. func decorateAndWriteInfo(info configstore.EnvironInfo, cfg *config.Config) error { // Sanity check our config. var endpoint configstore.APIEndpoint if cert, ok := cfg.CACert(); !ok { return errors.Errorf("CACert is not set") } else if uuid, ok := cfg.UUID(); !ok { return errors.Errorf("UUID is not set") } else if adminSecret := cfg.AdminSecret(); adminSecret == "" { return errors.Errorf("admin-secret is not set") } else { endpoint = configstore.APIEndpoint{ CACert: cert, ModelUUID: uuid, } } creds := configstore.APICredentials{ User: configstore.DefaultAdminUsername, Password: cfg.AdminSecret(), } endpoint.ServerUUID = endpoint.ModelUUID info.SetAPICredentials(creds) info.SetAPIEndpoint(endpoint) info.SetBootstrapConfig(cfg.AllAttrs()) if err := info.Write(); err != nil { return errors.Annotatef(err, "cannot create model info %q", cfg.Name()) } return nil }
// decorateAndWriteInfo decorates the info struct with information // from the given cfg, and the writes that out to the filesystem. func decorateAndWriteInfo(info configstore.EnvironInfo, cfg *config.Config) error { // Sanity check our config. var endpoint configstore.APIEndpoint if cert, ok := cfg.CACert(); !ok { return errors.Errorf("CACert is not set") } else if uuid, ok := cfg.UUID(); !ok { return errors.Errorf("UUID is not set") } else if adminSecret := cfg.AdminSecret(); adminSecret == "" { return errors.Errorf("admin-secret is not set") } else { endpoint = configstore.APIEndpoint{ CACert: cert, EnvironUUID: uuid, } } creds := configstore.APICredentials{ User: "******", // TODO(waigani) admin@local once we have that set Password: cfg.AdminSecret(), } info.SetAPICredentials(creds) info.SetAPIEndpoint(endpoint) info.SetBootstrapConfig(cfg.AllAttrs()) if err := info.Write(); err != nil { return errors.Annotatef(err, "cannot create environment info %q", cfg.Name()) } return nil }
// SecretAttrs filters the supplied configuration returning only values // which are considered sensitive. All of the values of these secret // attributes need to be strings. func (environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { logger.Infof("filtering secret attributes for environment %q", cfg.Name()) // If you keep configSecretFields up to date, this method should Just Work. ecfg, err := validateConfig(cfg, nil) if err != nil { return nil, err } secretAttrs := map[string]string{} for _, field := range configSecretFields { if value, ok := ecfg.attrs[field]; ok { if stringValue, ok := value.(string); ok { secretAttrs[field] = stringValue } else { // All your secret attributes must be strings at the moment. Sorry. // It's an expedient and hopefully temporary measure that helps us // plug a security hole in the API. return nil, errors.Errorf( "secret %q field must have a string value; got %v", field, value, ) } } } return secretAttrs, nil }
// NewModel creates a new model with its own UUID and // prepares it for use. Model and State instances for the new // model are returned. // // The controller model's UUID is attached to the new // model's document. Having the server UUIDs stored with each // model document means that we have a way to represent external // models, perhaps for future use around cross model // relations. func (st *State) NewModel(cfg *config.Config, owner names.UserTag) (_ *Model, _ *State, err error) { if owner.IsLocal() { if _, err := st.User(owner); err != nil { return nil, nil, errors.Annotate(err, "cannot create model") } } ssEnv, err := st.ControllerModel() if err != nil { return nil, nil, errors.Annotate(err, "could not load controller model") } uuid := cfg.UUID() newState, err := st.ForModel(names.NewModelTag(uuid)) if err != nil { return nil, nil, errors.Annotate(err, "could not create state for new model") } defer func() { if err != nil { newState.Close() } }() ops, err := newState.envSetupOps(cfg, uuid, ssEnv.UUID(), owner) if err != nil { return nil, nil, errors.Annotate(err, "failed to create new model") } err = newState.runTransaction(ops) if err == txn.ErrAborted { // We have a unique key restriction on the "owner" and "name" fields, // which will cause the insert to fail if there is another record with // the same "owner" and "name" in the collection. If the txn is // aborted, check if it is due to the unique key restriction. models, closer := st.getCollection(modelsC) defer closer() envCount, countErr := models.Find(bson.D{ {"owner", owner.Canonical()}, {"name", cfg.Name()}}, ).Count() if countErr != nil { err = errors.Trace(countErr) } else if envCount > 0 { err = errors.AlreadyExistsf("model %q for %s", cfg.Name(), owner.Canonical()) } else { err = errors.New("model already exists") } } if err != nil { return nil, nil, errors.Trace(err) } newEnv, err := newState.Model() if err != nil { return nil, nil, errors.Trace(err) } return newEnv, newState, nil }
func (maasEnvironProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Debugf("opening environment %q.", cfg.Name()) env, err := NewEnviron(cfg) if err != nil { return nil, err } return env, nil }
// Open is specified in the EnvironProvider interface. func (prov *azureEnvironProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Debugf("opening model %q", cfg.Name()) environ, err := newEnviron(prov, cfg) if err != nil { return nil, errors.Annotate(err, "opening model") } return environ, nil }
// VolumeSource is defined on the Provider interface. func (e *ebsProvider) VolumeSource(environConfig *config.Config, cfg *storage.Config) (storage.VolumeSource, error) { ec2, _, _, err := awsClients(environConfig) if err != nil { return nil, errors.Annotate(err, "creating AWS clients") } source := &ebsVolumeSource{ec2: ec2, envName: environConfig.Name()} return source, nil }
// Initialize sets up an initial empty state and returns it. // This needs to be performed only once for a given environment. // It returns unauthorizedError if access is unauthorized. func Initialize(info *mongo.MongoInfo, cfg *config.Config, opts mongo.DialOpts, policy Policy) (rst *State, err error) { st, err := open(info, opts, policy) if err != nil { return nil, err } defer func() { if err != nil { st.Close() } }() // A valid environment is used as a signal that the // state has already been initalized. If this is the case // do nothing. if _, err := st.Environment(); err == nil { return st, nil } else if !errors.IsNotFound(err) { return nil, err } logger.Infof("initializing environment") if err := checkEnvironConfig(cfg); err != nil { return nil, err } uuid, ok := cfg.UUID() if !ok { return nil, errors.Errorf("environment uuid was not supplied") } st.environTag = names.NewEnvironTag(uuid) ops := []txn.Op{ createConstraintsOp(st, environGlobalKey, constraints.Value{}), createSettingsOp(st, environGlobalKey, cfg.AllAttrs()), createEnvironmentOp(st, cfg.Name(), uuid), { C: stateServersC, Id: environGlobalKey, Assert: txn.DocMissing, Insert: &stateServersDoc{ EnvUUID: uuid, }, }, { C: stateServersC, Id: apiHostPortsKey, Assert: txn.DocMissing, Insert: &apiHostPortsDoc{}, }, { C: stateServersC, Id: stateServingInfoKey, Assert: txn.DocMissing, Insert: ¶ms.StateServingInfo{}, }, } if err := st.runTransaction(ops); err == txn.ErrAborted { // The config was created in the meantime. return st, nil } else if err != nil { return nil, err } return st, nil }
// Open opens the environment and returns it. // The configuration must have come from a previously // prepared environment. func (environProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Infof("opening model %q", cfg.Name()) env := &environ{name: cfg.Name()} if err := env.SetConfig(cfg); err != nil { return nil, err } return env, nil }
// resourceGroupName returns the name of the environment's resource group. func resourceGroupName(cfg *config.Config) string { uuid, _ := cfg.UUID() // UUID is always available for azure environments, since the (new) // provider was introduced after environment UUIDs. modelTag := names.NewModelTag(uuid) return fmt.Sprintf( "juju-%s-%s", cfg.Name(), resourceName(modelTag), ) }
func (p environProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Infof("opening environment %q", cfg.Name()) e := new(environ) e.name = cfg.Name() err := e.SetConfig(cfg) if err != nil { return nil, err } return e, nil }
// Validate implements environs.EnvironProvider.Validate. func (provider environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { // Check for valid changes for the base config values. if err := config.Validate(cfg, old); err != nil { return nil, err } validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, fmt.Errorf("failed to validate unknown attrs: %v", err) } localConfig := newEnvironConfig(cfg, validated) // Before potentially creating directories, make sure that the // root directory has not changed. containerType := localConfig.container() if old != nil { oldLocalConfig, err := provider.newConfig(old) if err != nil { return nil, fmt.Errorf("old config is not a valid local config: %v", old) } if containerType != oldLocalConfig.container() { return nil, fmt.Errorf("cannot change container from %q to %q", oldLocalConfig.container(), containerType) } if localConfig.rootDir() != oldLocalConfig.rootDir() { return nil, fmt.Errorf("cannot change root-dir from %q to %q", oldLocalConfig.rootDir(), localConfig.rootDir()) } if localConfig.networkBridge() != oldLocalConfig.networkBridge() { return nil, fmt.Errorf("cannot change network-bridge from %q to %q", oldLocalConfig.rootDir(), localConfig.rootDir()) } if localConfig.storagePort() != oldLocalConfig.storagePort() { return nil, fmt.Errorf("cannot change storage-port from %v to %v", oldLocalConfig.storagePort(), localConfig.storagePort()) } } // Currently only supported containers are "lxc" and "kvm". if containerType != instance.LXC && containerType != instance.KVM { return nil, fmt.Errorf("unsupported container type: %q", containerType) } dir, err := utils.NormalizePath(localConfig.rootDir()) if err != nil { return nil, err } if dir == "." { dir = osenv.JujuHomePath(cfg.Name()) } // Always assign the normalized path. localConfig.attrs["root-dir"] = dir // Apply the coerced unknown values back into the config. return cfg.Apply(localConfig.attrs) }
// newEnviron creates a new azureEnviron. func newEnviron(provider *azureEnvironProvider, cfg *config.Config) (*azureEnviron, error) { env := azureEnviron{provider: provider} err := env.SetConfig(cfg) if err != nil { return nil, err } modelTag := names.NewModelTag(cfg.UUID()) env.resourceGroup = resourceGroupName(modelTag, cfg.Name()) env.envName = cfg.Name() return &env, nil }
// newEnviron creates a new azureEnviron. func newEnviron(provider *azureEnvironProvider, cfg *config.Config) (*azureEnviron, error) { env := azureEnviron{provider: provider} err := env.SetConfig(cfg) if err != nil { return nil, err } env.resourceGroup = resourceGroupName(cfg) env.controllerResourceGroup = env.config.controllerResourceGroup env.envName = cfg.Name() return &env, nil }
// Open is specified in the EnvironProvider interface. func (prov azureEnvironProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Debugf("opening environment %q.", cfg.Name()) // We can't return NewEnviron(cfg) directly here because otherwise, // when err is not nil, we end up with a non-nil returned environ and // this breaks the loop in cmd/jujud/upgrade.go:run() (see // http://golang.org/doc/faq#nil_error for the gory details). environ, err := NewEnviron(cfg) if err != nil { return nil, err } return environ, nil }
func (p EnvironProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Infof("opening model %q", cfg.Name()) e := new(Environ) e.firewaller = p.FirewallerFactory.GetFirewaller(e) e.configurator = p.Configurator err := e.SetConfig(cfg) if err != nil { return nil, err } e.name = cfg.Name() return e, nil }
// newEnviron create a new Joyent environ instance from config. func newEnviron(cfg *config.Config) (*joyentEnviron, error) { env := new(joyentEnviron) if err := env.SetConfig(cfg); err != nil { return nil, err } env.name = cfg.Name() var err error env.compute, err = newCompute(env.ecfg) if err != nil { return nil, err } return env, nil }
func (g *storageProvider) VolumeSource(environConfig *config.Config, cfg *storage.Config) (storage.VolumeSource, error) { // Connect and authenticate. env, err := newEnviron(environConfig) if err != nil { return nil, errors.Annotate(err, "cannot create an environ with this config") } source := &volumeSource{ gce: env.gce, envName: environConfig.Name(), modelUUID: environConfig.UUID(), } return source, nil }
// VolumeSource implements storage.Provider. func (p *cinderProvider) VolumeSource(environConfig *config.Config, providerConfig *storage.Config) (storage.VolumeSource, error) { if err := p.ValidateConfig(providerConfig); err != nil { return nil, err } storageAdapter, err := p.newStorageAdapter(environConfig) if err != nil { return nil, err } source := &cinderVolumeSource{ storageAdapter: storageAdapter, envName: environConfig.Name(), } return source, nil }
// Prepare prepares a new environment based on the provided configuration. // If the environment is already prepared, it behaves like New. func Prepare(cfg *config.Config, ctx BootstrapContext, store configstore.Storage) (Environ, error) { if p, err := Provider(cfg.Type()); err != nil { return nil, err } else if info, err := store.ReadInfo(cfg.Name()); errors.IsNotFound(errors.Cause(err)) { info = store.CreateInfo(cfg.Name()) if env, err := prepare(ctx, cfg, info, p); err == nil { return env, decorateAndWriteInfo(info, env.Config()) } else { if err := info.Destroy(); err != nil { logger.Warningf("cannot destroy newly created environment info: %v", err) } return nil, err } } else if err != nil { return nil, errors.Annotatef(err, "error reading environment info %q", cfg.Name()) } else if !info.Initialized() { return nil, errors.Errorf( "found uninitialized environment info for %q; environment preparation probably in progress or interrupted", cfg.Name(), ) } else if len(info.BootstrapConfig()) == 0 { return nil, errors.New("found environment info but no bootstrap config") } else { cfg, err = config.New(config.NoDefaults, info.BootstrapConfig()) if err != nil { return nil, errors.Annotate(err, "cannot parse bootstrap config") } return New(cfg) } }
// newEnviron create a new Joyent environ instance from config. func newEnviron(cloud environs.CloudSpec, cfg *config.Config) (*joyentEnviron, error) { env := &joyentEnviron{ name: cfg.Name(), cloud: cloud, } if err := env.SetConfig(cfg); err != nil { return nil, err } var err error env.compute, err = newCompute(cloud) if err != nil { return nil, err } return env, nil }
// Initialize sets up an initial empty state and returns it. // This needs to be performed only once for a given environment. // It returns unauthorizedError if access is unauthorized. func Initialize(info *Info, cfg *config.Config, opts mongo.DialOpts, policy Policy) (rst *State, err error) { st, err := Open(info, opts, policy) if err != nil { return nil, err } defer func() { if err != nil { st.Close() } }() // A valid environment is used as a signal that the // state has already been initalized. If this is the case // do nothing. if _, err := st.Environment(); err == nil { return st, nil } else if !errors.IsNotFound(err) { return nil, err } logger.Infof("initializing environment") if err := checkEnvironConfig(cfg); err != nil { return nil, err } uuid, err := utils.NewUUID() if err != nil { return nil, fmt.Errorf("environment UUID cannot be created: %v", err) } ops := []txn.Op{ createConstraintsOp(st, environGlobalKey, constraints.Value{}), createSettingsOp(st, environGlobalKey, cfg.AllAttrs()), createEnvironmentOp(st, cfg.Name(), uuid.String()), { C: st.stateServers.Name, Id: environGlobalKey, Insert: &stateServersDoc{}, }, { C: st.stateServers.Name, Id: apiHostPortsKey, Insert: &apiHostPortsDoc{}, }, } if err := st.runTransaction(ops); err == txn.ErrAborted { // The config was created in the meantime. return st, nil } else if err != nil { return nil, err } return st, nil }
// VolumeSource is defined on the Provider interface. func (e *ebsProvider) VolumeSource(environConfig *config.Config, cfg *storage.Config) (storage.VolumeSource, error) { ec2, _, _, err := awsClients(environConfig) if err != nil { return nil, errors.Annotate(err, "creating AWS clients") } uuid, ok := environConfig.UUID() if !ok { return nil, errors.NotFoundf("model UUID") } source := &ebsVolumeSource{ ec2: ec2, envName: environConfig.Name(), modelUUID: uuid, } return source, nil }
// VolumeSource is defined on the Provider interface. func (e *azureStorageProvider) VolumeSource(environConfig *config.Config, cfg *storage.Config) (storage.VolumeSource, error) { env, err := NewEnviron(environConfig) if err != nil { return nil, errors.Trace(err) } uuid, ok := environConfig.UUID() if !ok { return nil, errors.NotFoundf("environment UUID") } source := &azureVolumeSource{ env: env, envName: environConfig.Name(), envUUID: uuid, } return source, nil }
func (sp *StubProvider) Open(cfg *config.Config) (environs.Environ, error) { sp.MethodCall(sp, "Open", cfg) if err := sp.NextErr(); err != nil { return nil, err } switch cfg.Name() { case StubEnvironName: return EnvironInstance, nil case StubZonedEnvironName: return ZonedEnvironInstance, nil case StubNetworkingEnvironName: return NetworkingEnvironInstance, nil case StubZonedNetworkingEnvironName: return ZonedNetworkingEnvironInstance, nil } panic("unexpected model name: " + cfg.Name()) }
// newEnviron creates a new azureEnviron. func newEnviron( provider *azureEnvironProvider, cloud environs.CloudSpec, cfg *config.Config, ) (*azureEnviron, error) { // The Azure storage code wants the endpoint host only, not the URL. storageEndpointURL, err := url.Parse(cloud.StorageEndpoint) if err != nil { return nil, errors.Annotate(err, "parsing storage endpoint URL") } env := azureEnviron{ provider: provider, cloud: cloud, location: canonicalLocation(cloud.Region), storageEndpoint: storageEndpointURL.Host, } if err := env.initEnviron(); err != nil { return nil, errors.Trace(err) } if err := env.SetConfig(cfg); err != nil { return nil, errors.Trace(err) } modelTag := names.NewModelTag(cfg.UUID()) env.resourceGroup = resourceGroupName(modelTag, cfg.Name()) env.envName = cfg.Name() // We need a deterministic storage account name, so that we can // defer creation of the storage account to the VM deployment, // and retain the ability to create multiple deployments in // parallel. // // We use the last 20 non-hyphen hex characters of the model's // UUID as the storage account name, prefixed with "juju". The // probability of clashing with another storage account should // be negligible. uuidAlphaNumeric := strings.Replace(env.config.Config.UUID(), "-", "", -1) env.storageAccountName = "juju" + uuidAlphaNumeric[len(uuidAlphaNumeric)-20:] return &env, nil }
// Open implements environs.EnvironProvider.Open. func (environProvider) Open(cfg *config.Config) (environs.Environ, error) { logger.Infof("opening environment %q", cfg.Name()) // Do the initial validation on the config. localConfig, err := providerInstance.newConfig(cfg) if err != nil { return nil, errors.Trace(err) } if err := VerifyPrerequisites(localConfig.container()); err != nil { return nil, errors.Annotate(err, "failed verification of local provider prerequisites") } if cfg, err = providerInstance.correctLocalhostURLs(cfg, localConfig); err != nil { return nil, errors.Annotate(err, "failed to replace localhost references in loopback URLs specified in proxy config settings") } environ := &localEnviron{name: cfg.Name()} if err := environ.SetConfig(cfg); err != nil { return nil, errors.Annotate(err, "failure setting config") } return environ, nil }
// VolumeSource implements storage.Provider. func (p *cinderProvider) VolumeSource(environConfig *config.Config, providerConfig *storage.Config) (storage.VolumeSource, error) { if err := p.ValidateConfig(providerConfig); err != nil { return nil, err } storageAdapter, err := p.newStorageAdapter(environConfig) if err != nil { return nil, err } uuid, ok := environConfig.UUID() if !ok { return nil, errors.NotFoundf("environment UUID") } source := &cinderVolumeSource{ storageAdapter: storageAdapter, envName: environConfig.Name(), envUUID: uuid, } return source, nil }
// ensureCertificate generates a new CA certificate and // attaches it to the given environment configuration, // unless the configuration already has one. func ensureCertificate(cfg *config.Config) (*config.Config, error) { _, hasCACert := cfg.CACert() _, hasCAKey := cfg.CAPrivateKey() if hasCACert && hasCAKey { return cfg, nil } if hasCACert && !hasCAKey { return nil, fmt.Errorf("environment configuration with a certificate but no CA private key") } caCert, caKey, err := cert.NewCA(cfg.Name(), time.Now().UTC().AddDate(10, 0, 0)) if err != nil { return nil, err } return cfg.Apply(map[string]interface{}{ "ca-cert": string(caCert), "ca-private-key": string(caKey), }) }