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) } }
// resolveCharmStoreEntityURL resolves the given charm or bundle URL string // by looking it up in the appropriate charm repository. // If it is a charm store URL, the given csParams will // be used to access the charm store repository. // If it is a local charm or bundle URL, the local charm repository at // the given repoPath will be used. The given configuration // will be used to add any necessary attributes to the repo // and to return the charm's supported series if possible. // // resolveCharmStoreEntityURL also returns the charm repository holding // the charm or bundle. func resolveCharmStoreEntityURL(args resolveCharmStoreEntityParams) (*charm.URL, []string, charmrepo.Interface, error) { url, err := charm.ParseURL(args.urlStr) if err != nil { return nil, nil, nil, errors.Trace(err) } repo, err := charmrepo.InferRepository(url, args.csParams, args.repoPath) if err != nil { return nil, nil, nil, errors.Trace(err) } repo = config.SpecializeCharmRepo(repo, args.conf) if url.Schema == "local" && url.Series == "" { if defaultSeries, ok := args.conf.DefaultSeries(); ok { url.Series = defaultSeries } if url.Series == "" { possibleURL := *url possibleURL.Series = config.LatestLtsSeries() logger.Errorf("The series is not specified in the model (default-series) or with the charm. Did you mean:\n\t%s", &possibleURL) return nil, nil, nil, errors.Errorf("cannot resolve series for charm: %q", url) } } resultUrl, supportedSeries, err := repo.Resolve(url) if err != nil { return nil, nil, nil, errors.Trace(err) } return resultUrl, supportedSeries, repo, nil }
func (s *BootstrapSuite) runAllowRetriesTest(c *gc.C, test bootstrapRetryTest) { toolsVersions := envtesting.VAll if test.version != "" { useVersion := strings.Replace(test.version, "%LTS%", config.LatestLtsSeries(), 1) testVersion := version.MustParseBinary(useVersion) s.PatchValue(&version.Current, testVersion) if test.addVersionToSource { toolsVersions = append([]version.Binary{}, toolsVersions...) toolsVersions = append(toolsVersions, testVersion) } } resetJujuHome(c) sourceDir := createToolsSource(c, toolsVersions) s.PatchValue(&envtools.DefaultBaseURL, sourceDir) var findToolsRetryValues []bool mockFindTools := func(cloudInst environs.ConfigGetter, majorVersion, minorVersion int, filter coretools.Filter, allowRetry bool) (list coretools.List, err error) { findToolsRetryValues = append(findToolsRetryValues, allowRetry) return nil, errors.NotFoundf("tools") } restore := envtools.TestingPatchBootstrapFindTools(mockFindTools) defer restore() _, errc := runCommand(nullContext(c), envcmd.Wrap(new(BootstrapCommand)), test.args...) err := <-errc c.Check(findToolsRetryValues, gc.DeepEquals, test.expectedAllowRetry) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Check(stripped, gc.Matches, test.err) }
func (s *ImageMetadataSuite) TestImageMetadataFilesLatestLts(c *gc.C) { ec2Config, err := config.New(config.UseDefaults, map[string]interface{}{ "name": "ec2-latest-lts", "type": "ec2", "uuid": testing.ModelTag.Id(), "controller-uuid": testing.ModelTag.Id(), "region": "us-east-1", }) c.Assert(err, jc.ErrorIsNil) s.store.BootstrapConfig["ec2-controller"] = jujuclient.BootstrapConfig{ Cloud: "ec2", CloudRegion: "us-east-1", Config: ec2Config.AllAttrs(), } ctx, err := runImageMetadata(c, s.store, "-m", "ec2-controller:ec2-latest-lts", "-d", s.dir, "-i", "1234", "-r", "region", "-a", "arch", "-u", "endpoint", ) c.Assert(err, jc.ErrorIsNil) out := testing.Stdout(ctx) expected := expectedMetadata{ series: config.LatestLtsSeries(), arch: "arch", } s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) }
func (s *funcSuite) SetUpTest(c *gc.C) { s.baseImageMetadataSuite.SetUpTest(c) var err error s.env, err = environs.Prepare( envtesting.BootstrapContext(c), jujuclienttesting.NewMemStore(), environs.PrepareParams{ ControllerName: "dummycontroller", BaseConfig: mockConfig(), CloudName: "dummy", }, ) c.Assert(err, jc.ErrorIsNil) s.state = s.constructState(s.env.Config()) s.expected = cloudimagemetadata.Metadata{ cloudimagemetadata.MetadataAttributes{ Stream: "released", Source: "custom", Series: config.LatestLtsSeries(), Arch: "amd64", Region: "dummy_region", }, 0, "", } }
func (s *UpgradeJujuSuite) TestUpgradeJuju(c *gc.C) { for i, test := range upgradeJujuTests { c.Logf("\ntest %d: %s", i, test.about) s.Reset(c) tools.DefaultBaseURL = "" // Set up apparent CLI version and initialize the command. s.PatchValue(&version.Current, version.MustParseBinary(test.currentVersion)) com := &UpgradeJujuCommand{} if err := coretesting.InitCommand(envcmd.Wrap(com), test.args); err != nil { if test.expectInitErr != "" { c.Check(err, gc.ErrorMatches, test.expectInitErr) } else { c.Check(err, jc.ErrorIsNil) } continue } // Set up state and environ, and run the command. toolsDir := c.MkDir() updateAttrs := map[string]interface{}{ "agent-version": test.agentVersion, "agent-metadata-url": "file://" + toolsDir + "/tools", } err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil) c.Assert(err, jc.ErrorIsNil) versions := make([]version.Binary, len(test.tools)) for i, v := range test.tools { versions[i] = version.MustParseBinary(v) } if len(versions) > 0 { stor, err := filestorage.NewFileStorageWriter(toolsDir) c.Assert(err, jc.ErrorIsNil) envtesting.MustUploadFakeToolsVersions(stor, s.Environ.Config().AgentStream(), versions...) } err = com.Run(coretesting.Context(c)) if test.expectErr != "" { c.Check(err, gc.ErrorMatches, test.expectErr) continue } else if !c.Check(err, jc.ErrorIsNil) { continue } // Check expected changes to environ/state. cfg, err := s.State.EnvironConfig() c.Check(err, jc.ErrorIsNil) agentVersion, ok := cfg.AgentVersion() c.Check(ok, jc.IsTrue) c.Check(agentVersion, gc.Equals, version.MustParse(test.expectVersion)) for _, uploaded := range test.expectUploaded { // Substitute latest LTS for placeholder in expected series for uploaded tools uploaded = strings.Replace(uploaded, "%LTS%", config.LatestLtsSeries(), 1) vers := version.MustParseBinary(uploaded) s.checkToolsUploaded(c, vers, agentVersion) } } }
func (s *RepoSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.CharmsPath = c.MkDir() // Change the environ's config to ensure we're using the one in state. updateAttrs := map[string]interface{}{"default-series": config.LatestLtsSeries()} err := s.State.UpdateModelConfig(updateAttrs, nil, nil) c.Assert(err, jc.ErrorIsNil) }
// setParams sets parameters based on the environment configuration // for those which have not been explicitly specified. func (c *imageMetadataCommand) setParams(context *cmd.Context) error { c.privateStorage = "<private storage name>" var environ environs.Environ if environ, err := c.prepare(context); err == nil { logger.Infof("creating image metadata for model %q", environ.Config().Name()) // If the user has not specified region and endpoint, try and get it from the environment. if c.Region == "" || c.Endpoint == "" { var cloudSpec simplestreams.CloudSpec if inst, ok := environ.(simplestreams.HasRegion); ok { if cloudSpec, err = inst.Region(); err != nil { return err } } else { return errors.Errorf("model %q cannot provide region and endpoint", environ.Config().Name()) } // If only one of region or endpoint is provided, that is a problem. if cloudSpec.Region != cloudSpec.Endpoint && (cloudSpec.Region == "" || cloudSpec.Endpoint == "") { return errors.Errorf("cannot generate metadata without a complete cloud configuration") } if c.Region == "" { c.Region = cloudSpec.Region } if c.Endpoint == "" { c.Endpoint = cloudSpec.Endpoint } } cfg := environ.Config() if c.Series == "" { c.Series = config.PreferredSeries(cfg) } } else { logger.Warningf("model could not be opened: %v", err) } if environ == nil { logger.Infof("no model found, creating image metadata using user supplied data") } if c.Series == "" { c.Series = config.LatestLtsSeries() } if c.ImageId == "" { return errors.Errorf("image id must be specified") } if c.Region == "" { return errors.Errorf("image region must be specified") } if c.Endpoint == "" { return errors.Errorf("cloud endpoint URL must be specified") } if c.Dir == "" { logger.Infof("no destination directory specified, using current directory") var err error if c.Dir, err = os.Getwd(); err != nil { return err } } return nil }
func (s *NewAPIClientSuite) TestWithConfigAndNoInfo(c *gc.C) { coretesting.MakeSampleJujuHome(c) store := newConfigStore(coretesting.SampleEnvName, &environInfo{ bootstrapConfig: map[string]interface{}{ "type": "dummy", "name": "myenv", "state-server": true, "authorized-keys": "i-am-a-key", "default-series": config.LatestLtsSeries(), "firewall-mode": config.FwInstance, "development": false, "ssl-hostname-verification": true, "admin-secret": "adminpass", }, }) bootstrapEnv(c, coretesting.SampleEnvName, store) // Verify the cache is empty. info, err := store.ReadInfo("myenv") c.Assert(err, gc.IsNil) c.Assert(info, gc.NotNil) c.Assert(info.APIEndpoint(), jc.DeepEquals, configstore.APIEndpoint{}) c.Assert(info.APICredentials(), jc.DeepEquals, configstore.APICredentials{}) called := 0 expectState := mockedAPIState(0) apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (juju.APIState, error) { c.Check(apiInfo.Tag, gc.Equals, names.NewUserTag("admin")) c.Check(string(apiInfo.CACert), gc.Not(gc.Equals), "") c.Check(apiInfo.Password, gc.Equals, "adminpass") // EnvironTag wasn't in regular Config c.Check(apiInfo.EnvironTag, gc.IsNil) c.Check(opts, gc.DeepEquals, api.DefaultDialOpts()) called++ return expectState, nil } st, err := juju.NewAPIFromStore("myenv", store, apiOpen) c.Assert(err, gc.IsNil) c.Assert(st, gc.Equals, expectState) c.Assert(called, gc.Equals, 1) // Make sure the cache is updated. info, err = store.ReadInfo("myenv") c.Assert(err, gc.IsNil) c.Assert(info, gc.NotNil) ep := info.APIEndpoint() c.Assert(ep.Addresses, gc.HasLen, 1) c.Check(ep.Addresses[0], gc.Matches, `localhost:\d+`) c.Check(ep.CACert, gc.Not(gc.Equals), "") // Old servers won't hand back EnvironTag, so it should stay empty in // the cache c.Check(ep.EnvironUUID, gc.Equals, "") creds := info.APICredentials() c.Check(creds.User, gc.Equals, "admin") c.Check(creds.Password, gc.Equals, "adminpass") }
func (s *RepoSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.BaseRepoSuite.SetUpTest(c) // Change the environ's config to ensure we're using the one in state, // not the one in the local environments.yaml updateAttrs := map[string]interface{}{"default-series": config.LatestLtsSeries()} err := s.State.UpdateModelConfig(updateAttrs, nil, nil) c.Assert(err, jc.ErrorIsNil) }
// charmSeries determine what series to use with a charm. // Order of preference is: // - user requested or defined by bundle when deploying // - default from charm metadata supported series // - model default // - charm store default func charmSeries( requestedSeries, seriesFromCharm string, supportedSeries []string, force bool, conf *config.Config, fromBundle bool, ) (string, string, error) { // User has requested a series and we have a new charm with series in metadata. if requestedSeries != "" && seriesFromCharm == "" { if !force && !isSeriesSupported(requestedSeries, supportedSeries) { return "", "", charm.NewUnsupportedSeriesError(requestedSeries, supportedSeries) } if fromBundle { return requestedSeries, msgBundleSeries, nil } else { return requestedSeries, msgUserRequestedSeries, nil } } // User has requested a series and it's an old charm for a single series. if seriesFromCharm != "" { if !force && requestedSeries != "" && requestedSeries != seriesFromCharm { return "", "", charm.NewUnsupportedSeriesError(requestedSeries, []string{seriesFromCharm}) } if requestedSeries != "" { if fromBundle { return requestedSeries, msgBundleSeries, nil } else { return requestedSeries, msgUserRequestedSeries, nil } } return seriesFromCharm, msgSingleCharmSeries, nil } // Use charm default. if len(supportedSeries) > 0 { return supportedSeries[0], msgDefaultCharmSeries, nil } // Use model default supported series. if defaultSeries, ok := conf.DefaultSeries(); ok { if !force && !isSeriesSupported(defaultSeries, supportedSeries) { return "", "", charm.NewUnsupportedSeriesError(defaultSeries, supportedSeries) } return defaultSeries, msgDefaultModelSeries, nil } // Use latest LTS. latestLtsSeries := config.LatestLtsSeries() if !force && !isSeriesSupported(latestLtsSeries, supportedSeries) { return "", "", charm.NewUnsupportedSeriesError(latestLtsSeries, supportedSeries) } return latestLtsSeries, msgLatestLTSSeries, nil }
func (s *RepoSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // Change the environ's config to ensure we're using the one in state, // not the one in the local environments.yaml updateAttrs := map[string]interface{}{"default-series": config.LatestLtsSeries()} err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil) c.Assert(err, jc.ErrorIsNil) s.RepoPath = os.Getenv("JUJU_REPOSITORY") repoPath := c.MkDir() os.Setenv("JUJU_REPOSITORY", repoPath) s.SeriesPath = filepath.Join(repoPath, config.LatestLtsSeries()) err = os.Mkdir(s.SeriesPath, 0777) c.Assert(err, jc.ErrorIsNil) // Create a symlink "quantal" -> "precise", because most charms // and machines are written with hard-coded "quantal" series, // hence they interact badly with a local repository that assumes // only "precise" charms are available. err = symlink.New(s.SeriesPath, filepath.Join(repoPath, "quantal")) c.Assert(err, jc.ErrorIsNil) }
func (s *AddMachineSuite) TestAddMachine(c *gc.C) { context, err := runAddMachine(c) c.Assert(err, gc.IsNil) c.Assert(testing.Stderr(context), gc.Equals, "created machine 0\n") m, err := s.State.Machine("0") c.Assert(err, gc.IsNil) c.Assert(m.Life(), gc.Equals, state.Alive) c.Assert(m.Series(), gc.DeepEquals, config.LatestLtsSeries()) mcons, err := m.Constraints() c.Assert(err, gc.IsNil) c.Assert(&mcons, jc.Satisfies, constraints.IsEmpty) }
func (s *BootstrapSuite) TestAutoUploadAfterFailedSync(c *gc.C) { s.PatchValue(&version.Current.Series, config.LatestLtsSeries()) s.setupAutoUploadTest(c, "1.7.3", "quantal") // Run command and check for that upload has been run for tools matching // the current juju version. opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), envcmd.Wrap(new(BootstrapCommand)), "-e", "devenv") c.Assert(<-errc, gc.IsNil) c.Check((<-opc).(dummy.OpBootstrap).Env, gc.Equals, "devenv") icfg := (<-opc).(dummy.OpFinalizeBootstrap).InstanceConfig c.Assert(icfg, gc.NotNil) c.Assert(icfg.Tools.Version.String(), gc.Equals, "1.7.3.1-raring-"+arch.HostArch()) }
func (s *BootstrapSuite) TestAutoUploadAfterFailedSync(c *gc.C) { s.PatchValue(&version.Current.Series, config.LatestLtsSeries()) s.setupAutoUploadTest(c, "1.7.3", "quantal") // 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.Check((<-opc).(dummy.OpPutFile).Env, gc.Equals, "peckham") // verify storage c.Check((<-opc).(dummy.OpBootstrap).Env, gc.Equals, "peckham") mcfg := (<-opc).(dummy.OpFinalizeBootstrap).MachineConfig c.Assert(mcfg, gc.NotNil) c.Assert(mcfg.Tools.Version.String(), gc.Equals, "1.7.3.1-raring-"+version.Current.Arch) }
func (s *NewAPIClientSuite) TestWithConfigAndNoInfo(c *gc.C) { c.Skip("not really possible now that there is no defined admin user") s.PatchValue(&version.Current, coretesting.FakeVersionNumber) coretesting.MakeSampleJujuHome(c) store := newConfigStore(coretesting.SampleModelName, &environInfo{ bootstrapConfig: map[string]interface{}{ "type": "dummy", "name": "myenv", "state-server": true, "authorized-keys": "i-am-a-key", "default-series": config.LatestLtsSeries(), "firewall-mode": config.FwInstance, "development": false, "ssl-hostname-verification": true, "admin-secret": "adminpass", }, }) s.bootstrapEnv(c, coretesting.SampleModelName, store) info, err := store.ReadInfo("myenv") c.Assert(err, jc.ErrorIsNil) c.Assert(info, gc.NotNil) c.Logf("%#v", info.APICredentials()) called := 0 expectState := mockedAPIState(0) apiOpen := func(apiInfo *api.Info, opts api.DialOpts) (api.Connection, error) { c.Check(apiInfo.Tag, gc.Equals, dummy.AdminUserTag()) c.Check(string(apiInfo.CACert), gc.Not(gc.Equals), "") c.Check(apiInfo.Password, gc.Equals, "adminpass") // ModelTag wasn't in regular Config c.Check(apiInfo.ModelTag.Id(), gc.Equals, "") c.Check(opts, gc.DeepEquals, api.DefaultDialOpts()) called++ return expectState, nil } st, err := juju.NewAPIFromStore("myenv", store, apiOpen) c.Assert(err, jc.ErrorIsNil) c.Assert(st, gc.Equals, expectState) c.Assert(called, gc.Equals, 1) // Make sure the cache is updated. info, err = store.ReadInfo("myenv") c.Assert(err, jc.ErrorIsNil) c.Assert(info, gc.NotNil) ep := info.APIEndpoint() c.Assert(ep.Addresses, gc.HasLen, 1) c.Check(ep.Addresses[0], gc.Matches, `localhost:\d+`) c.Check(ep.CACert, gc.Not(gc.Equals), "") }
func (s *ImageMetadataSuite) TestImageMetadataFilesLatestLts(c *gc.C) { envConfig := strings.Replace(metadataTestEnvConfig, "default-series: precise", "", -1) testing.WriteEnvironments(c, envConfig) ctx := testing.Context(c) code := cmd.Main( envcmd.Wrap(&ImageMetadataCommand{}), ctx, []string{ "-d", s.dir, "-i", "1234", "-r", "region", "-a", "arch", "-u", "endpoint"}) c.Assert(code, gc.Equals, 0) out := testing.Stdout(ctx) expected := expectedMetadata{ series: config.LatestLtsSeries(), arch: "arch", } s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) }
func (s *BaseRepoSuite) SetUpTest(c *gc.C) { // Set up a local repository. s.RepoPath = os.Getenv("JUJU_REPOSITORY") repoPath := c.MkDir() os.Setenv("JUJU_REPOSITORY", repoPath) s.SeriesPath = filepath.Join(repoPath, config.LatestLtsSeries()) c.Assert(os.Mkdir(s.SeriesPath, 0777), jc.ErrorIsNil) // Create a symlink "quantal" -> "precise", because most charms // and machines are written with hard-coded "quantal" series, // hence they interact badly with a local repository that assumes // only "precise" charms are available. err := symlink.New(s.SeriesPath, filepath.Join(repoPath, "quantal")) c.Assert(err, jc.ErrorIsNil) s.BundlesPath = filepath.Join(repoPath, "bundle") c.Assert(os.Mkdir(s.BundlesPath, 0777), jc.ErrorIsNil) }
func (s *BootstrapSuite) TestAutoUploadAfterFailedSync(c *gc.C) { s.PatchValue(&series.HostSeries, func() string { return config.LatestLtsSeries() }) s.setupAutoUploadTest(c, "1.7.3", "quantal") // Run command and check for that upload has been run for tools matching // the current juju version. opc, errc := cmdtesting.RunCommand( cmdtesting.NullContext(c), s.newBootstrapCommand(), "devcontroller", "dummy-cloud/region-1", "--config", "default-series=raring", "--auto-upgrade", ) c.Assert(<-errc, gc.IsNil) c.Check((<-opc).(dummy.OpBootstrap).Env, gc.Equals, "admin") icfg := (<-opc).(dummy.OpFinalizeBootstrap).InstanceConfig c.Assert(icfg, gc.NotNil) c.Assert(icfg.Tools.Version.String(), gc.Equals, "1.7.3.1-raring-"+arch.HostArch()) }
func (s *SetEnvironmentSuite) TestChangeDefaultSeries(c *gc.C) { // default-series not set stateConfig, err := s.State.EnvironConfig() c.Assert(err, gc.IsNil) series, ok := stateConfig.DefaultSeries() c.Assert(ok, gc.Equals, true) c.Assert(series, gc.Equals, config.LatestLtsSeries()) // default-series set in RepoSuite.SetUpTest _, err = testing.RunCommand(c, envcmd.Wrap(&SetEnvironmentCommand{}), "default-series=raring") c.Assert(err, gc.IsNil) stateConfig, err = s.State.EnvironConfig() c.Assert(err, gc.IsNil) series, ok = stateConfig.DefaultSeries() c.Assert(ok, gc.Equals, true) c.Assert(series, gc.Equals, "raring") c.Assert(config.PreferredSeries(stateConfig), gc.Equals, "raring") }
// resolve resolves the given given charm or bundle URL // string by looking it up in the appropriate charm repository. If it is // a charm store URL, the given csParams will be used to access the // charm store repository. If it is a local charm or bundle URL, the // local charm repository at the given repoPath will be used. The given // configuration will be used to add any necessary attributes to the // repo and to return the charm's supported series if possible. // // It returns the fully resolved URL, any series supported by the entity, // and the repository that holds it. func (r *charmURLResolver) resolve(urlStr string) (*charm.URL, csparams.Channel, []string, charmrepo.Interface, error) { var noChannel csparams.Channel url, err := charm.ParseURL(urlStr) if err != nil { return nil, noChannel, nil, nil, errors.Trace(err) } switch url.Schema { case "cs": repo := config.SpecializeCharmRepo(r.csRepo, r.conf).(*charmrepo.CharmStore) resultUrl, channel, supportedSeries, err := repo.ResolveWithChannel(url) if err != nil { return nil, noChannel, nil, nil, errors.Trace(err) } return resultUrl, channel, supportedSeries, repo, nil case "local": if url.Series == "" { if defaultSeries, ok := r.conf.DefaultSeries(); ok { url.Series = defaultSeries } } if url.Series == "" { possibleURL := *url possibleURL.Series = config.LatestLtsSeries() logger.Errorf("The series is not specified in the model (default-series) or with the charm. Did you mean:\n\t%s", &possibleURL) return nil, noChannel, nil, nil, errors.Errorf("cannot resolve series for charm: %q", url) } repo, err := charmrepo.NewLocalRepository(r.repoPath) if err != nil { return nil, noChannel, nil, nil, errors.Mask(err) } repo = config.SpecializeCharmRepo(repo, r.conf) resultUrl, supportedSeries, err := repo.Resolve(url) if err != nil { return nil, noChannel, nil, nil, errors.Trace(err) } return resultUrl, noChannel, supportedSeries, repo, nil default: return nil, noChannel, nil, nil, errors.Errorf("unknown schema for charm reference %q", urlStr) } }
// SampleConfig() returns an environment configuration with all required // attributes set. func SampleConfig() testing.Attrs { return testing.Attrs{ "type": "dummy", "name": "only", "uuid": testing.ModelTag.Id(), "authorized-keys": testing.FakeAuthKeys, "firewall-mode": config.FwInstance, "admin-secret": testing.DefaultMongoPassword, "ca-cert": testing.CACert, "ca-private-key": testing.CAKey, "ssl-hostname-verification": true, "development": false, "state-port": 1234, "api-port": 4321, "default-series": config.LatestLtsSeries(), "secret": "pork", "state-server": true, "prefer-ipv6": true, } }
func (s *funcSuite) SetUpTest(c *gc.C) { s.baseImageMetadataSuite.SetUpTest(c) cfg, err := config.New(config.NoDefaults, mockConfig()) c.Assert(err, jc.ErrorIsNil) s.env, err = environs.Prepare(cfg, envtesting.BootstrapContext(c), configstore.NewMem()) c.Assert(err, jc.ErrorIsNil) s.state = s.constructState(cfg) s.expected = cloudimagemetadata.Metadata{ cloudimagemetadata.MetadataAttributes{ Stream: "released", Source: "custom", Series: config.LatestLtsSeries(), Arch: "amd64", Region: "dummy_region", }, 0, "", } }
func (s *UpgradeJujuSuite) TestUpgradeJuju(c *gc.C) { oldVersion := version.Current defer func() { version.Current = oldVersion }() for i, test := range upgradeJujuTests { c.Logf("\ntest %d: %s", i, test.about) s.Reset(c) // Set up apparent CLI version and initialize the command. version.Current = version.MustParseBinary(test.currentVersion) com := &UpgradeJujuCommand{} if err := coretesting.InitCommand(envcmd.Wrap(com), test.args); err != nil { if test.expectInitErr != "" { c.Check(err, gc.ErrorMatches, test.expectInitErr) } else { c.Check(err, gc.IsNil) } continue } // Set up state and environ, and run the command. toolsDir := c.MkDir() updateAttrs := map[string]interface{}{ "agent-version": test.agentVersion, "tools-metadata-url": "file://" + toolsDir, } err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil) c.Assert(err, gc.IsNil) versions := make([]version.Binary, len(test.tools)) for i, v := range test.tools { versions[i] = version.MustParseBinary(v) } if len(versions) > 0 { envtesting.MustUploadFakeToolsVersions(s.Conn.Environ.Storage(), versions...) stor, err := filestorage.NewFileStorageWriter(toolsDir) c.Assert(err, gc.IsNil) envtesting.MustUploadFakeToolsVersions(stor, versions...) } err = com.Run(coretesting.Context(c)) if test.expectErr != "" { c.Check(err, gc.ErrorMatches, test.expectErr) continue } else if !c.Check(err, gc.IsNil) { continue } // Check expected changes to environ/state. cfg, err := s.State.EnvironConfig() c.Check(err, gc.IsNil) agentVersion, ok := cfg.AgentVersion() c.Check(ok, gc.Equals, true) c.Check(agentVersion, gc.Equals, version.MustParse(test.expectVersion)) for _, uploaded := range test.expectUploaded { // Substitute latest LTS for placeholder in expected series for uploaded tools uploaded = strings.Replace(uploaded, "%LTS%", config.LatestLtsSeries(), 1) vers := version.MustParseBinary(uploaded) r, err := storage.Get(s.Conn.Environ.Storage(), envtools.StorageName(vers)) if !c.Check(err, gc.IsNil) { continue } data, err := ioutil.ReadAll(r) r.Close() c.Check(err, gc.IsNil) expectContent := version.Current expectContent.Number = agentVersion checkToolsContent(c, data, "jujud contents "+expectContent.String()) } } }
func (s *BootstrapSuite) run(c *gc.C, test bootstrapTest) (restore gitjujutesting.Restorer) { // Create home with dummy provider and remove all // of its envtools. env := resetJujuHome(c, "peckham") // Although we're testing PrepareEndpointsForCaching interactions // separately in the juju package, here we just ensure it gets // called with the right arguments. prepareCalled := false addrConnectedTo := "localhost:17070" restore = gitjujutesting.PatchValue( &prepareEndpointsForCaching, func(info configstore.EnvironInfo, hps [][]network.HostPort, addr network.HostPort) (_, _ []string, _ bool) { prepareCalled = true addrs, hosts, changed := juju.PrepareEndpointsForCaching(info, hps, addr) // Because we're bootstrapping the addresses will always // change, as there's no .jenv file saved yet. c.Assert(changed, jc.IsTrue) return addrs, hosts, changed }, ) if test.version != "" { useVersion := strings.Replace(test.version, "%LTS%", config.LatestLtsSeries(), 1) origVersion := version.Current version.Current = version.MustParseBinary(useVersion) restore = restore.Add(func() { version.Current = origVersion }) } if test.hostArch != "" { origArch := arch.HostArch arch.HostArch = func() string { return test.hostArch } restore = restore.Add(func() { arch.HostArch = origArch }) } // Run command and check for uploads. opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), envcmd.Wrap(new(BootstrapCommand)), test.args...) // Check for remaining operations/errors. if test.err != "" { err := <-errc c.Assert(err, gc.NotNil) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Check(stripped, gc.Matches, test.err) return restore } if !c.Check(<-errc, gc.IsNil) { return restore } 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) opFinalizeBootstrap := (<-opc).(dummy.OpFinalizeBootstrap) c.Check(opFinalizeBootstrap.Env, gc.Equals, "peckham") c.Check(opFinalizeBootstrap.InstanceConfig.Tools, gc.NotNil) if test.upload != "" { c.Check(opFinalizeBootstrap.InstanceConfig.Tools.Version.String(), gc.Equals, test.upload) } store, err := configstore.Default() c.Assert(err, jc.ErrorIsNil) // Check a CA cert/key was generated by reloading the environment. env, err = environs.NewFromName("peckham", store) c.Assert(err, jc.ErrorIsNil) _, hasCert := env.Config().CACert() c.Check(hasCert, jc.IsTrue) _, hasKey := env.Config().CAPrivateKey() c.Check(hasKey, jc.IsTrue) info, err := store.ReadInfo("peckham") c.Assert(err, jc.ErrorIsNil) c.Assert(info, gc.NotNil) c.Assert(prepareCalled, jc.IsTrue) c.Assert(info.APIEndpoint().Addresses, gc.DeepEquals, []string{addrConnectedTo}) return restore }
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 }() } if test.upload == "" { usefulVersion := version.Current usefulVersion.Series = config.PreferredSeries(env.Config()) envtesting.AssertUploadFakeToolsVersions(c, env.Storage(), usefulVersion) } // Run command and check for uploads. opc, errc := cmdtesting.RunCommand(cmdtesting.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 } opPutBootstrapVerifyFile := (<-opc).(dummy.OpPutFile) c.Check(opPutBootstrapVerifyFile.Env, gc.Equals, "peckham") c.Check(opPutBootstrapVerifyFile.FileName, gc.Equals, environs.VerificationFilename) 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) c.Check(opBootstrap.Args.KeepBroken, gc.Equals, test.keepBroken) opFinalizeBootstrap := (<-opc).(dummy.OpFinalizeBootstrap) c.Check(opFinalizeBootstrap.Env, gc.Equals, "peckham") c.Check(opFinalizeBootstrap.MachineConfig.Tools, gc.NotNil) if test.upload != "" { c.Check(opFinalizeBootstrap.MachineConfig.Tools.Version.String(), gc.Equals, test.upload) } 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) info, err := store.ReadInfo("peckham") c.Assert(err, gc.IsNil) c.Assert(info, gc.NotNil) c.Assert(info.APIEndpoint().Addresses, gc.DeepEquals, []string{"localhost:17070"}) }
func (t *LiveTests) TestBootstrapAndDeploy(c *gc.C) { if !t.CanOpenState || !t.HasProvisioner { c.Skip(fmt.Sprintf("skipping provisioner test, CanOpenState: %v, HasProvisioner: %v", t.CanOpenState, t.HasProvisioner)) } t.BootstrapOnce(c) // TODO(niemeyer): Stop growing this kitchen sink test and split it into proper parts. c.Logf("opening state") st := t.Env.(testing.GetStater).GetStateInAPIServer() c.Logf("opening API connection") apiState, err := juju.NewAPIState(t.Env, api.DefaultDialOpts()) c.Assert(err, gc.IsNil) defer apiState.Close() // Check that the agent version has made it through the // bootstrap process (it's optional in the config.Config) cfg, err := st.EnvironConfig() c.Assert(err, gc.IsNil) agentVersion, ok := cfg.AgentVersion() c.Check(ok, gc.Equals, true) c.Check(agentVersion, gc.Equals, version.Current.Number) // Check that the constraints have been set in the environment. cons, err := st.EnvironConstraints() c.Assert(err, gc.IsNil) c.Assert(cons.String(), gc.Equals, "mem=2048M") // Wait for machine agent to come up on the bootstrap // machine and find the deployed series from that. m0, err := st.Machine("0") c.Assert(err, gc.IsNil) instId0, err := m0.InstanceId() c.Assert(err, gc.IsNil) // Check that the API connection is working. status, err := apiState.Client().Status(nil) c.Assert(err, gc.IsNil) c.Assert(status.Machines["0"].InstanceId, gc.Equals, string(instId0)) mw0 := newMachineToolWaiter(m0) defer mw0.Stop() // If the series has not been specified, we expect the most recent Ubuntu LTS release to be used. expectedVersion := version.Current expectedVersion.Series = config.LatestLtsSeries() mtools0 := waitAgentTools(c, mw0, expectedVersion) // Create a new service and deploy a unit of it. c.Logf("deploying service") repoDir := c.MkDir() url := charmtesting.Charms.ClonedURL(repoDir, mtools0.Version.Series, "dummy") sch, err := testing.PutCharm(st, url, &charm.LocalRepository{Path: repoDir}, false) c.Assert(err, gc.IsNil) svc, err := st.AddService("dummy", "user-admin", sch, nil) c.Assert(err, gc.IsNil) units, err := juju.AddUnits(st, svc, 1, "") c.Assert(err, gc.IsNil) unit := units[0] // Wait for the unit's machine and associated agent to come up // and announce itself. mid1, err := unit.AssignedMachineId() c.Assert(err, gc.IsNil) m1, err := st.Machine(mid1) c.Assert(err, gc.IsNil) mw1 := newMachineToolWaiter(m1) defer mw1.Stop() waitAgentTools(c, mw1, mtools0.Version) err = m1.Refresh() c.Assert(err, gc.IsNil) instId1, err := m1.InstanceId() c.Assert(err, gc.IsNil) uw := newUnitToolWaiter(unit) defer uw.Stop() utools := waitAgentTools(c, uw, expectedVersion) // Check that we can upgrade the environment. newVersion := utools.Version newVersion.Patch++ t.checkUpgrade(c, st, newVersion, mw0, mw1, uw) // BUG(niemeyer): Logic below is very much wrong. Must be: // // 1. EnsureDying on the unit and EnsureDying on the machine // 2. Unit dies by itself // 3. Machine removes dead unit // 4. Machine dies by itself // 5. Provisioner removes dead machine // // Now remove the unit and its assigned machine and // check that the PA removes it. c.Logf("removing unit") err = unit.Destroy() c.Assert(err, gc.IsNil) // Wait until unit is dead uwatch := unit.Watch() defer uwatch.Stop() for unit.Life() != state.Dead { c.Logf("waiting for unit change") <-uwatch.Changes() err := unit.Refresh() c.Logf("refreshed; err %v", err) if errors.IsNotFound(err) { c.Logf("unit has been removed") break } c.Assert(err, gc.IsNil) } for { c.Logf("destroying machine") err := m1.Destroy() if err == nil { break } c.Assert(err, gc.FitsTypeOf, &state.HasAssignedUnitsError{}) time.Sleep(5 * time.Second) err = m1.Refresh() if errors.IsNotFound(err) { break } c.Assert(err, gc.IsNil) } c.Logf("waiting for instance to be removed") t.assertStopInstance(c, t.Env, instId1) }
func (s *BootstrapSuite) run(c *gc.C, test bootstrapTest) testing.Restorer { // Create home with dummy provider and remove all // of its envtools. resetJujuXDGDataHome(c) dummy.Reset(c) var restore testing.Restorer = func() { s.store = jujuclienttesting.NewMemStore() } if test.version != "" { useVersion := strings.Replace(test.version, "%LTS%", config.LatestLtsSeries(), 1) v := version.MustParseBinary(useVersion) restore = restore.Add(testing.PatchValue(&jujuversion.Current, v.Number)) restore = restore.Add(testing.PatchValue(&arch.HostArch, func() string { return v.Arch })) restore = restore.Add(testing.PatchValue(&series.HostSeries, func() string { return v.Series })) } if test.hostArch != "" { restore = restore.Add(testing.PatchValue(&arch.HostArch, func() string { return test.hostArch })) } controllerName := "peckham-controller" cloudName := "dummy" // Run command and check for uploads. args := append([]string{ controllerName, cloudName, "--config", "default-series=raring", }, test.args...) opc, errc := cmdtesting.RunCommand(cmdtesting.NullContext(c), s.newBootstrapCommand(), args...) // Check for remaining operations/errors. if test.err != "" { err := <-errc c.Assert(err, gc.NotNil) stripped := strings.Replace(err.Error(), "\n", "", -1) c.Check(stripped, gc.Matches, test.err) return restore } if !c.Check(<-errc, gc.IsNil) { return restore } opBootstrap := (<-opc).(dummy.OpBootstrap) c.Check(opBootstrap.Env, gc.Equals, "admin") c.Check(opBootstrap.Args.ModelConstraints, gc.DeepEquals, test.constraints) if test.bootstrapConstraints == (constraints.Value{}) { test.bootstrapConstraints = test.constraints } c.Check(opBootstrap.Args.BootstrapConstraints, gc.DeepEquals, test.bootstrapConstraints) c.Check(opBootstrap.Args.Placement, gc.Equals, test.placement) opFinalizeBootstrap := (<-opc).(dummy.OpFinalizeBootstrap) c.Check(opFinalizeBootstrap.Env, gc.Equals, "admin") c.Check(opFinalizeBootstrap.InstanceConfig.Tools, gc.NotNil) if test.upload != "" { c.Check(opFinalizeBootstrap.InstanceConfig.Tools.Version.String(), gc.Equals, test.upload) } expectedBootstrappedControllerName := bootstrappedControllerName(controllerName) // Check controllers.yaml controller details. addrConnectedTo := []string{"localhost:17070"} controller, err := s.store.ControllerByName(expectedBootstrappedControllerName) c.Assert(err, jc.ErrorIsNil) c.Assert(controller.CACert, gc.Not(gc.Equals), "") c.Assert(controller.UnresolvedAPIEndpoints, gc.DeepEquals, addrConnectedTo) c.Assert(controller.APIEndpoints, gc.DeepEquals, addrConnectedTo) c.Assert(utils.IsValidUUIDString(controller.ControllerUUID), jc.IsTrue) // Controller model should be called "admin". controllerModel, err := s.store.ModelByName(expectedBootstrappedControllerName, "admin@local", "admin") c.Assert(controllerModel.ModelUUID, gc.Equals, controller.ControllerUUID) c.Assert(err, jc.ErrorIsNil) // Bootstrap config should have been saved, and should only contain // the type, name, and any user-supplied configuration. bootstrapConfig, err := s.store.BootstrapConfigForController(expectedBootstrappedControllerName) c.Assert(err, jc.ErrorIsNil) c.Assert(bootstrapConfig.Cloud, gc.Equals, "dummy") c.Assert(bootstrapConfig.Credential, gc.Equals, "") c.Assert(bootstrapConfig.Config, jc.DeepEquals, map[string]interface{}{ "name": "admin", "type": "dummy", "default-series": "raring", }) return restore }
} return values } // machineInfo is the structure used to pass information between the provider // and the agent running on a node. // When a node is started, the provider code creates a machineInfo object // containing information about the node being started and configures // cloudinit to get a YAML representation of that object written on the node's // filesystem during its first startup. That file is then read by the juju // agent running on the node and converted back into a machineInfo object. type machineInfo struct { Hostname string `yaml:,omitempty` } var maasDataDir = paths.MustSucceed(paths.DataDir(config.LatestLtsSeries())) var _MAASInstanceFilename = path.Join(maasDataDir, "MAASmachine.txt") // cloudinitRunCmd returns the shell command that, when run, will create the // "machine info" file containing the hostname of a machine. // That command is destined to be used by cloudinit. func (info *machineInfo) cloudinitRunCmd(series string) (string, error) { dataDir, err := paths.DataDir(series) if err != nil { return "", err } renderer, err := cloudinit.NewRenderer(series) if err != nil { return "", err }
func (s *DeploySuite) TestCharmSeries(c *gc.C) { deploySeriesTests := []struct { requestedSeries string force bool seriesFromCharm string supportedSeries []string modelSeries string ltsSeries string expectedSeries string message string err string }{{ ltsSeries: "precise", modelSeries: "wily", supportedSeries: []string{"trusty", "precise"}, expectedSeries: "trusty", message: "with the default charm metadata series %q", }, { requestedSeries: "trusty", seriesFromCharm: "trusty", expectedSeries: "trusty", message: "with the user specified series %q", }, { requestedSeries: "wily", seriesFromCharm: "trusty", err: `series "wily" not supported by charm, supported series are: trusty`, }, { requestedSeries: "wily", supportedSeries: []string{"trusty", "precise"}, err: `series "wily" not supported by charm, supported series are: trusty,precise`, }, { ltsSeries: config.LatestLtsSeries(), err: `series .* not supported by charm, supported series are: .*`, }, { modelSeries: "xenial", err: `series "xenial" not supported by charm, supported series are: .*`, }, { requestedSeries: "wily", seriesFromCharm: "trusty", expectedSeries: "wily", message: "with the user specified series %q", force: true, }, { requestedSeries: "wily", supportedSeries: []string{"trusty", "precise"}, expectedSeries: "wily", message: "with the user specified series %q", force: true, }, { ltsSeries: config.LatestLtsSeries(), force: true, expectedSeries: config.LatestLtsSeries(), message: "with the latest LTS series %q", }, { ltsSeries: "precise", modelSeries: "xenial", force: true, expectedSeries: "xenial", message: "with the configured model default series %q", }} for i, test := range deploySeriesTests { c.Logf("test %d", i) cfg, err := config.New(config.UseDefaults, map[string]interface{}{ "name": "test", "type": "dummy", "uuid": coretesting.ModelTag.Id(), "ca-cert": coretesting.CACert, "ca-private-key": coretesting.CAKey, "authorized-keys": coretesting.FakeAuthKeys, "default-series": test.modelSeries, }) c.Assert(err, jc.ErrorIsNil) series, msg, err := charmSeries(test.requestedSeries, test.seriesFromCharm, test.supportedSeries, test.force, cfg) if test.err != "" { c.Check(err, gc.ErrorMatches, test.err) continue } c.Assert(err, jc.ErrorIsNil) c.Check(series, gc.Equals, test.expectedSeries) c.Check(msg, gc.Matches, test.message) } }