Example #1
0
func PreferredDefaultVersions(conf *config.Config, template version.Binary) []version.Binary {
	prefVersion := template
	prefVersion.Series = config.PreferredSeries(conf)
	defaultVersion := template
	defaultVersion.Series = testing.FakeDefaultSeries
	return []version.Binary{prefVersion, defaultVersion}
}
Example #2
0
func (s *BootstrapSuite) TestBootstrapJenvWarning(c *gc.C) {
	env, fake := makeEmptyFakeHome(c)
	defer fake.Restore()
	defaultSeriesVersion := version.Current
	defaultSeriesVersion.Series = config.PreferredSeries(env.Config())
	// Force a dev version by having an odd minor version number.
	// This is because we have not uploaded any tools and auto
	// upload is only enabled for dev versions.
	defaultSeriesVersion.Minor = 11
	s.PatchValue(&version.Current, defaultSeriesVersion)

	store, err := configstore.Default()
	c.Assert(err, gc.IsNil)
	ctx := coretesting.Context(c)
	environs.PrepareFromName("peckham", ctx, store)

	logger := "jenv.warning.test"
	testWriter := &loggo.TestWriter{}
	loggo.RegisterWriter(logger, testWriter, loggo.WARNING)
	defer loggo.RemoveWriter(logger)

	_, errc := runCommand(ctx, new(BootstrapCommand), "-e", "peckham")
	c.Assert(<-errc, gc.IsNil)
	c.Assert(testWriter.Log, jc.LogMatches, []string{"ignoring environments.yaml: using bootstrap config in .*"})
}
Example #3
0
// StartInstanceWithConstraintsAndNetworks is a test helper function that
// starts an instance with the given networks, and a plausible but invalid
// configuration, and returns the result of Environ.StartInstance.
func StartInstanceWithConstraintsAndNetworks(
	env environs.Environ, machineId string, cons constraints.Value,
	includeNetworks, excludeNetworks []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")
	}
	possibleTools, err := tools.FindInstanceTools(env, agentVersion, series, cons.Arch)
	if err != nil {
		return nil, nil, nil, err
	}
	machineNonce := "fake_nonce"
	stateInfo := FakeStateInfo(machineId)
	apiInfo := FakeAPIInfo(machineId)
	machineConfig := environs.NewMachineConfig(
		machineId, machineNonce,
		includeNetworks, excludeNetworks,
		stateInfo, apiInfo)
	return env.StartInstance(environs.StartInstanceParams{
		Constraints:   cons,
		Tools:         possibleTools,
		MachineConfig: machineConfig,
	})
}
Example #4
0
// MetadataLookupParams returns parameters which are used to query simplestreams metadata.
func (env *joyentEnviron) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) {
	if region == "" {
		region = env.Ecfg().Region()
	}
	return &simplestreams.MetadataLookupParams{
		Series:        config.PreferredSeries(env.Ecfg()),
		Region:        region,
		Endpoint:      env.Ecfg().sdcUrl(),
		Architectures: []string{"amd64", "armhf"},
	}, nil
}
Example #5
0
// Bootstrap is a common implementation of the Bootstrap method defined on
// environs.Environ; we strongly recommend that this implementation be used
// when writing a new provider.
func Bootstrap(ctx environs.BootstrapContext, env environs.Environ, args environs.BootstrapParams) (err error) {
	// TODO make safe in the case of racing Bootstraps
	// If two Bootstraps are called concurrently, there's
	// no way to make sure that only one succeeds.

	var inst instance.Instance
	defer func() { handleBootstrapError(err, ctx, inst, env) }()

	// First thing, ensure we have tools otherwise there's no point.
	selectedTools, err := EnsureBootstrapTools(ctx, env, config.PreferredSeries(env.Config()), args.Constraints.Arch)
	if err != nil {
		return err
	}

	// Get the bootstrap SSH client. Do this early, so we know
	// not to bother with any of the below if we can't finish the job.
	client := ssh.DefaultClient
	if client == nil {
		// This should never happen: if we don't have OpenSSH, then
		// go.crypto/ssh should be used with an auto-generated key.
		return fmt.Errorf("no SSH client available")
	}

	privateKey, err := GenerateSystemSSHKey(env)
	if err != nil {
		return err
	}
	machineConfig := environs.NewBootstrapMachineConfig(privateKey)

	fmt.Fprintln(ctx.GetStderr(), "Launching instance")
	inst, hw, _, err := env.StartInstance(environs.StartInstanceParams{
		Constraints:   args.Constraints,
		Tools:         selectedTools,
		MachineConfig: machineConfig,
		Placement:     args.Placement,
	})
	if err != nil {
		return fmt.Errorf("cannot start bootstrap instance: %v", err)
	}
	fmt.Fprintf(ctx.GetStderr(), " - %s\n", inst.Id())
	machineConfig.InstanceId = inst.Id()
	machineConfig.HardwareCharacteristics = hw

	err = bootstrap.SaveState(
		env.Storage(),
		&bootstrap.BootstrapState{
			StateInstances: []instance.Id{inst.Id()},
		})
	if err != nil {
		return fmt.Errorf("cannot save state: %v", err)
	}
	return FinishBootstrap(ctx, client, inst, machineConfig)
}
Example #6
0
func (s *bootstrapSuite) TestEnsureToolsAvailabilityAgentVersionAlreadySet(c *gc.C) {
	// Can't upload tools if agent version already set.
	env := newEnviron("foo", useDefaultKeys, map[string]interface{}{"agent-version": "1.16.0"})
	s.setDummyStorage(c, env)
	envtesting.RemoveFakeTools(c, env.Storage())
	_, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), nil)
	c.Assert(err, gc.NotNil)
	stripped := strings.Replace(err.Error(), "\n", "", -1)
	c.Assert(stripped,
		gc.Matches,
		"cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment.*")
}
Example #7
0
func (s *bootstrapSuite) TestEnsureToolsAvailabilityNonDevVersion(c *gc.C) {
	// Can't automatically upload tools for released versions.
	s.PatchValue(&version.Current, version.MustParseBinary("1.18.0-trusty-arm64"))
	env := newEnviron("foo", useDefaultKeys, nil)
	s.setDummyStorage(c, env)
	envtesting.RemoveFakeTools(c, env.Storage())
	_, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), nil)
	c.Assert(err, gc.NotNil)
	stripped := strings.Replace(err.Error(), "\n", "", -1)
	c.Assert(stripped,
		gc.Matches,
		"cannot upload bootstrap tools: Juju cannot bootstrap because no tools are available for your environment.*")
}
Example #8
0
func (s *bootstrapSuite) TestEnsureToolsAvailability(c *gc.C) {
	existingToolsVersion := version.MustParseBinary("1.19.0-trusty-amd64")
	s.PatchValue(&version.Current, existingToolsVersion)
	env := newEnviron("foo", useDefaultKeys, nil)
	s.setDummyStorage(c, env)
	// At this point, as a result of setDummyStorage, env has tools for amd64 uploaded.
	// Set version.Current to be arm64 to simulate a different CLI version.
	cliVersion := version.Current
	cliVersion.Arch = "arm64"
	version.Current = cliVersion
	s.PatchValue(&sync.BuildToolsTarball, s.getMockBuildTools(c))
	// Host runs arm64, environment supports arm64.
	s.PatchValue(&arch.HostArch, func() string {
		return "arm64"
	})
	arch := "arm64"
	agentTools, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), &arch)
	c.Assert(err, gc.IsNil)
	c.Assert(agentTools, gc.HasLen, 1)
	expectedVers := version.Current
	expectedVers.Number.Build++
	expectedVers.Series = config.PreferredSeries(env.Config())
	c.Assert(agentTools[0].Version, gc.DeepEquals, expectedVers)
}
Example #9
0
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, "precise") // default-series set in RepoSuite.SetUpTest

	_, err = testing.RunCommand(c, &SetEnvironmentCommand{}, []string{"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")
}
Example #10
0
// EnvironmentInfo returns information about the current environment (default
// series and type).
func (c *Client) EnvironmentInfo() (api.EnvironmentInfo, error) {
	state := c.api.state
	conf, err := state.EnvironConfig()
	if err != nil {
		return api.EnvironmentInfo{}, err
	}
	env, err := state.Environment()
	if err != nil {
		return api.EnvironmentInfo{}, err
	}

	info := api.EnvironmentInfo{
		DefaultSeries: config.PreferredSeries(conf),
		ProviderType:  conf.Type(),
		Name:          conf.Name(),
		UUID:          env.UUID(),
	}
	return info, nil
}
Example #11
0
func (s *bootstrapSuite) TestEnsureToolsAvailabilityIncompatibleTargetArch(c *gc.C) {
	// Host runs ppc64, environment only supports amd64, arm64.
	s.PatchValue(&arch.HostArch, func() string {
		return "ppc64"
	})
	// Force a dev version by having an odd minor version number.
	// This is because we have not uploaded any tools and auto
	// upload is only enabled for dev versions.
	devVersion := version.Current
	devVersion.Minor = 11
	s.PatchValue(&version.Current, devVersion)
	env := newEnviron("foo", useDefaultKeys, nil)
	s.setDummyStorage(c, env)
	envtesting.RemoveFakeTools(c, env.Storage())
	_, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), nil)
	c.Assert(err, gc.NotNil)
	stripped := strings.Replace(err.Error(), "\n", "", -1)
	c.Assert(stripped,
		gc.Matches,
		`cannot upload bootstrap tools: environment "foo" of type dummy does not support instances running on "ppc64"`)
}
Example #12
0
func (s *bootstrapSuite) TestSeriesToUpload(c *gc.C) {
	vers := version.Current
	vers.Series = "quantal"
	s.PatchValue(&version.Current, vers)
	env := newEnviron("foo", useDefaultKeys, nil)
	cfg := env.Config()

	prefSeries := config.PreferredSeries(cfg)
	expect := []string{"quantal", prefSeries}
	for _, series := range []string{"precise", "trusty"} {
		if prefSeries != series {
			expect = append(expect, series)
		}
	}
	c.Assert(bootstrap.SeriesToUpload(cfg, nil), jc.SameContents, expect)

	c.Assert(bootstrap.SeriesToUpload(cfg, []string{"quantal"}), jc.SameContents, []string{"quantal"})
	env = newEnviron("foo", useDefaultKeys, map[string]interface{}{"default-series": "lucid"})
	cfg = env.Config()
	c.Assert(bootstrap.SeriesToUpload(cfg, nil), jc.SameContents, []string{"quantal", "precise", "trusty", "lucid"})
}
Example #13
0
func (s *BootstrapSuite) TestBootstrapTwice(c *gc.C) {
	env, fake := makeEmptyFakeHome(c)
	defer fake.Restore()
	defaultSeriesVersion := version.Current
	defaultSeriesVersion.Series = config.PreferredSeries(env.Config())
	// Force a dev version by having an odd minor version number.
	// This is because we have not uploaded any tools and auto
	// upload is only enabled for dev versions.
	defaultSeriesVersion.Minor = 11
	s.PatchValue(&version.Current, defaultSeriesVersion)

	ctx := coretesting.Context(c)
	code := cmd.Main(&BootstrapCommand{}, ctx, nil)
	c.Check(code, gc.Equals, 0)

	ctx2 := coretesting.Context(c)
	code2 := cmd.Main(&BootstrapCommand{}, ctx2, nil)
	c.Check(code2, gc.Equals, 1)
	expectedErrText := "error: environment is already bootstrapped\n"
	c.Check(coretesting.Stderr(ctx2), gc.Equals, expectedErrText)
	c.Check(coretesting.Stdout(ctx2), gc.Equals, "")
}
Example #14
0
// 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 store, err := configstore.Default(); err == nil {
		if environ, err = environs.PrepareFromName(c.EnvName, context, store); err == nil {
			logger.Infof("creating image metadata for environment %q", environ.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 fmt.Errorf("environment %q cannot provide region and endpoint", environ.Name())
				}
				// If only one of region or endpoint is provided, that is a problem.
				if cloudSpec.Region != cloudSpec.Endpoint && (cloudSpec.Region == "" || cloudSpec.Endpoint == "") {
					return fmt.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)
			}
			if v, ok := cfg.AllAttrs()["control-bucket"]; ok {
				c.privateStorage = v.(string)
			}
		} else {
			logger.Warningf("environment %q could not be opened: %v", c.EnvName, err)
		}
	}
	if environ == nil {
		logger.Infof("no environment found, creating image metadata using user supplied data")
	}
	if c.Series == "" {
		c.Series = config.LatestLtsSeries()
	}
	if c.ImageId == "" {
		return fmt.Errorf("image id must be specified")
	}
	if c.Region == "" {
		return fmt.Errorf("image region must be specified")
	}
	if c.Endpoint == "" {
		return fmt.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
}
Example #15
0
func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) error {
	selectedTools, err := common.EnsureBootstrapTools(ctx, e, config.PreferredSeries(e.Config()), args.Constraints.Arch)
	if err != nil {
		return err
	}

	defer delay()
	if err := e.checkBroken("Bootstrap"); err != nil {
		return err
	}
	password := e.Config().AdminSecret()
	if password == "" {
		return fmt.Errorf("admin-secret is required for bootstrap")
	}
	if _, ok := e.Config().CACert(); !ok {
		return fmt.Errorf("no CA certificate in environment configuration")
	}

	logger.Infof("would pick tools from %s", selectedTools)
	cfg, err := environs.BootstrapConfig(e.Config())
	if err != nil {
		return fmt.Errorf("cannot make bootstrap config: %v", err)
	}

	estate, err := e.state()
	if err != nil {
		return err
	}
	estate.mu.Lock()
	defer estate.mu.Unlock()
	if estate.bootstrapped {
		return fmt.Errorf("environment is already bootstrapped")
	}
	// Write the bootstrap file just like a normal provider. However
	// we need to release the mutex for the save state to work, so regain
	// it after the call.
	estate.mu.Unlock()
	if err := bootstrap.SaveState(e.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{"localhost"}}); err != nil {
		logger.Errorf("failed to save state instances: %v", err)
		estate.mu.Lock() // otherwise defered unlock will fail
		return err
	}
	estate.mu.Lock() // back at it

	if e.ecfg().stateServer() {
		// TODO(rog) factor out relevant code from cmd/jujud/bootstrap.go
		// so that we can call it here.

		info := stateInfo()
		st, err := state.Initialize(info, cfg, state.DefaultDialOpts(), estate.statePolicy)
		if err != nil {
			panic(err)
		}
		if err := st.SetEnvironConstraints(args.Constraints); err != nil {
			panic(err)
		}
		if err := st.SetAdminMongoPassword(utils.UserPasswordHash(password, utils.CompatSalt)); err != nil {
			panic(err)
		}
		_, err = st.AddUser("admin", password)
		if err != nil {
			panic(err)
		}
		estate.apiServer, err = apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert), []byte(testing.ServerKey), DataDir, LogDir)
		if err != nil {
			panic(err)
		}
		estate.apiState = st
	}
	estate.bootstrapped = true
	estate.ops <- OpBootstrap{Context: ctx, Env: e.name, Args: args}
	return nil
}
Example #16
0
func (c *Client) addOneMachine(p params.AddMachineParams) (*state.Machine, error) {
	if p.ParentId != "" && p.ContainerType == "" {
		return nil, fmt.Errorf("parent machine specified without container type")
	}
	if p.ContainerType != "" && p.Placement != nil {
		return nil, fmt.Errorf("container type and placement are mutually exclusive")
	}
	if p.Placement != nil {
		// Extract container type and parent from container placement directives.
		containerType, err := instance.ParseContainerType(p.Placement.Scope)
		if err == nil {
			p.ContainerType = containerType
			p.ParentId = p.Placement.Directive
			p.Placement = nil
		}
	}

	if p.ContainerType != "" || p.Placement != nil {
		// Guard against dubious client by making sure that
		// the following attributes can only be set when we're
		// not using placement.
		p.InstanceId = ""
		p.Nonce = ""
		p.HardwareCharacteristics = instance.HardwareCharacteristics{}
		p.Addrs = nil
	}

	if p.Series == "" {
		conf, err := c.api.state.EnvironConfig()
		if err != nil {
			return nil, err
		}
		p.Series = config.PreferredSeries(conf)
	}

	var placementDirective string
	if p.Placement != nil {
		env, err := c.api.state.Environment()
		if err != nil {
			return nil, err
		}
		if p.Placement.Scope != env.Name() {
			return nil, fmt.Errorf("invalid environment name %q", p.Placement.Scope)
		}
		placementDirective = p.Placement.Directive
	}

	jobs, err := stateJobs(p.Jobs)
	if err != nil {
		return nil, err
	}
	template := state.MachineTemplate{
		Series:      p.Series,
		Constraints: p.Constraints,
		InstanceId:  p.InstanceId,
		Jobs:        jobs,
		Nonce:       p.Nonce,
		HardwareCharacteristics: p.HardwareCharacteristics,
		Addresses:               p.Addrs,
		Placement:               placementDirective,
	}
	if p.ContainerType == "" {
		return c.api.state.AddOneMachine(template)
	}
	if p.ParentId != "" {
		return c.api.state.AddMachineInsideMachine(template, p.ParentId, p.ContainerType)
	}
	return c.api.state.AddMachineInsideNewMachine(template, template, p.ContainerType)
}
Example #17
0
func (test bootstrapTest) run(c *gc.C) {
	// Create home with dummy provider and remove all
	// of its envtools.
	env, fake := makeEmptyFakeHome(c)
	defer fake.Restore()

	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), 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)
}
Example #18
0
func uploadTools(c *gc.C, env environs.Environ) {
	usefulVersion := version.Current
	usefulVersion.Series = config.PreferredSeries(env.Config())
	envtesting.AssertUploadFakeToolsVersions(c, env.Storage(), usefulVersion)
}