func (s *LiveTests) assertStartInstanceDefaultSecurityGroup(c *gc.C, useDefault bool) { attrs := s.TestConfig.Merge(coretesting.Attrs{ "name": "sample-" + randomName(), "control-bucket": "juju-test-" + randomName(), "use-default-secgroup": useDefault, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) // Set up a test environment. env, err := environs.New(cfg) c.Assert(err, gc.IsNil) c.Assert(env, gc.NotNil) defer env.Destroy() // Bootstrap and start an instance. err = bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) c.Assert(err, gc.IsNil) inst, _ := jujutesting.AssertStartInstance(c, env, "100") // Check whether the instance has the default security group assigned. novaClient := openstack.GetNovaClient(env) groups, err := novaClient.GetServerSecurityGroups(string(inst.Id())) c.Assert(err, gc.IsNil) defaultGroupFound := false for _, group := range groups { if group.Name == "default" { defaultGroupFound = true break } } c.Assert(defaultGroupFound, gc.Equals, useDefault) }
func (s *machineConfigSuite) TestMachineConfig(c *gc.C) { addrs := []instance.Address{instance.NewAddress("1.2.3.4", instance.NetworkUnknown)} hc := instance.MustParseHardware("mem=4G arch=amd64") apiParams := params.AddMachineParams{ Jobs: []params.MachineJob{params.JobHostUnits}, InstanceId: instance.Id("1234"), Nonce: "foo", HardwareCharacteristics: hc, Addrs: addrs, } machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams}) c.Assert(err, gc.IsNil) c.Assert(len(machines), gc.Equals, 1) machineId := machines[0].Machine machineConfig, err := client.MachineConfig(s.State, machineId, apiParams.Nonce, "") c.Assert(err, gc.IsNil) envConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) env, err := environs.New(envConfig) c.Assert(err, gc.IsNil) stateInfo, apiInfo, err := env.StateInfo() c.Assert(err, gc.IsNil) c.Check(machineConfig.StateInfo.Addrs, gc.DeepEquals, stateInfo.Addrs) c.Check(machineConfig.APIInfo.Addrs, gc.DeepEquals, apiInfo.Addrs) c.Assert(machineConfig.Tools.URL, gc.Not(gc.Equals), "") }
func (s *ConfigSuite) TestNewEnvironConfig(c *gc.C) { for i, test := range newConfigTests { c.Logf("test %d: %s", i, test.info) for k, v := range test.envVars { os.Setenv(k, v) } attrs := validAttrs().Merge(test.insert).Delete(test.remove...) attrs["private-key"] = s.privateKeyData testConfig := newConfig(c, attrs) environ, err := environs.New(testConfig) if test.err == "" { c.Check(err, gc.IsNil) if err != nil { continue } attrs := environ.Config().AllAttrs() for field, value := range test.expect { c.Check(attrs[field], gc.Equals, value) } } else { c.Check(environ, gc.IsNil) c.Check(err, gc.ErrorMatches, test.err) } } }
// MachineConfig returns information from the environment config that is // needed for machine cloud-init (for non-state servers only). // It is exposed for testing purposes. // TODO(rog) fix environs/manual tests so they do not need to // call this, or move this elsewhere. func MachineConfig(st *state.State, machineId, nonce, dataDir string) (*cloudinit.MachineConfig, error) { environConfig, err := st.EnvironConfig() if err != nil { return nil, err } // Get the machine so we can get its series and arch. // If the Arch is not set in hardware-characteristics, // an error is returned. machine, err := st.Machine(machineId) if err != nil { return nil, err } hc, err := machine.HardwareCharacteristics() if err != nil { return nil, err } if hc.Arch == nil { return nil, fmt.Errorf("arch is not set for %q", machine.Tag()) } // Find the appropriate tools information. env, err := environs.New(environConfig) if err != nil { return nil, err } tools, err := findInstanceTools(env, machine.Series(), *hc.Arch) if err != nil { return nil, err } // Find the secrets and API endpoints. auth, err := environs.NewEnvironAuthenticator(env) if err != nil { return nil, err } stateInfo, apiInfo, err := auth.SetupAuthentication(machine) if err != nil { return nil, err } // Find requested networks. includeNetworks, excludeNetworks, err := machine.RequestedNetworks() if err != nil { return nil, err } mcfg := environs.NewMachineConfig(machineId, nonce, includeNetworks, excludeNetworks, stateInfo, apiInfo) if dataDir != "" { mcfg.DataDir = dataDir } mcfg.Tools = tools err = environs.FinishMachineConfig(mcfg, environConfig, constraints.Value{}) if err != nil { return nil, err } return mcfg, nil }
// Open opens an instance of the testing environment. func (t *Tests) Open(c *gc.C) environs.Environ { info, err := t.ConfigStore.ReadInfo(t.TestConfig["name"].(string)) c.Assert(err, gc.IsNil) cfg, err := config.New(config.NoDefaults, info.BootstrapConfig()) c.Assert(err, gc.IsNil) e, err := environs.New(cfg) c.Assert(err, gc.IsNil, gc.Commentf("opening environ %#v", cfg.AllAttrs())) c.Assert(e, gc.NotNil) return e }
// NewConnFromState returns a Conn that uses an Environ // made by reading the environment configuration. // The resulting Conn uses the given State - closing // it will close that State. func NewConnFromState(st *state.State) (*Conn, error) { cfg, err := st.EnvironConfig() if err != nil { return nil, err } environ, err := environs.New(cfg) if err != nil { return nil, err } return &Conn{ Environ: environ, State: st, }, nil }
// FindTools returns a List containing all tools matching the given parameters. func (c *Client) FindTools(args params.FindToolsParams) (params.FindToolsResults, error) { result := params.FindToolsResults{} // Get the existing environment config from the state. envConfig, err := c.api.state.EnvironConfig() if err != nil { return result, err } env, err := environs.New(envConfig) if err != nil { return result, err } filter := coretools.Filter{ Arch: args.Arch, Series: args.Series, } result.List, err = envtools.FindTools(env, args.MajorVersion, args.MinorVersion, filter, envtools.DoNotAllowRetry) result.Error = common.ServerError(err) return result, nil }
// apiConfigConnect looks for configuration info on the given environment, // and tries to use an Environ constructed from that to connect to // its endpoint. It only starts the attempt after the given delay, // to allow the faster apiInfoConnect to hopefully succeed first. // It returns nil if there was no configuration information found. func apiConfigConnect(cfg *config.Config, apiOpen apiOpenFunc, stop <-chan struct{}, delay time.Duration) (apiState, error) { select { case <-time.After(delay): case <-stop: return nil, errAborted } environ, err := environs.New(cfg) if err != nil { return nil, err } apiInfo, err := environAPIInfo(environ) if err != nil { return nil, err } st, err := apiOpen(apiInfo, api.DefaultDialOpts()) // TODO(rog): handle errUnauthorized when the API handles passwords. if err != nil { return nil, err } return apiStateCachedInfo{st, apiInfo}, nil }
func (u *UnitUpgraderAPI) getMachineTools(tag string) params.ToolsResult { var result params.ToolsResult machine, err := u.getAssignedMachine(tag) if err != nil { result.Error = common.ServerError(err) return result } machineTools, err := machine.AgentTools() if err != nil { result.Error = common.ServerError(err) return result } // For older 1.16 upgrader workers, we need to supply a tools URL since the worker will attempt to // download the tools even though they already have been fetched by the machine agent. Newer upgrader // workers do not have this problem. So to be compatible across all versions, we return the full // tools metadata. // TODO (wallyworld) - remove in 1.20, just return machineTools cfg, err := u.st.EnvironConfig() if err != nil { result.Error = common.ServerError(err) return result } // SSLHostnameVerification defaults to true, so we need to // invert that, for backwards-compatibility (older versions // will have DisableSSLHostnameVerification: false by default). result.DisableSSLHostnameVerification = !cfg.SSLHostnameVerification() env, err := environs.New(cfg) if err != nil { result.Error = common.ServerError(err) return result } agentTools, err := envtools.FindExactTools( env, machineTools.Version.Number, machineTools.Version.Series, machineTools.Version.Arch) if err != nil { result.Error = common.ServerError(err) return result } result.Tools = agentTools return result }
// AssertEnvironConfig provides a method to test the config from the // envWatcher. This allows other tests that embed this type to have // more than just the default test. func (s *EnvironWatcherTest) AssertEnvironConfig(c *gc.C, envWatcher EnvironmentWatcher, hasSecrets bool) { envConfig, err := s.st.EnvironConfig() c.Assert(err, gc.IsNil) result, err := envWatcher.EnvironConfig() c.Assert(err, gc.IsNil) configAttributes := envConfig.AllAttrs() // If the implementor doesn't provide secrets, we need to replace the config // values in our environment to compare against with the secrets replaced. if !hasSecrets { env, err := environs.New(envConfig) c.Assert(err, gc.IsNil) secretAttrs, err := env.Provider().SecretAttrs(envConfig) c.Assert(err, gc.IsNil) for key := range secretAttrs { configAttributes[key] = "not available" } } c.Assert(result.Config, jc.DeepEquals, params.EnvironConfig(configAttributes)) }
func (s *ConfigSuite) TestSetConfig(c *gc.C) { baseConfig := newConfig(c, validAttrs()) for i, test := range changeConfigTests { c.Logf("test %d: %s", i, test.info) environ, err := environs.New(baseConfig) c.Assert(err, gc.IsNil) attrs := validAttrs().Merge(test.insert).Delete(test.remove...) testConfig := newConfig(c, attrs) err = environ.SetConfig(testConfig) newAttrs := environ.Config().AllAttrs() if test.err == "" { c.Check(err, gc.IsNil) for field, value := range test.expect { c.Check(newAttrs[field], gc.Equals, value) } } else { c.Check(err, gc.ErrorMatches, test.err) for field, value := range baseConfig.UnknownAttrs() { c.Check(newAttrs[field], gc.Equals, value) } } } }
func (s *EnvironWatcherTests) TestEnvironConfig(c *gc.C) { envConfig, err := s.state.EnvironConfig() c.Assert(err, gc.IsNil) conf, err := s.facade.EnvironConfig() c.Assert(err, gc.IsNil) // If the facade doesn't have secrets, we need to replace the config // values in our environment to compare against with the secrets replaced. if !s.hasSecrets { env, err := environs.New(envConfig) c.Assert(err, gc.IsNil) secretAttrs, err := env.Provider().SecretAttrs(envConfig) c.Assert(err, gc.IsNil) secrets := make(map[string]interface{}) for key := range secretAttrs { secrets[key] = "not available" } envConfig, err = envConfig.Apply(secrets) c.Assert(err, gc.IsNil) } c.Assert(conf, jc.DeepEquals, envConfig) }
// AddCharm adds the given charm URL (which must include revision) to // the environment, if it does not exist yet. Local charms are not // supported, only charm store URLs. See also AddLocalCharm(). func (c *Client) AddCharm(args params.CharmURL) error { charmURL, err := charm.ParseURL(args.URL) if err != nil { return err } if charmURL.Schema != "cs" { return fmt.Errorf("only charm store charm URLs are supported, with cs: schema") } if charmURL.Revision < 0 { return fmt.Errorf("charm URL must include revision") } // First, check if a pending or a real charm exists in state. stateCharm, err := c.api.state.PrepareStoreCharmUpload(charmURL) if err == nil && stateCharm.IsUploaded() { // Charm already in state (it was uploaded already). return nil } else if err != nil { return err } // Get the charm and its information from the store. envConfig, err := c.api.state.EnvironConfig() if err != nil { return err } store := config.SpecializeCharmRepo(CharmStore, envConfig) downloadedCharm, err := store.Get(charmURL) if err != nil { return errors.Annotatef(err, "cannot download charm %q", charmURL.String()) } // Open it and calculate the SHA256 hash. downloadedBundle, ok := downloadedCharm.(*charm.Bundle) if !ok { return errors.Errorf("expected a charm archive, got %T", downloadedCharm) } archive, err := os.Open(downloadedBundle.Path) if err != nil { return errors.Annotate(err, "cannot read downloaded charm") } defer archive.Close() bundleSHA256, size, err := utils.ReadSHA256(archive) if err != nil { return errors.Annotate(err, "cannot calculate SHA256 hash of charm") } if _, err := archive.Seek(0, 0); err != nil { return errors.Annotate(err, "cannot rewind charm archive") } // Get the environment storage and upload the charm. env, err := environs.New(envConfig) if err != nil { return errors.Annotate(err, "cannot access environment") } storage := env.Storage() archiveName, err := CharmArchiveName(charmURL.Name, charmURL.Revision) if err != nil { return errors.Annotate(err, "cannot generate charm archive name") } if err := storage.Put(archiveName, archive, size); err != nil { return errors.Annotate(err, "cannot upload charm to provider storage") } storageURL, err := storage.URL(archiveName) if err != nil { return errors.Annotate(err, "cannot get storage URL for charm") } bundleURL, err := url.Parse(storageURL) if err != nil { return errors.Annotate(err, "cannot parse storage URL") } // Finally, update the charm data in state and mark it as no longer pending. _, err = c.api.state.UpdateUploadedCharm(downloadedCharm, charmURL, bundleURL, bundleSHA256) if err == state.ErrCharmRevisionAlreadyModified || state.IsCharmAlreadyUploadedError(err) { // This is not an error, it just signifies somebody else // managed to upload and update the charm in state before // us. This means we have to delete what we just uploaded // to storage. if err := storage.Remove(archiveName); err != nil { errors.Annotate(err, "cannot remove duplicated charm from storage") } return nil } return err }
func (c *DeployCommand) Run(ctx *cmd.Context) error { client, err := juju.NewAPIClientFromName(c.EnvName) if err != nil { return err } defer client.Close() attrs, err := client.EnvironmentGet() if err != nil { return err } conf, err := config.New(config.NoDefaults, attrs) if err != nil { return err } curl, err := resolveCharmURL(c.CharmName, client, conf) if err != nil { return err } repo, err := charm.InferRepository(curl.Reference, ctx.AbsPath(c.RepoPath)) if err != nil { return err } repo = config.SpecializeCharmRepo(repo, conf) curl, err = addCharmViaAPI(client, ctx, curl, repo) if err != nil { return err } if c.BumpRevision { ctx.Infof("--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") } var includeNetworks []string if c.Networks != "" { includeNetworks = parseNetworks(c.Networks) env, err := environs.New(conf) if err != nil { return err } if !env.SupportNetworks() { return errors.New("cannot use --networks: not supported by the environment") } } charmInfo, err := client.CharmInfo(curl.String()) if err != nil { return err } numUnits := c.NumUnits if charmInfo.Meta.Subordinate { if !constraints.IsEmpty(&c.Constraints) { return errors.New("cannot use --constraints with subordinate service") } if numUnits == 1 && c.ToMachineSpec == "" { numUnits = 0 } else { return errors.New("cannot use --num-units or --to with subordinate service") } } serviceName := c.ServiceName if serviceName == "" { serviceName = charmInfo.Meta.Name } var configYAML []byte if c.Config.Path != "" { configYAML, err = c.Config.Read(ctx) if err != nil { return err } } err = client.ServiceDeployWithNetworks( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec, includeNetworks, nil, ) if params.IsCodeNotImplemented(err) { if len(includeNetworks) > 0 { return errors.New("cannot use --networks: not supported by the API server") } err = client.ServiceDeploy( curl.String(), serviceName, numUnits, string(configYAML), c.Constraints, c.ToMachineSpec) } return err }
// Run initializes state for an environment. func (c *BootstrapCommand) Run(_ *cmd.Context) error { envCfg, err := config.New(config.NoDefaults, c.EnvConfig) if err != nil { return err } err = c.ReadConfig("machine-0") if err != nil { return err } agentConfig := c.CurrentConfig() // agent.Jobs is an optional field in the agent config, and was // introduced after 1.17.2. We default to allowing units on // machine-0 if missing. jobs := agentConfig.Jobs() if len(jobs) == 0 { jobs = []params.MachineJob{ params.JobManageEnviron, params.JobHostUnits, } } // Get the bootstrap machine's addresses from the provider. env, err := environs.New(envCfg) if err != nil { return err } instanceId := instance.Id(c.InstanceId) instances, err := env.Instances([]instance.Id{instanceId}) if err != nil { return err } addrs, err := instances[0].Addresses() if err != nil { return err } // Create system-identity file if err := agent.WriteSystemIdentityFile(agentConfig); err != nil { return err } // Generate a shared secret for the Mongo replica set, and write it out. sharedSecret, err := mongo.GenerateSharedSecret() if err != nil { return err } info, ok := agentConfig.StateServingInfo() if !ok { return fmt.Errorf("bootstrap machine config has no state serving info") } info.SharedSecret = sharedSecret err = c.ChangeConfig(func(agentConfig agent.ConfigSetter) { agentConfig.SetStateServingInfo(info) }) if err != nil { return fmt.Errorf("cannot write agent config: %v", err) } agentConfig = c.CurrentConfig() if err := c.startMongo(addrs, agentConfig); err != nil { return err } logger.Infof("started mongo") // Initialise state, and store any agent config (e.g. password) changes. var st *state.State var m *state.Machine err = nil writeErr := c.ChangeConfig(func(agentConfig agent.ConfigSetter) { st, m, err = agent.InitializeState( agentConfig, envCfg, agent.BootstrapMachineConfig{ Addresses: addrs, Constraints: c.Constraints, Jobs: jobs, InstanceId: instanceId, Characteristics: c.Hardware, SharedSecret: sharedSecret, }, state.DefaultDialOpts(), environs.NewStatePolicy(), ) }) if writeErr != nil { return fmt.Errorf("cannot write initial configuration: %v", err) } if err != nil { return err } defer st.Close() // bootstrap machine always gets the vote return m.SetHasVote(true) }