func (s *syncSuite) TestSyncing(c *gc.C) { for i, test := range tests { // Perform all tests in a "clean" environment. func() { s.setUpTest(c) defer s.tearDownTest(c) c.Logf("test %d: %s", i, test.description) if test.source { test.ctx.Source = s.localStorage } if test.version != version.Zero { version.Current.Number = test.version } if test.major > 0 { test.ctx.MajorVersion = test.major test.ctx.MinorVersion = test.minor } stor := s.targetEnv.Storage() test.ctx.TargetToolsFinder = sync.StorageToolsFinder{stor} test.ctx.TargetToolsUploader = sync.StorageToolsUploader{stor, true, false} err := sync.SyncTools(test.ctx) c.Assert(err, gc.IsNil) targetTools, err := envtools.FindTools( s.targetEnv, test.ctx.MajorVersion, test.ctx.MinorVersion, coretools.Filter{}, envtools.DoNotAllowRetry) c.Assert(err, gc.IsNil) assertToolsList(c, targetTools, test.tools) assertNoUnexpectedTools(c, s.targetEnv.Storage()) assertMirrors(c, s.targetEnv.Storage(), test.expectMirrors) }() } }
func (s *SimpleStreamsToolsSuite) TestFindToolsFiltering(c *gc.C) { var tw loggo.TestWriter c.Assert(loggo.RegisterWriter("filter-tester", &tw, loggo.TRACE), gc.IsNil) defer loggo.RemoveWriter("filter-tester") logger := loggo.GetLogger("juju.environs") defer logger.SetLogLevel(logger.LogLevel()) logger.SetLogLevel(loggo.TRACE) _, err := envtools.FindTools( s.env, 1, -1, "released", coretools.Filter{Number: version.Number{Major: 1, Minor: 2, Patch: 3}}) c.Assert(err, jc.Satisfies, errors.IsNotFound) // This is slightly overly prescriptive, but feel free to change or add // messages. This still helps to ensure that all log messages are // properly formed. messages := []jc.SimpleMessage{ {loggo.INFO, "reading tools with major version 1"}, {loggo.INFO, "filtering tools by version: \\d+\\.\\d+\\.\\d+"}, {loggo.TRACE, "no architecture specified when finding tools, looking for "}, {loggo.TRACE, "no series specified when finding tools, looking for \\[.*\\]"}, } sources, err := envtools.GetMetadataSources(s.env) c.Assert(err, jc.ErrorIsNil) for i := 0; i < 2*len(sources); i++ { messages = append(messages, jc.SimpleMessage{loggo.TRACE, `fetchData failed for .*`}, jc.SimpleMessage{loggo.TRACE, `cannot load index .*`}) } c.Check(tw.Log(), jc.LogMatches, messages) }
func (s *BootstrapSuite) TestAutoUploadAfterFailedSync(c *gc.C) { s.PatchValue(&version.Current.Series, config.LatestLtsSeries()) otherSeries := "quantal" env := s.setupAutoUploadTest(c, "1.7.3", otherSeries) // Run command and check for that upload has been run for tools matching the current juju version. opc, errc := runCommand(nullContext(c), envcmd.Wrap(new(BootstrapCommand))) c.Assert(<-errc, gc.IsNil) c.Assert((<-opc).(dummy.OpPutFile).Env, gc.Equals, "peckham") list, err := envtools.FindTools(env, version.Current.Major, version.Current.Minor, coretools.Filter{}, false) c.Assert(err, gc.IsNil) c.Logf("found: " + list.String()) urls := list.URLs() // We expect: // supported LTS series precise, trusty, // the specified series (quantal), // and the environment's default series (raring). expectedVers := []version.Binary{ version.MustParseBinary(fmt.Sprintf("1.7.3.1-%s-%s", "quantal", version.Current.Arch)), version.MustParseBinary(fmt.Sprintf("1.7.3.1-%s-%s", "raring", version.Current.Arch)), version.MustParseBinary(fmt.Sprintf("1.7.3.1-%s-%s", "precise", version.Current.Arch)), version.MustParseBinary(fmt.Sprintf("1.7.3.1-%s-%s", "trusty", version.Current.Arch)), } c.Assert(urls, gc.HasLen, len(expectedVers)) for _, vers := range expectedVers { c.Logf("seeking: " + vers.String()) _, found := urls[vers] c.Check(found, gc.Equals, true) } }
// processGet handles a tools GET request. func (h *toolsDownloadHandler) processGet(r *http.Request) (*tools.Tools, utils.SSLHostnameVerification, error) { version, err := version.ParseBinary(r.URL.Query().Get(":version")) if err != nil { return nil, false, err } cfg, err := h.state.EnvironConfig() if err != nil { return nil, false, err } env, err := environs.New(cfg) if err != nil { return nil, false, err } filter := tools.Filter{ Number: version.Number, Series: version.Series, Arch: version.Arch, } tools, err := envtools.FindTools(env, version.Major, version.Minor, filter, false) if err != nil { return nil, false, errors.Annotate(err, "failed to find tools") } verify := utils.SSLHostnameVerification(cfg.SSLHostnameVerification()) return tools[0], verify, nil }
func (s *SimpleStreamsToolsSuite) TestFindTools(c *gc.C) { for i, test := range findToolsTests { c.Logf("\ntest %d: %s", i, test.info) s.reset(c, nil) custom := s.uploadCustom(c, test.custom...) public := s.uploadPublic(c, test.public...) stream := envtools.PreferredStream(&version.Current, s.env.Config().Development(), s.env.Config().AgentStream()) actual, err := envtools.FindTools(s.env, test.major, test.minor, stream, coretools.Filter{}) if test.err != nil { if len(actual) > 0 { c.Logf(actual.String()) } c.Check(err, jc.Satisfies, errors.IsNotFound) continue } expect := map[version.Binary]string{} for _, expected := range test.expect { // If the tools exist in custom, that's preferred. var ok bool if expect[expected], ok = custom[expected]; !ok { expect[expected] = public[expected] } } c.Check(actual.URLs(), gc.DeepEquals, expect) } }
func (s *SimpleStreamsToolsSuite) TestFindToolsFiltering(c *gc.C) { tw := &loggo.TestWriter{} c.Assert(loggo.RegisterWriter("filter-tester", tw, loggo.DEBUG), gc.IsNil) defer loggo.RemoveWriter("filter-tester") _, err := envtools.FindTools( s.env, 1, -1, coretools.Filter{Number: version.Number{Major: 1, Minor: 2, Patch: 3}}, envtools.DoNotAllowRetry) c.Assert(err, jc.Satisfies, errors.IsNotFound) // This is slightly overly prescriptive, but feel free to change or add // messages. This still helps to ensure that all log messages are // properly formed. messages := []jc.SimpleMessage{ {loggo.INFO, "reading tools with major version 1"}, {loggo.INFO, "filtering tools by version: \\d+\\.\\d+\\.\\d+"}, {loggo.DEBUG, "no architecture specified when finding tools, looking for any"}, {loggo.DEBUG, "no series specified when finding tools, looking for any"}, } sources, err := envtools.GetMetadataSources(s.env) c.Assert(err, gc.IsNil) for i := 0; i < 2*len(sources); i++ { messages = append(messages, jc.SimpleMessage{loggo.DEBUG, `fetchData failed for .*`}, jc.SimpleMessage{loggo.DEBUG, `cannot load index .*`}) } c.Check(tw.Log, jc.LogMatches, messages) }
// StartInstanceWithParams is a test helper function that starts an instance // with the given parameters, and a plausible but invalid configuration, and // returns the result of Environ.StartInstance. The provided params's // InstanceConfig and Tools field values will be ignored. func StartInstanceWithParams( env environs.Environ, machineId string, params environs.StartInstanceParams, networks []string, ) ( *environs.StartInstanceResult, error, ) { preferredSeries := config.PreferredSeries(env.Config()) agentVersion, ok := env.Config().AgentVersion() if !ok { return nil, errors.New("missing agent version in model config") } filter := coretools.Filter{ Number: agentVersion, Series: preferredSeries, } if params.Constraints.Arch != nil { filter.Arch = *params.Constraints.Arch } stream := tools.PreferredStream(&agentVersion, env.Config().Development(), env.Config().AgentStream()) possibleTools, err := tools.FindTools(env, -1, -1, stream, filter) if err != nil { return nil, errors.Trace(err) } if params.ImageMetadata == nil { if err := SetImageMetadata( env, possibleTools.AllSeries(), possibleTools.Arches(), ¶ms.ImageMetadata, ); err != nil { return nil, errors.Trace(err) } } machineNonce := "fake_nonce" stateInfo := FakeStateInfo(machineId) apiInfo := FakeAPIInfo(machineId) instanceConfig, err := instancecfg.NewInstanceConfig( machineId, machineNonce, imagemetadata.ReleasedStream, preferredSeries, "", true, networks, stateInfo, apiInfo, ) if err != nil { return nil, errors.Trace(err) } eUUID, _ := env.Config().UUID() instanceConfig.Tags[tags.JujuModel] = eUUID params.Tools = possibleTools params.InstanceConfig = instanceConfig return env.StartInstance(params) }
// checkTools check if the environment contains the passed envtools. func checkTools(c *gc.C, env environs.Environ, expected []version.Binary) { list, err := envtools.FindTools( env, version.Current.Major, version.Current.Minor, "released", coretools.Filter{}) c.Check(err, jc.ErrorIsNil) c.Logf("found: " + list.String()) urls := list.URLs() c.Check(urls, gc.HasLen, len(expected)) }
// findTools1dot17 allows 1.17.x versions to be upgraded. func findTools1dot17(cfg *config.Config) (coretools.List, error) { logger.Warningf("running find tools in 1.17 compatibility mode") env, err := environs.New(cfg) if err != nil { return nil, err } clientVersion := version.Current.Number return envtools.FindTools(env, clientVersion.Major, -1, coretools.Filter{}, envtools.DoNotAllowRetry) }
func (s *SimpleStreamsToolsSuite) TestFindToolsInControlBucket(c *gc.C) { s.reset(c, nil) custom := toolstesting.UploadToStorage(c, s.env.Storage(), envtesting.V110p...) s.uploadPublic(c, envtesting.VAll...) actual, err := envtools.FindTools(s.env, 1, 1, coretools.Filter{}, envtools.DoNotAllowRetry) c.Assert(err, gc.IsNil) expect := map[version.Binary]string{} for _, expected := range envtesting.V110p { expect[expected] = custom[expected] } c.Assert(actual.URLs(), gc.DeepEquals, expect) }
func (s *BootstrapSuite) TestInvalidLocalSource(c *gc.C) { s.PatchValue(&version.Current.Number, version.MustParse("1.2.0")) env := resetJujuHome(c, "devenv") // Bootstrap the environment with an invalid source. // The command returns with an error. _, err := coretesting.RunCommand(c, envcmd.Wrap(&BootstrapCommand{}), "--metadata-source", c.MkDir()) c.Check(err, gc.ErrorMatches, `failed to bootstrap environment: Juju cannot bootstrap because no tools are available for your environment(.|\n)*`) // Now check that there are no tools available. _, err = envtools.FindTools( env, version.Current.Major, version.Current.Minor, "released", coretools.Filter{}) c.Assert(err, gc.FitsTypeOf, errors.NotFoundf("")) }
// StartInstanceWithParams is a test helper function that starts an instance // with the given parameters, and a plausible but invalid configuration, and // returns the result of Environ.StartInstance. The provided params's // MachineConfig and Tools field values will be ignored. func StartInstanceWithParams( env environs.Environ, machineId string, params environs.StartInstanceParams, networks []string, ) ( instance.Instance, *instance.HardwareCharacteristics, []network.Info, error, ) { series := config.PreferredSeries(env.Config()) agentVersion, ok := env.Config().AgentVersion() if !ok { return nil, nil, nil, fmt.Errorf("missing agent version in environment config") } filter := coretools.Filter{ Number: agentVersion, Series: series, } if params.Constraints.Arch != nil { filter.Arch = *params.Constraints.Arch } possibleTools, err := tools.FindTools( env, -1, -1, filter, tools.DoNotAllowRetry, ) if err != nil { return nil, nil, nil, err } machineNonce := "fake_nonce" stateInfo := FakeStateInfo(machineId) apiInfo := FakeAPIInfo(machineId) machineConfig, err := environs.NewMachineConfig( machineId, machineNonce, imagemetadata.ReleasedStream, series, networks, stateInfo, apiInfo, ) if err != nil { return nil, nil, nil, err } params.Tools = possibleTools params.MachineConfig = machineConfig return env.StartInstance(params) }
func (s *ProvisionerSuite) TestPossibleTools(c *gc.C) { storageDir := c.MkDir() s.PatchValue(&tools.DefaultBaseURL, storageDir) stor, err := filestorage.NewFileStorageWriter(storageDir) c.Assert(err, jc.ErrorIsNil) // Set a current version that does not match the // agent-version in the environ config. currentVersion := version.MustParseBinary("1.2.3-quantal-arm64") s.PatchValue(&arch.HostArch, func() string { return currentVersion.Arch }) s.PatchValue(&series.HostSeries, func() string { return currentVersion.Series }) s.PatchValue(&version.Current, currentVersion.Number) // Upload some plausible matches, and some that should be filtered out. compatibleVersion := version.MustParseBinary("1.2.3-quantal-amd64") ignoreVersion1 := version.MustParseBinary("1.2.4-quantal-arm64") ignoreVersion2 := version.MustParseBinary("1.2.3-precise-arm64") availableVersions := []version.Binary{ currentVersion, compatibleVersion, ignoreVersion1, ignoreVersion2, } envtesting.AssertUploadFakeToolsVersions(c, stor, s.cfg.AgentStream(), s.cfg.AgentStream(), availableVersions...) // Extract the tools that we expect to actually match. expectedList, err := tools.FindTools(s.Environ, -1, -1, s.cfg.AgentStream(), coretools.Filter{ Number: currentVersion.Number, Series: currentVersion.Series, }) c.Assert(err, jc.ErrorIsNil) // Create the machine and check the tools that get passed into StartInstance. machine, err := s.BackingState.AddOneMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }) c.Assert(err, jc.ErrorIsNil) provisioner := s.newEnvironProvisioner(c) defer stop(c, provisioner) s.checkStartInstanceCustom( c, machine, "pork", constraints.Value{}, nil, nil, nil, nil, false, expectedList, true, ) }
// StartInstanceWithParams is a test helper function that starts an instance // with the given parameters, and a plausible but invalid configuration, and // returns the result of Environ.StartInstance. The provided params's // InstanceConfig and Tools field values will be ignored. func StartInstanceWithParams( env environs.Environ, machineId string, params environs.StartInstanceParams, networks []string, ) ( *environs.StartInstanceResult, error, ) { series := config.PreferredSeries(env.Config()) agentVersion, ok := env.Config().AgentVersion() if !ok { return nil, errors.New("missing agent version in environment config") } filter := coretools.Filter{ Number: agentVersion, Series: series, } if params.Constraints.Arch != nil { filter.Arch = *params.Constraints.Arch } possibleTools, err := tools.FindTools(env, -1, -1, filter) if err != nil { return nil, errors.Trace(err) } machineNonce := "fake_nonce" stateInfo := FakeStateInfo(machineId) apiInfo := FakeAPIInfo(machineId) instanceConfig, err := instancecfg.NewInstanceConfig( machineId, machineNonce, imagemetadata.ReleasedStream, series, true, networks, stateInfo, apiInfo, ) if err != nil { return nil, errors.Trace(err) } params.Tools = possibleTools params.InstanceConfig = instanceConfig return env.StartInstance(params) }
// 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 }
func (s *ProvisionerSuite) TestPossibleTools(c *gc.C) { // Clear out all tools, and set a current version that does not match the // agent-version in the environ config. envStorage := s.Environ.Storage() envtesting.RemoveFakeTools(c, envStorage) currentVersion := version.MustParseBinary("1.2.3-quantal-arm64") s.PatchValue(&version.Current, currentVersion) // Upload some plausible matches, and some that should be filtered out. compatibleVersion := version.MustParseBinary("1.2.3-quantal-amd64") ignoreVersion1 := version.MustParseBinary("1.2.4-quantal-arm64") ignoreVersion2 := version.MustParseBinary("1.2.3-precise-arm64") availableVersions := []version.Binary{ currentVersion, compatibleVersion, ignoreVersion1, ignoreVersion2, } envtesting.AssertUploadFakeToolsVersions(c, envStorage, availableVersions...) // Extract the tools that we expect to actually match. expectedList, err := tools.FindTools(s.Environ, -1, -1, coretools.Filter{ Number: currentVersion.Number, Series: currentVersion.Series, }, tools.DoNotAllowRetry) c.Assert(err, gc.IsNil) // Create the machine and check the tools that get passed into StartInstance. machine, err := s.BackingState.AddOneMachine(state.MachineTemplate{ Series: "quantal", Jobs: []state.MachineJob{state.JobHostUnits}, }) c.Assert(err, gc.IsNil) provisioner := s.newEnvironProvisioner(c) defer stop(c, provisioner) s.checkStartInstanceCustom( c, machine, "pork", constraints.Value{}, nil, nil, expectedList, true, ) }
func (s *provisionerSuite) TestFindTools(c *gc.C) { list, err := envtools.FindTools(s.Environ, -1, -1, coretools.Filter{ Number: version.Current.Number, Series: version.Current.Series, Arch: version.Current.Arch, }, false) c.Assert(err, gc.IsNil) apiList, err := s.provisioner.FindTools(version.Current.Number, version.Current.Series, &version.Current.Arch) c.Assert(err, gc.IsNil) c.Assert(apiList, gc.HasLen, len(list)) listStrings := make([]string, len(list)) for i, tools := range list { listStrings[i] = fmt.Sprintf("%+v", tools) } apiListStrings := make([]string, len(apiList)) for i, tools := range apiList { apiListStrings[i] = fmt.Sprintf("%+v", tools) } c.Assert(apiListStrings, jc.SameContents, listStrings) }
func fillinStartInstanceParams(env environs.Environ, machineId string, isController bool, params *environs.StartInstanceParams) error { if params.ControllerUUID == "" { return errors.New("missing controller UUID in start instance parameters") } preferredSeries := config.PreferredSeries(env.Config()) agentVersion, ok := env.Config().AgentVersion() if !ok { return errors.New("missing agent version in model config") } filter := coretools.Filter{ Number: agentVersion, Series: preferredSeries, } if params.Constraints.Arch != nil { filter.Arch = *params.Constraints.Arch } stream := tools.PreferredStream(&agentVersion, env.Config().Development(), env.Config().AgentStream()) possibleTools, err := tools.FindTools(env, -1, -1, stream, filter) if err != nil { return errors.Trace(err) } if params.ImageMetadata == nil { if err := SetImageMetadata( env, possibleTools.AllSeries(), possibleTools.Arches(), ¶ms.ImageMetadata, ); err != nil { return errors.Trace(err) } } machineNonce := "fake_nonce" apiInfo := FakeAPIInfo(machineId) instanceConfig, err := instancecfg.NewInstanceConfig( testing.ControllerTag, machineId, machineNonce, imagemetadata.ReleasedStream, preferredSeries, apiInfo, ) if err != nil { return errors.Trace(err) } if isController { instanceConfig.Controller = &instancecfg.ControllerConfig{ Config: testing.FakeControllerConfig(), MongoInfo: &mongo.MongoInfo{ Info: mongo.Info{ Addrs: []string{"127.0.0.1:1234"}, CACert: "CA CERT\n" + testing.CACert, }, Password: "******", Tag: names.NewMachineTag(machineId), }, } instanceConfig.Jobs = []multiwatcher.MachineJob{multiwatcher.JobHostUnits, multiwatcher.JobManageModel} } cfg := env.Config() instanceConfig.Tags = instancecfg.InstanceTags(env.Config().UUID(), params.ControllerUUID, cfg, nil) params.Tools = possibleTools params.InstanceConfig = instanceConfig return nil }
// Run initializes state for an environment. func (c *BootstrapCommand) Run(_ *cmd.Context) error { envCfg, err := config.New(config.NoDefaults, c.ControllerModelConfig) if err != nil { return err } err = c.ReadConfig("machine-0") if err != nil { return err } agentConfig := c.CurrentConfig() network.SetPreferIPv6(agentConfig.PreferIPv6()) // 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 = []multiwatcher.MachineJob{ multiwatcher.JobManageModel, multiwatcher.JobHostUnits, multiwatcher.JobManageNetworking, } } // Get the bootstrap machine's addresses from the provider. env, err := environs.New(envCfg) if err != nil { return err } newConfigAttrs := make(map[string]interface{}) // Check to see if a newer agent version has been requested // by the bootstrap client. desiredVersion, ok := envCfg.AgentVersion() if ok && desiredVersion != jujuversion.Current { // If we have been asked for a newer version, ensure the newer // tools can actually be found, or else bootstrap won't complete. stream := envtools.PreferredStream(&desiredVersion, envCfg.Development(), envCfg.AgentStream()) logger.Infof("newer tools requested, looking for %v in stream %v", desiredVersion, stream) filter := tools.Filter{ Number: desiredVersion, Arch: arch.HostArch(), Series: series.HostSeries(), } _, toolsErr := envtools.FindTools(env, -1, -1, stream, filter) if toolsErr == nil { logger.Infof("tools are available, upgrade will occur after bootstrap") } if errors.IsNotFound(toolsErr) { // Newer tools not available, so revert to using the tools // matching the current agent version. logger.Warningf("newer tools for %q not available, sticking with version %q", desiredVersion, jujuversion.Current) newConfigAttrs["agent-version"] = jujuversion.Current.String() } else if toolsErr != nil { logger.Errorf("cannot find newer tools: %v", toolsErr) return toolsErr } } 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 } // When machine addresses are reported from state, they have // duplicates removed. We should do the same here so that // there is not unnecessary churn in the mongo replicaset. // TODO (cherylj) Add explicit unit tests for this - tracked // by bug #1544158. addrs = network.MergedAddresses([]network.Address{}, addrs) // Generate a private SSH key for the controllers, and add // the public key to the environment config. We'll add the // private key to StateServingInfo below. privateKey, publicKey, err := sshGenerateKey(config.JujuSystemKey) if err != nil { return errors.Annotate(err, "failed to generate system key") } authorizedKeys := config.ConcatAuthKeys(envCfg.AuthorizedKeys(), publicKey) newConfigAttrs[config.AuthKeysConfig] = authorizedKeys // 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 info.SystemIdentity = privateKey err = c.ChangeConfig(func(agentConfig agent.ConfigSetter) error { agentConfig.SetStateServingInfo(info) return nil }) if err != nil { return fmt.Errorf("cannot write agent config: %v", err) } err = c.ChangeConfig(func(config agent.ConfigSetter) error { // We'll try for mongo 3.2 first and fallback to // mongo 2.4 if the newer binaries are not available. if mongo.BinariesAvailable(mongo.Mongo32wt) { config.SetMongoVersion(mongo.Mongo32wt) } else { config.SetMongoVersion(mongo.Mongo24) } return nil }) if err != nil { return errors.Annotate(err, "cannot set mongo version") } agentConfig = c.CurrentConfig() // Create system-identity file if err := agent.WriteSystemIdentityFile(agentConfig); err != nil { return err } 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. envCfg, err = env.Config().Apply(newConfigAttrs) if err != nil { return errors.Annotate(err, "failed to update model config") } var st *state.State var m *state.Machine err = c.ChangeConfig(func(agentConfig agent.ConfigSetter) error { var stateErr error dialOpts := mongo.DefaultDialOpts() // Set a longer socket timeout than usual, as the machine // will be starting up and disk I/O slower than usual. This // has been known to cause timeouts in queries. timeouts := envCfg.BootstrapSSHOpts() dialOpts.SocketTimeout = timeouts.Timeout if dialOpts.SocketTimeout < minSocketTimeout { dialOpts.SocketTimeout = minSocketTimeout } // We shouldn't attempt to dial peers until we have some. dialOpts.Direct = true adminTag := names.NewLocalUserTag(c.AdminUsername) st, m, stateErr = agentInitializeState( adminTag, agentConfig, envCfg, c.HostedModelConfig, agentbootstrap.BootstrapMachineConfig{ Addresses: addrs, BootstrapConstraints: c.BootstrapConstraints, ModelConstraints: c.ModelConstraints, Jobs: jobs, InstanceId: instanceId, Characteristics: c.Hardware, SharedSecret: sharedSecret, }, dialOpts, environs.NewStatePolicy(), ) return stateErr }) if err != nil { return err } defer st.Close() // Populate the tools catalogue. if err := c.populateTools(st, env); err != nil { return err } // Populate the GUI archive catalogue. if err := c.populateGUIArchive(st, env); err != nil { // Do not stop the bootstrapping process for Juju GUI archive errors. logger.Warningf("cannot set up Juju GUI: %s", err) } else { logger.Debugf("Juju GUI successfully set up") } // Add custom image metadata to environment storage. if c.ImageMetadataDir != "" { if err := c.saveCustomImageMetadata(st, env); err != nil { return err } stor := newStateStorage(st.ModelUUID(), st.MongoSession()) if err := c.storeCustomImageMetadata(stor); err != nil { return err } } // Populate the storage pools. if err = c.populateDefaultStoragePools(st); err != nil { return err } // bootstrap machine always gets the vote return m.SetHasVote(true) }
func (test bootstrapTest) run(c *gc.C) { // Create home with dummy provider and remove all // of its envtools. env := resetJujuHome(c) if test.version != "" { useVersion := strings.Replace(test.version, "%LTS%", config.LatestLtsSeries(), 1) origVersion := version.Current version.Current = version.MustParseBinary(useVersion) defer func() { version.Current = origVersion }() } if test.hostArch != "" { origVersion := arch.HostArch arch.HostArch = func() string { return test.hostArch } defer func() { arch.HostArch = origVersion }() } uploadCount := len(test.uploads) if uploadCount == 0 { usefulVersion := version.Current usefulVersion.Series = config.PreferredSeries(env.Config()) envtesting.AssertUploadFakeToolsVersions(c, env.Storage(), usefulVersion) } // Run command and check for uploads. opc, errc := runCommand(nullContext(c), envcmd.Wrap(new(BootstrapCommand)), test.args...) // Check for remaining operations/errors. if test.err != "" { err := <-errc stripped := strings.Replace(err.Error(), "\n", "", -1) c.Check(stripped, gc.Matches, test.err) return } if !c.Check(<-errc, gc.IsNil) { return } if uploadCount > 0 { for i := 0; i < uploadCount; i++ { c.Check((<-opc).(dummy.OpPutFile).Env, gc.Equals, "peckham") } list, err := envtools.FindTools( env, version.Current.Major, version.Current.Minor, coretools.Filter{}, envtools.DoNotAllowRetry) c.Check(err, gc.IsNil) c.Logf("found: " + list.String()) urls := list.URLs() c.Check(urls, gc.HasLen, len(test.uploads)) for _, v := range test.uploads { v := strings.Replace(v, "%LTS%", config.LatestLtsSeries(), 1) c.Logf("seeking: " + v) vers := version.MustParseBinary(v) _, found := urls[vers] c.Check(found, gc.Equals, true) } } if len(test.uploads) > 0 { indexFile := (<-opc).(dummy.OpPutFile) c.Check(indexFile.FileName, gc.Equals, "tools/streams/v1/index.json") productFile := (<-opc).(dummy.OpPutFile) c.Check(productFile.FileName, gc.Equals, "tools/streams/v1/com.ubuntu.juju:released:tools.json") } opPutBootstrapVerifyFile := (<-opc).(dummy.OpPutFile) c.Check(opPutBootstrapVerifyFile.Env, gc.Equals, "peckham") c.Check(opPutBootstrapVerifyFile.FileName, gc.Equals, environs.VerificationFilename) opPutBootstrapInitFile := (<-opc).(dummy.OpPutFile) c.Check(opPutBootstrapInitFile.Env, gc.Equals, "peckham") c.Check(opPutBootstrapInitFile.FileName, gc.Equals, "provider-state") opBootstrap := (<-opc).(dummy.OpBootstrap) c.Check(opBootstrap.Env, gc.Equals, "peckham") c.Check(opBootstrap.Args.Constraints, gc.DeepEquals, test.constraints) c.Check(opBootstrap.Args.Placement, gc.Equals, test.placement) store, err := configstore.Default() c.Assert(err, gc.IsNil) // Check a CA cert/key was generated by reloading the environment. env, err = environs.NewFromName("peckham", store) c.Assert(err, gc.IsNil) _, hasCert := env.Config().CACert() c.Check(hasCert, gc.Equals, true) _, hasKey := env.Config().CAPrivateKey() c.Check(hasKey, gc.Equals, true) }