Пример #1
0
func nullContext() environs.BootstrapContext {
	ctx, _ := cmd.DefaultContext()
	ctx.Stdin = io.LimitReader(nil, 0)
	ctx.Stdout = ioutil.Discard
	ctx.Stderr = ioutil.Discard
	return modelcmd.BootstrapContext(ctx)
}
Пример #2
0
// mockTestingEnvConfig prepares an environment configuration using
// the mock provider which does not support networking.
func mockTestingEnvConfig(c *gc.C) *config.Config {
	cfg, err := config.New(config.NoDefaults, mockConfig())
	c.Assert(err, jc.ErrorIsNil)
	env, err := environs.Prepare(cfg, modelcmd.BootstrapContext(coretesting.Context(c)), configstore.NewMem())
	c.Assert(err, jc.ErrorIsNil)
	return env.Config()
}
Пример #3
0
func (s *bootstrapSuite) TestBootstrapGUISuccessLocal(c *gc.C) {
	path := makeGUIArchive(c, "jujugui-2.2.0")
	s.PatchEnvironment("JUJU_GUI", path)
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, "Preparing for Juju GUI 2.2.0 installation from local archive\n")

	// Check GUI URL and version.
	c.Assert(env.instanceConfig.GUI.URL, gc.Equals, "file://"+path)
	c.Assert(env.instanceConfig.GUI.Version.String(), gc.Equals, "2.2.0")

	// Check GUI size.
	f, err := os.Open(path)
	c.Assert(err, jc.ErrorIsNil)
	defer f.Close()
	info, err := f.Stat()
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(env.instanceConfig.GUI.Size, gc.Equals, info.Size())

	// Check GUI hash.
	h := sha256.New()
	_, err = io.Copy(h, f)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(env.instanceConfig.GUI.SHA256, gc.Equals, fmt.Sprintf("%x", h.Sum(nil)))
}
Пример #4
0
func (s *bootstrapSuite) TestBootstrapGUISuccessRemote(c *gc.C) {
	s.PatchValue(bootstrap.GUIFetchMetadata, func(stream string, sources ...simplestreams.DataSource) ([]*gui.Metadata, error) {
		c.Assert(stream, gc.Equals, gui.ReleasedStream)
		c.Assert(sources[0].Description(), gc.Equals, "gui simplestreams")
		c.Assert(sources[0].RequireSigned(), jc.IsTrue)
		return []*gui.Metadata{{
			Version:  version.MustParse("2.0.42"),
			FullPath: "https://1.2.3.4/juju-gui-2.0.42.tar.bz2",
			SHA256:   "hash-2.0.42",
			Size:     42,
		}, {
			Version:  version.MustParse("2.0.47"),
			FullPath: "https://1.2.3.4/juju-gui-2.0.47.tar.bz2",
			SHA256:   "hash-2.0.47",
			Size:     47,
		}}, nil
	})
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{
		GUIDataSourceBaseURL: "https://1.2.3.4/gui/sources",
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, "Preparing for Juju GUI 2.0.42 release installation\n")

	// The most recent GUI release info has been stored.
	c.Assert(env.instanceConfig.GUI.URL, gc.Equals, "https://1.2.3.4/juju-gui-2.0.42.tar.bz2")
	c.Assert(env.instanceConfig.GUI.Version.String(), gc.Equals, "2.0.42")
	c.Assert(env.instanceConfig.GUI.Size, gc.Equals, int64(42))
	c.Assert(env.instanceConfig.GUI.SHA256, gc.Equals, "hash-2.0.42")
}
Пример #5
0
func (s *Suite) SetUpTest(c *gc.C) {
	// Set up InitialConfig with a dummy provider configuration. This
	// is required to allow model import test to work.
	env, err := environs.Prepare(
		modelcmd.BootstrapContext(testing.Context(c)),
		jujuclienttesting.NewMemStore(),
		environs.PrepareParams{
			ControllerName: "dummycontroller",
			BaseConfig:     dummy.SampleConfig(),
			CloudName:      "dummy",
		},
	)
	c.Assert(err, jc.ErrorIsNil)
	s.InitialConfig = testing.CustomModelConfig(c, env.Config().AllAttrs())

	// The call up to StateSuite's SetUpTest uses s.InitialConfig so
	// it has to happen here.
	s.StateSuite.SetUpTest(c)

	s.resources = common.NewResources()
	s.AddCleanup(func(*gc.C) { s.resources.StopAll() })

	s.authorizer = apiservertesting.FakeAuthorizer{
		Tag: s.Owner,
	}
}
Пример #6
0
func (s *bootstrapSuite) TestBootstrapGUIErrorNotFound(c *gc.C) {
	s.PatchEnvironment("JUJU_GUI", "/no/such/file")
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, `Cannot use Juju GUI at "/no/such/file": cannot open Juju GUI archive:`)
}
Пример #7
0
func (s *bootstrapSuite) TestBootstrapGUISuccessNoGUI(c *gc.C) {
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, "Juju GUI installation has been disabled\n")
	c.Assert(env.instanceConfig.GUI, gc.IsNil)
}
Пример #8
0
func (s *bootstrapSuite) TestBootstrapGUIErrorUnexpectedArchive(c *gc.C) {
	path := makeGUIArchive(c, "not-a-gui")
	s.PatchEnvironment("JUJU_GUI", path)
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, fmt.Sprintf("Cannot use Juju GUI at %q: cannot find Juju GUI version", path))
}
Пример #9
0
func (s *bootstrapSuite) TestBootstrapGUIErrorInvalidArchive(c *gc.C) {
	path := filepath.Join(c.MkDir(), "gui.bz2")
	err := ioutil.WriteFile(path, []byte("invalid"), 0666)
	c.Assert(err, jc.ErrorIsNil)
	s.PatchEnvironment("JUJU_GUI", path)
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err = bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, fmt.Sprintf("Cannot use Juju GUI at %q: cannot read Juju GUI archive", path))
}
Пример #10
0
// resetJujuHome restores an new, clean Juju home environment without tools.
func resetJujuHome(c *gc.C, envName string) environs.Environ {
	jenvDir := testing.HomePath(".juju", "models")
	err := os.RemoveAll(jenvDir)
	c.Assert(err, jc.ErrorIsNil)
	coretesting.WriteEnvironments(c, modelConfig)
	dummy.Reset()
	store, err := configstore.Default()
	c.Assert(err, jc.ErrorIsNil)
	env, err := environs.PrepareFromName(envName, modelcmd.BootstrapContext(cmdtesting.NullContext(c)), store)
	c.Assert(err, jc.ErrorIsNil)
	return env
}
Пример #11
0
func (s *bootstrapSuite) TestBootstrapGUISuccessNoGUI(c *gc.C) {
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{
		ControllerConfig: coretesting.FakeControllerConfig(),
		AdminSecret:      "admin-secret",
		CAPrivateKey:     coretesting.CAKey,
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, "Juju GUI installation has been disabled\n")
	c.Assert(env.instanceConfig.Bootstrap.GUI, gc.IsNil)
}
Пример #12
0
func testingEnvConfig(c *gc.C) *config.Config {
	env, err := environs.Prepare(
		modelcmd.BootstrapContext(testing.Context(c)),
		jujuclienttesting.NewMemStore(),
		environs.PrepareParams{
			ControllerName: "dummycontroller",
			BaseConfig:     dummy.SampleConfig(),
			CloudName:      "dummy",
		},
	)
	c.Assert(err, jc.ErrorIsNil)
	return env.Config()
}
Пример #13
0
func (s *bootstrapSuite) TestBootstrapGUIStreamsFailure(c *gc.C) {
	s.PatchValue(bootstrap.GUIFetchMetadata, func(string, ...simplestreams.DataSource) ([]*gui.Metadata, error) {
		return nil, errors.New("bad wolf")
	})
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{
		GUIDataSourceBaseURL: "https://1.2.3.4/gui/sources",
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, "Unable to fetch Juju GUI info: bad wolf\n")
	c.Assert(env.instanceConfig.GUI, gc.IsNil)
}
Пример #14
0
func (s *bootstrapSuite) TestBootstrapGUIErrorInvalidVersion(c *gc.C) {
	path := makeGUIArchive(c, "jujugui-invalid")
	s.PatchEnvironment("JUJU_GUI", path)
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{
		ControllerConfig: coretesting.FakeControllerConfig(),
		AdminSecret:      "admin-secret",
		CAPrivateKey:     coretesting.CAKey,
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, fmt.Sprintf(`Cannot use Juju GUI at %q: cannot parse version "invalid"`, path))
}
Пример #15
0
// rebootstrap will bootstrap a new server in safe-mode (not killing any other agent)
// if there is no current server available to restore to.
func (c *restoreCommand) rebootstrap(ctx *cmd.Context, meta *params.BackupsMetadataResult) error {
	env, err := c.getEnvironFunc(c.ControllerName(), meta)
	if err != nil {
		return errors.Trace(err)
	}
	instanceIds, err := env.ControllerInstances()
	if err != nil && errors.Cause(err) != environs.ErrNotBootstrapped {
		return errors.Annotatef(err, "cannot determine controller instances")
	}
	if len(instanceIds) > 0 {
		inst, err := env.Instances(instanceIds)
		if err == nil {
			return errors.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst)
		}
		if err != environs.ErrNoInstances {
			return errors.Annotatef(err, "cannot detect whether old instance is still running")
		}
	}

	// We require a hosted model config to bootstrap. We'll fill in some defaults
	// just to get going. The restore will clear the initial state.
	hostedModelUUID, err := utils.NewUUID()
	if err != nil {
		return errors.Trace(err)
	}
	hostedModelConfig := map[string]interface{}{
		"name":         "default",
		config.UUIDKey: hostedModelUUID.String(),
	}

	args := bootstrap.BootstrapParams{
		ModelConstraints:  c.constraints,
		UploadTools:       c.uploadTools,
		BuildToolsTarball: sync.BuildToolsTarball,
		HostedModelConfig: hostedModelConfig,
	}
	if err := BootstrapFunc(modelcmd.BootstrapContext(ctx), env, args); err != nil {
		return errors.Annotatef(err, "cannot bootstrap new instance")
	}

	// New controller is bootstrapped, so now record the API address so
	// we can connect.
	err = common.SetBootstrapEndpointAddress(c.ClientStore(), c.ControllerName(), env)
	if err != nil {
		errors.Trace(err)
	}
	// To avoid race conditions when running scripted bootstraps, wait
	// for the controller's machine agent to be ready to accept commands
	// before exiting this bootstrap command.
	return c.waitForAgentFunc(ctx, &c.ModelCommandBase, c.ControllerName())
}
Пример #16
0
func (s *BootstrapSuite) TestSuccess(c *gc.C) {
	s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
	stor := newStorage(s, c)
	checkInstanceId := "i-success"
	checkHardware := instance.MustParseHardware("arch=ppc64el mem=2T")

	startInstance := func(
		_ string, _ constraints.Value, _ []string, _ tools.List, icfg *instancecfg.InstanceConfig,
	) (
		instance.Instance, *instance.HardwareCharacteristics, []network.InterfaceInfo, error,
	) {
		return &mockInstance{id: checkInstanceId}, &checkHardware, nil, nil
	}
	var mocksConfig = minimalConfig(c)
	var getConfigCalled int
	getConfig := func() *config.Config {
		getConfigCalled++
		return mocksConfig
	}
	setConfig := func(c *config.Config) error {
		mocksConfig = c
		return nil
	}

	env := &mockEnviron{
		storage:       stor,
		startInstance: startInstance,
		config:        getConfig,
		setConfig:     setConfig,
	}
	inner := coretesting.Context(c)
	ctx := modelcmd.BootstrapContext(inner)
	result, err := common.Bootstrap(ctx, env, environs.BootstrapParams{
		ControllerConfig: coretesting.FakeControllerConfig(),
		AvailableTools: tools.List{
			&tools.Tools{
				Version: version.Binary{
					Number: jujuversion.Current,
					Arch:   arch.HostArch(),
					Series: series.HostSeries(),
				},
			},
		}})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(result.Arch, gc.Equals, "ppc64el") // based on hardware characteristics
	c.Assert(result.Series, gc.Equals, config.PreferredSeries(mocksConfig))
	output := inner.Stderr.(*bytes.Buffer)
	lines := strings.Split(output.String(), "\n")
	c.Assert(len(lines), jc.GreaterThan, 1)
	c.Assert(lines[0], gc.Equals, "Some message")
}
Пример #17
0
func testingEnvConfig(c *gc.C) *config.Config {
	env, err := bootstrap.Prepare(
		modelcmd.BootstrapContext(testing.Context(c)),
		jujuclienttesting.NewMemStore(),
		bootstrap.PrepareParams{
			ControllerConfig: testing.FakeControllerConfig(),
			ControllerName:   "dummycontroller",
			ModelConfig:      dummy.SampleConfig(),
			Cloud:            dummy.SampleCloudSpec(),
			AdminSecret:      "admin-secret",
		},
	)
	c.Assert(err, jc.ErrorIsNil)
	return env.Config()
}
Пример #18
0
func (s *bootstrapSuite) TestBootstrapGUINoStreams(c *gc.C) {
	s.PatchValue(bootstrap.GUIFetchMetadata, func(string, ...simplestreams.DataSource) ([]*gui.Metadata, error) {
		return nil, nil
	})
	env := newEnviron("foo", useDefaultKeys, nil)
	ctx := coretesting.Context(c)
	err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, bootstrap.BootstrapParams{
		ControllerConfig:     coretesting.FakeControllerConfig(),
		AdminSecret:          "admin-secret",
		CAPrivateKey:         coretesting.CAKey,
		GUIDataSourceBaseURL: "https://1.2.3.4/gui/sources",
	})
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(coretesting.Stderr(ctx), jc.Contains, "No available Juju GUI archives found\n")
	c.Assert(env.instanceConfig.Bootstrap.GUI, gc.IsNil)
}
Пример #19
0
func (s *BootstrapSuite) TestBootstrapJenvWarning(c *gc.C) {
	const envName = "devenv"
	s.patchVersionAndSeries(c, envName)

	store, err := configstore.Default()
	c.Assert(err, jc.ErrorIsNil)
	ctx := coretesting.Context(c)
	environs.PrepareFromName(envName, modelcmd.BootstrapContext(ctx), store)

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

	_, errc := cmdtesting.RunCommand(ctx, newBootstrapCommand(), "-m", envName, "--auto-upgrade")
	c.Assert(<-errc, gc.IsNil)
	c.Assert(testWriter.Log(), jc.LogMatches, []string{"ignoring environments.yaml: using bootstrap config in .*"})
}
Пример #20
0
func environFromNameProductionFunc(
	ctx *cmd.Context,
	envName string,
	action string,
	ensureNotBootstrapped func(environs.Environ) error,
) (env environs.Environ, cleanup func(), err error) {

	store, err := configstore.Default()
	if err != nil {
		return nil, nil, err
	}

	envExisted := false
	if environInfo, err := store.ReadInfo(envName); err == nil {
		envExisted = true
		logger.Warningf(
			"ignoring environments.yaml: using bootstrap config in %s",
			environInfo.Location(),
		)
	} else if !errors.IsNotFound(err) {
		return nil, nil, err
	}

	cleanup = func() {
		// Distinguish b/t removing the jenv file or tearing down the
		// environment. We want to remove the jenv file if preparation
		// was not successful. We want to tear down the environment
		// only in the case where the environment didn't already
		// exist.
		if env == nil {
			logger.Debugf("Destroying model info.")
			destroyEnvInfo(ctx, envName, store, action)
		} else if !envExisted && ensureNotBootstrapped(env) != environs.ErrAlreadyBootstrapped {
			logger.Debugf("Destroying model.")
			destroyPreparedEnviron(ctx, env, store, action)
		}
	}

	if env, err = environs.PrepareFromName(envName, modelcmd.BootstrapContext(ctx), store); err != nil {
		return nil, cleanup, err
	}

	return env, cleanup, err
}
Пример #21
0
// rebootstrap will bootstrap a new server in safe-mode (not killing any other agent)
// if there is no current server available to restore to.
func (c *restoreCommand) rebootstrap(ctx *cmd.Context) error {
	store, err := configstore.Default()
	if err != nil {
		return errors.Trace(err)
	}
	cfg, err := c.Config(store, nil)
	if err != nil {
		return errors.Trace(err)
	}
	// Turn on safe mode so that the newly bootstrapped instance
	// will not destroy all the instances it does not know about.
	cfg, err = cfg.Apply(map[string]interface{}{
		"provisioner-safe-mode": true,
	})
	if err != nil {
		return errors.Annotatef(err, "cannot enable provisioner-safe-mode")
	}
	env, err := environs.New(cfg)
	if err != nil {
		return errors.Trace(err)
	}
	instanceIds, err := env.ControllerInstances()
	if err != nil {
		return errors.Annotatef(err, "cannot determine controller instances")
	}
	if len(instanceIds) == 0 {
		return errors.Errorf("no instances found; perhaps the model was not bootstrapped")
	}
	inst, err := env.Instances(instanceIds)
	if err == nil {
		return errors.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst)
	}
	if err != environs.ErrNoInstances {
		return errors.Annotatef(err, "cannot detect whether old instance is still running")
	}

	cons := c.constraints
	args := bootstrap.BootstrapParams{EnvironConstraints: cons, UploadTools: c.uploadTools}
	if err := bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), env, args); err != nil {
		return errors.Annotatef(err, "cannot bootstrap new instance")
	}
	return nil
}
Пример #22
0
func (s *ImportSuite) SetUpTest(c *gc.C) {
	// Specify the config to use for the controller model before calling
	// SetUpTest of the StateSuite, otherwise we get testing.ModelConfig(c).
	// The default provider type specified in the testing.ModelConfig function
	// is one that isn't registered as a valid provider. For our tests here we
	// need a real registered provider, so we use the dummy provider.
	// NOTE: make a better test provider.
	env, err := environs.Prepare(
		modelcmd.BootstrapContext(testing.Context(c)),
		jujuclienttesting.NewMemStore(),
		environs.PrepareParams{
			ControllerName: "dummycontroller",
			BaseConfig:     dummy.SampleConfig(),
			CloudName:      "dummy",
		},
	)
	c.Assert(err, jc.ErrorIsNil)

	s.InitialConfig = testing.CustomModelConfig(c, env.Config().AllAttrs())
	s.StateSuite.SetUpTest(c)
}
Пример #23
0
// rebootstrap will bootstrap a new server in safe-mode (not killing any other agent)
// if there is no current server available to restore to.
func (c *restoreCommand) rebootstrap(ctx *cmd.Context, meta *params.BackupsMetadataResult) error {
	env, err := c.getEnvironFunc(c.ControllerName(), meta)
	if err != nil {
		return errors.Trace(err)
	}
	instanceIds, err := env.ControllerInstances()
	if err != nil && errors.Cause(err) != environs.ErrNotBootstrapped {
		return errors.Annotatef(err, "cannot determine controller instances")
	}
	if len(instanceIds) > 0 {
		inst, err := env.Instances(instanceIds)
		if err == nil {
			return errors.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst)
		}
		if err != environs.ErrNoInstances {
			return errors.Annotatef(err, "cannot detect whether old instance is still running")
		}
	}

	// We require a hosted model config to bootstrap. We'll fill in some defaults
	// just to get going. The restore will clear the initial state.
	hostedModelUUID, err := utils.NewUUID()
	if err != nil {
		return errors.Trace(err)
	}
	hostedModelConfig := map[string]interface{}{
		"name":         "default",
		config.UUIDKey: hostedModelUUID.String(),
	}

	args := bootstrap.BootstrapParams{
		ModelConstraints:  c.constraints,
		UploadTools:       c.uploadTools,
		HostedModelConfig: hostedModelConfig,
	}
	if err := BootstrapFunc(modelcmd.BootstrapContext(ctx), env, args); err != nil {
		return errors.Annotatef(err, "cannot bootstrap new instance")
	}
	return nil
}
Пример #24
0
func (s *imageMetadataUpdateSuite) TestUpdateFromPublishedImagesForProviderWithNoRegions(c *gc.C) {
	// This will save all available image metadata.
	saved := []cloudimagemetadata.Metadata{}

	// testingEnvConfig prepares an environment configuration using
	// the dummy provider since it doesn't implement simplestreams.HasRegion.
	s.state.environConfig = func() (*config.Config, error) {
		cfg, err := config.New(config.NoDefaults, dummy.SampleConfig())
		c.Assert(err, jc.ErrorIsNil)
		env, err := environs.Prepare(cfg, modelcmd.BootstrapContext(testing.Context(c)), configstore.NewMem())
		c.Assert(err, jc.ErrorIsNil)
		return env.Config(), err
	}

	s.state.saveMetadata = func(m []cloudimagemetadata.Metadata) error {
		saved = append(saved, m...)
		return nil
	}

	err := s.api.UpdateFromPublishedImages()
	c.Assert(err, jc.ErrorIsNil)
	s.assertCalls(c, environConfig)
	c.Assert(saved, jc.SameContents, []cloudimagemetadata.Metadata{})
}
Пример #25
0
func (s *ModelCommandSuite) TestBootstrapContext(c *gc.C) {
	ctx := modelcmd.BootstrapContext(&cmd.Context{})
	c.Assert(ctx.ShouldVerifyCredentials(), jc.IsTrue)
}
Пример #26
0
// BootstrapContext creates a simple bootstrap execution context.
func BootstrapContext(c *gc.C) environs.BootstrapContext {
	return modelcmd.BootstrapContext(coretesting.Context(c))
}
Пример #27
0
// Run connects to the environment specified on the command line and bootstraps
// a juju in that environment if none already exists. If there is as yet no environments.yaml file,
// the user is informed how to create one.
func (c *bootstrapCommand) Run(ctx *cmd.Context) (resultErr error) {
	bootstrapFuncs := getBootstrapFuncs()

	envName := getModelName(c)
	if envName == "" {
		return errors.Errorf("the name of the model must be specified")
	}
	if err := checkProviderType(envName); errors.IsNotFound(err) {
		// This error will get handled later.
	} else if err != nil {
		return errors.Trace(err)
	}

	environ, cleanup, err := environFromName(
		ctx,
		envName,
		"Bootstrap",
		bootstrapFuncs.EnsureNotBootstrapped,
	)

	// If we error out for any reason, clean up the environment.
	defer func() {
		if resultErr != nil && cleanup != nil {
			if c.KeepBrokenEnvironment {
				logger.Warningf("bootstrap failed but --keep-broken was specified so model is not being destroyed.\n" +
					"When you are finished diagnosing the problem, remember to run juju destroy-model --force\n" +
					"to clean up the model.")
			} else {
				handleBootstrapError(ctx, resultErr, cleanup)
			}
		}
	}()

	// Handle any errors from environFromName(...).
	if err != nil {
		return errors.Annotatef(err, "there was an issue examining the model")
	}

	// Check to see if this environment is already bootstrapped. If it
	// is, we inform the user and exit early. If an error is returned
	// but it is not that the environment is already bootstrapped,
	// then we're in an unknown state.
	if err := bootstrapFuncs.EnsureNotBootstrapped(environ); nil != err {
		if environs.ErrAlreadyBootstrapped == err {
			logger.Warningf("This juju model is already bootstrapped. If you want to start a new Juju\nmodel, first run juju destroy-model to clean up, or switch to an\nalternative model.")
			return err
		}
		return errors.Annotatef(err, "cannot determine if model is already bootstrapped.")
	}

	// Block interruption during bootstrap. Providers may also
	// register for interrupt notification so they can exit early.
	interrupted := make(chan os.Signal, 1)
	defer close(interrupted)
	ctx.InterruptNotify(interrupted)
	defer ctx.StopInterruptNotify(interrupted)
	go func() {
		for _ = range interrupted {
			ctx.Infof("Interrupt signalled: waiting for bootstrap to exit")
		}
	}()

	// If --metadata-source is specified, override the default tools metadata source so
	// SyncTools can use it, and also upload any image metadata.
	var metadataDir string
	if c.MetadataSource != "" {
		metadataDir = ctx.AbsPath(c.MetadataSource)
	}

	// Merge environ and bootstrap-specific constraints.
	constraintsValidator, err := environ.ConstraintsValidator()
	if err != nil {
		return errors.Trace(err)
	}
	bootstrapConstraints, err := constraintsValidator.Merge(
		c.Constraints, c.BootstrapConstraints,
	)
	if err != nil {
		return errors.Trace(err)
	}
	logger.Infof("combined bootstrap constraints: %v", bootstrapConstraints)

	err = bootstrapFuncs.Bootstrap(modelcmd.BootstrapContext(ctx), environ, bootstrap.BootstrapParams{
		EnvironConstraints:   c.Constraints,
		BootstrapConstraints: bootstrapConstraints,
		BootstrapSeries:      c.BootstrapSeries,
		BootstrapImage:       c.BootstrapImage,
		Placement:            c.Placement,
		UploadTools:          c.UploadTools,
		AgentVersion:         c.AgentVersion,
		MetadataDir:          metadataDir,
	})
	if err != nil {
		return errors.Annotate(err, "failed to bootstrap model")
	}
	err = c.SetBootstrapEndpointAddress(environ)
	if err != nil {
		return errors.Annotate(err, "saving bootstrap endpoint address")
	}

	err = modelcmd.SetCurrentModel(ctx, envName)
	if err != nil {
		return errors.Trace(err)
	}

	// To avoid race conditions when running scripted bootstraps, wait
	// for the controller's machine agent to be ready to accept commands
	// before exiting this bootstrap command.
	return c.waitForAgentInitialisation(ctx)
}
Пример #28
0
// Run connects to the environment specified on the command line and bootstraps
// a juju in that environment if none already exists. If there is as yet no environments.yaml file,
// the user is informed how to create one.
func (c *bootstrapCommand) Run(ctx *cmd.Context) (resultErr error) {
	bootstrapFuncs := getBootstrapFuncs()

	// Get the cloud definition identified by c.Cloud. If c.Cloud does not
	// identify a cloud in clouds.yaml, but is the name of a provider, and
	// that provider implements environs.CloudRegionDetector, we'll
	// synthesise a Cloud structure with the detected regions and no auth-
	// types.
	cloud, err := jujucloud.CloudByName(c.Cloud)
	if errors.IsNotFound(err) {
		ctx.Verbosef("cloud %q not found, trying as a provider name", c.Cloud)
		provider, err := environs.Provider(c.Cloud)
		if errors.IsNotFound(err) {
			return errors.NewNotFound(nil, fmt.Sprintf("unknown cloud %q, please try %q", c.Cloud, "juju update-clouds"))
		} else if err != nil {
			return errors.Trace(err)
		}
		detector, ok := provider.(environs.CloudRegionDetector)
		if !ok {
			ctx.Verbosef(
				"provider %q does not support detecting regions",
				c.Cloud,
			)
			return errors.NewNotFound(nil, fmt.Sprintf("unknown cloud %q, please try %q", c.Cloud, "juju update-clouds"))
		}
		regions, err := detector.DetectRegions()
		if err != nil && !errors.IsNotFound(err) {
			// It's not an error to have no regions.
			return errors.Annotatef(err,
				"detecting regions for %q cloud provider",
				c.Cloud,
			)
		}
		cloud = &jujucloud.Cloud{
			Type:    c.Cloud,
			Regions: regions,
		}
	} else if err != nil {
		return errors.Trace(err)
	}
	if err := checkProviderType(cloud.Type); errors.IsNotFound(err) {
		// This error will get handled later.
	} else if err != nil {
		return errors.Trace(err)
	}

	// Get the credentials and region name.
	store := c.ClientStore()
	credential, credentialName, regionName, err := modelcmd.GetCredentials(
		store, c.Region, c.CredentialName, c.Cloud, cloud.Type,
	)
	if errors.IsNotFound(err) && c.CredentialName == "" {
		// No credential was explicitly specified, and no credential
		// was found in credentials.yaml; have the provider detect
		// credentials from the environment.
		ctx.Verbosef("no credentials found, checking environment")
		detected, err := modelcmd.DetectCredential(c.Cloud, cloud.Type)
		if errors.Cause(err) == modelcmd.ErrMultipleCredentials {
			return ambiguousCredentialError
		} else if err != nil {
			return errors.Trace(err)
		}
		// We have one credential so extract it from the map.
		var oneCredential jujucloud.Credential
		for _, oneCredential = range detected.AuthCredentials {
		}
		credential = &oneCredential
		regionName = c.Region
		if regionName == "" {
			regionName = detected.DefaultRegion
		}
		logger.Tracef("authenticating with region %q and %v", regionName, credential)
	} else if err != nil {
		return errors.Trace(err)
	}

	region, err := getRegion(cloud, c.Cloud, regionName)
	if err != nil {
		return errors.Trace(err)
	}

	hostedModelUUID, err := utils.NewUUID()
	if err != nil {
		return errors.Trace(err)
	}
	controllerUUID, err := utils.NewUUID()
	if err != nil {
		return errors.Trace(err)
	}

	// Create an environment config from the cloud and credentials.
	configAttrs := map[string]interface{}{
		"type":                   cloud.Type,
		"name":                   environs.ControllerModelName,
		config.UUIDKey:           controllerUUID.String(),
		config.ControllerUUIDKey: controllerUUID.String(),
	}
	userConfigAttrs, err := c.config.ReadAttrs(ctx)
	if err != nil {
		return errors.Trace(err)
	}
	for k, v := range userConfigAttrs {
		configAttrs[k] = v
	}
	logger.Debugf("preparing controller with config: %v", configAttrs)

	// Read existing current controller, account, model so we can clean up on error.
	var oldCurrentController string
	oldCurrentController, err = modelcmd.ReadCurrentController()
	if err != nil {
		return errors.Annotate(err, "error reading current controller")
	}

	defer func() {
		if resultErr == nil || errors.IsAlreadyExists(resultErr) {
			return
		}
		if oldCurrentController != "" {
			if err := modelcmd.WriteCurrentController(oldCurrentController); err != nil {
				logger.Warningf(
					"cannot reset current controller to %q: %v",
					oldCurrentController, err,
				)
			}
		}
		if err := store.RemoveController(c.controllerName); err != nil {
			logger.Warningf(
				"cannot destroy newly created controller %q details: %v",
				c.controllerName, err,
			)
		}
	}()

	environ, err := environsPrepare(
		modelcmd.BootstrapContext(ctx), store,
		environs.PrepareParams{
			BaseConfig:           configAttrs,
			ControllerName:       c.controllerName,
			CloudName:            c.Cloud,
			CloudRegion:          region.Name,
			CloudEndpoint:        region.Endpoint,
			CloudStorageEndpoint: region.StorageEndpoint,
			Credential:           *credential,
			CredentialName:       credentialName,
		},
	)
	if err != nil {
		return errors.Trace(err)
	}

	// Set the current model to the initial hosted model.
	accountName, err := store.CurrentAccount(c.controllerName)
	if err != nil {
		return errors.Trace(err)
	}
	if err := store.UpdateModel(c.controllerName, accountName, c.hostedModelName, jujuclient.ModelDetails{
		hostedModelUUID.String(),
	}); err != nil {
		return errors.Trace(err)
	}
	if err := store.SetCurrentModel(c.controllerName, accountName, c.hostedModelName); err != nil {
		return errors.Trace(err)
	}

	// Set the current controller so "juju status" can be run while
	// bootstrapping is underway.
	if err := modelcmd.WriteCurrentController(c.controllerName); err != nil {
		return errors.Trace(err)
	}

	cloudRegion := c.Cloud
	if region.Name != "" {
		cloudRegion = fmt.Sprintf("%s/%s", cloudRegion, region.Name)
	}
	ctx.Infof(
		"Creating Juju controller %q on %s",
		c.controllerName, cloudRegion,
	)

	// If we error out for any reason, clean up the environment.
	defer func() {
		if resultErr != nil {
			if c.KeepBrokenEnvironment {
				logger.Warningf(`
bootstrap failed but --keep-broken was specified so model is not being destroyed.
When you are finished diagnosing the problem, remember to run juju destroy-model --force
to clean up the model.`[1:])
			} else {
				handleBootstrapError(ctx, resultErr, func() error {
					return environsDestroy(
						c.controllerName, environ, store,
					)
				})
			}
		}
	}()

	// Block interruption during bootstrap. Providers may also
	// register for interrupt notification so they can exit early.
	interrupted := make(chan os.Signal, 1)
	defer close(interrupted)
	ctx.InterruptNotify(interrupted)
	defer ctx.StopInterruptNotify(interrupted)
	go func() {
		for _ = range interrupted {
			ctx.Infof("Interrupt signalled: waiting for bootstrap to exit")
		}
	}()

	// If --metadata-source is specified, override the default tools metadata source so
	// SyncTools can use it, and also upload any image metadata.
	var metadataDir string
	if c.MetadataSource != "" {
		metadataDir = ctx.AbsPath(c.MetadataSource)
	}

	// Merge environ and bootstrap-specific constraints.
	constraintsValidator, err := environ.ConstraintsValidator()
	if err != nil {
		return errors.Trace(err)
	}
	bootstrapConstraints, err := constraintsValidator.Merge(
		c.Constraints, c.BootstrapConstraints,
	)
	if err != nil {
		return errors.Trace(err)
	}
	logger.Infof("combined bootstrap constraints: %v", bootstrapConstraints)

	hostedModelConfig := map[string]interface{}{
		"name":         c.hostedModelName,
		config.UUIDKey: hostedModelUUID.String(),
	}

	// We copy across any user supplied attributes to the hosted model config.
	// But only if the attributes have not been removed from the controller
	// model config as part of preparing the controller model.
	controllerConfigAttrs := environ.Config().AllAttrs()
	for k, v := range userConfigAttrs {
		if _, ok := controllerConfigAttrs[k]; ok {
			hostedModelConfig[k] = v
		}
	}
	// Ensure that certain config attributes are not included in the hosted
	// model config. These attributes may be modified during bootstrap; by
	// removing them from this map, we ensure the modified values are
	// inherited.
	delete(hostedModelConfig, config.AuthKeysConfig)
	delete(hostedModelConfig, config.AgentVersionKey)

	// Check whether the Juju GUI must be installed in the controller.
	// Leaving this value empty means no GUI will be installed.
	var guiDataSourceBaseURL string
	if !c.noGUI {
		guiDataSourceBaseURL = common.GUIDataSourceBaseURL()
	}

	err = bootstrapFuncs.Bootstrap(modelcmd.BootstrapContext(ctx), environ, bootstrap.BootstrapParams{
		ModelConstraints:     c.Constraints,
		BootstrapConstraints: bootstrapConstraints,
		BootstrapSeries:      c.BootstrapSeries,
		BootstrapImage:       c.BootstrapImage,
		Placement:            c.Placement,
		UploadTools:          c.UploadTools,
		AgentVersion:         c.AgentVersion,
		MetadataDir:          metadataDir,
		HostedModelConfig:    hostedModelConfig,
		GUIDataSourceBaseURL: guiDataSourceBaseURL,
	})
	if err != nil {
		return errors.Annotate(err, "failed to bootstrap model")
	}

	if err := c.SetModelName(c.hostedModelName); err != nil {
		return errors.Trace(err)
	}

	err = c.setBootstrapEndpointAddress(environ)
	if err != nil {
		return errors.Annotate(err, "saving bootstrap endpoint address")
	}

	// To avoid race conditions when running scripted bootstraps, wait
	// for the controller's machine agent to be ready to accept commands
	// before exiting this bootstrap command.
	return c.waitForAgentInitialisation(ctx)
}
Пример #29
0
// rebootstrap will bootstrap a new server in safe-mode (not killing any other agent)
// if there is no current server available to restore to.
func (c *restoreCommand) rebootstrap(ctx *cmd.Context, meta *params.BackupsMetadataResult) error {
	params, err := c.getRebootstrapParamsFunc(ctx, c.ControllerName(), meta)
	if err != nil {
		return errors.Trace(err)
	}

	cloudParam, err := cloud.CloudByName(params.Cloud.Name)
	if errors.IsNotFound(err) {
		provider, err := environs.Provider(params.Cloud.Type)
		if errors.IsNotFound(err) {
			return errors.NewNotFound(nil, fmt.Sprintf("unknown cloud %q, please try %q", params.Cloud.Name, "juju update-clouds"))
		} else if err != nil {
			return errors.Trace(err)
		}
		detector, ok := provider.(environs.CloudRegionDetector)
		if !ok {
			return errors.Errorf("provider %q does not support detecting regions", params.Cloud.Type)
		}
		var cloudEndpoint string
		regions, err := detector.DetectRegions()
		if errors.IsNotFound(err) {
			// It's not an error to have no regions. If the
			// provider does not support regions, then we
			// reinterpret the supplied region name as the
			// cloud's endpoint. This enables the user to
			// supply, for example, maas/<IP> or manual/<IP>.
			if params.Cloud.Region != "" {
				cloudEndpoint = params.Cloud.Region
			}
		} else if err != nil {
			return errors.Annotatef(err, "detecting regions for %q cloud provider", params.Cloud.Type)
		}
		schemas := provider.CredentialSchemas()
		authTypes := make([]cloud.AuthType, 0, len(schemas))
		for authType := range schemas {
			authTypes = append(authTypes, authType)
		}
		cloudParam = &cloud.Cloud{
			Type:      params.Cloud.Type,
			AuthTypes: authTypes,
			Endpoint:  cloudEndpoint,
			Regions:   regions,
		}
	} else if err != nil {
		return errors.Trace(err)
	}

	env, err := c.newEnvironFunc(environs.OpenParams{
		Cloud:  params.Cloud,
		Config: params.ModelConfig,
	})
	if err != nil {
		return errors.Annotate(err, "opening environ for rebootstrapping")
	}

	instanceIds, err := env.ControllerInstances(params.ControllerConfig.ControllerUUID())
	if err != nil && errors.Cause(err) != environs.ErrNotBootstrapped {
		return errors.Annotatef(err, "cannot determine controller instances")
	}
	if len(instanceIds) > 0 {
		inst, err := env.Instances(instanceIds)
		if err == nil {
			return errors.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst)
		}
		if err != environs.ErrNoInstances {
			return errors.Annotatef(err, "cannot detect whether old instance is still running")
		}
	}

	// We require a hosted model config to bootstrap. We'll fill in some defaults
	// just to get going. The restore will clear the initial state.
	hostedModelUUID, err := utils.NewUUID()
	if err != nil {
		return errors.Trace(err)
	}
	hostedModelConfig := map[string]interface{}{
		"name":         "default",
		config.UUIDKey: hostedModelUUID.String(),
	}

	// We may have previous controller metadata. We need to replace that so it
	// will contain the new CA Cert and UUID required to connect to the newly
	// bootstrapped controller API.
	store := c.ClientStore()
	details := jujuclient.ControllerDetails{
		ControllerUUID: params.ControllerConfig.ControllerUUID(),
		CACert:         meta.CACert,
		Cloud:          params.Cloud.Name,
		CloudRegion:    params.Cloud.Region,
	}
	err = store.UpdateController(c.ControllerName(), details)
	if err != nil {
		return errors.Trace(err)
	}

	bootVers := version.Current
	args := bootstrap.BootstrapParams{
		Cloud:               *cloudParam,
		CloudName:           params.Cloud.Name,
		CloudRegion:         params.Cloud.Region,
		CloudCredentialName: params.CredentialName,
		CloudCredential:     params.Cloud.Credential,
		ModelConstraints:    c.constraints,
		BuildAgent:          c.buildAgent,
		BuildAgentTarball:   sync.BuildAgentTarball,
		ControllerConfig:    params.ControllerConfig,
		HostedModelConfig:   hostedModelConfig,
		BootstrapSeries:     meta.Series,
		AgentVersion:        &bootVers,
		AdminSecret:         params.AdminSecret,
		CAPrivateKey:        meta.CAPrivateKey,
		DialOpts: environs.BootstrapDialOpts{
			Timeout:        time.Second * bootstrap.DefaultBootstrapSSHTimeout,
			RetryDelay:     time.Second * bootstrap.DefaultBootstrapSSHRetryDelay,
			AddressesDelay: time.Second * bootstrap.DefaultBootstrapSSHAddressesDelay,
		},
	}
	if err := BootstrapFunc(modelcmd.BootstrapContext(ctx), env, args); err != nil {
		return errors.Annotatef(err, "cannot bootstrap new instance")
	}

	// New controller is bootstrapped, so now record the API address so
	// we can connect.
	apiPort := params.ControllerConfig.APIPort()
	err = common.SetBootstrapEndpointAddress(store, c.ControllerName(), bootVers, apiPort, env)
	if err != nil {
		return errors.Trace(err)
	}

	// To avoid race conditions when running scripted bootstraps, wait
	// for the controller's machine agent to be ready to accept commands
	// before exiting this bootstrap command.
	return c.waitForAgentFunc(ctx, &c.ModelCommandBase, c.ControllerName(), "default")
}
Пример #30
0
func (s *JujuConnSuite) setUpConn(c *gc.C) {
	if s.RootDir != "" {
		c.Fatal("JujuConnSuite.setUpConn without teardown")
	}
	s.RootDir = c.MkDir()
	s.oldHome = utils.Home()
	home := filepath.Join(s.RootDir, "/home/ubuntu")
	err := os.MkdirAll(home, 0777)
	c.Assert(err, jc.ErrorIsNil)
	utils.SetHome(home)

	err = os.MkdirAll(filepath.Join(home, ".local", "share"), 0777)
	c.Assert(err, jc.ErrorIsNil)

	s.oldJujuXDGDataHome = osenv.SetJujuXDGDataHome(filepath.Join(home, ".local", "share", "juju"))
	err = os.MkdirAll(osenv.JujuXDGDataHome(), 0777)
	c.Assert(err, jc.ErrorIsNil)

	err = os.MkdirAll(s.DataDir(), 0777)
	c.Assert(err, jc.ErrorIsNil)
	s.PatchEnvironment(osenv.JujuModelEnvKey, "admin")

	cfg, err := config.New(config.UseDefaults, (map[string]interface{})(s.sampleConfig()))
	c.Assert(err, jc.ErrorIsNil)

	s.ControllerStore = jujuclient.NewFileClientStore()

	ctx := testing.Context(c)
	environ, err := environs.Prepare(
		modelcmd.BootstrapContext(ctx),
		s.ControllerStore,
		environs.PrepareParams{
			BaseConfig:     cfg.AllAttrs(),
			Credential:     cloud.NewEmptyCredential(),
			ControllerName: ControllerName,
			CloudName:      "dummy",
		},
	)
	c.Assert(err, jc.ErrorIsNil)
	// sanity check we've got the correct environment.
	c.Assert(environ.Config().Name(), gc.Equals, "admin")
	s.PatchValue(&dummy.DataDir, s.DataDir())
	s.LogDir = c.MkDir()
	s.PatchValue(&dummy.LogDir, s.LogDir)

	versions := PreferredDefaultVersions(environ.Config(), version.Binary{
		Number: jujuversion.Current,
		Arch:   "amd64",
		Series: "precise",
	})
	current := version.Binary{
		Number: jujuversion.Current,
		Arch:   arch.HostArch(),
		Series: series.HostSeries(),
	}
	versions = append(versions, current)

	// Upload tools for both preferred and fake default series
	s.DefaultToolsStorageDir = c.MkDir()
	s.PatchValue(&tools.DefaultBaseURL, s.DefaultToolsStorageDir)
	stor, err := filestorage.NewFileStorageWriter(s.DefaultToolsStorageDir)
	c.Assert(err, jc.ErrorIsNil)
	// Upload tools to both release and devel streams since config will dictate that we
	// end up looking in both places.
	envtesting.AssertUploadFakeToolsVersions(c, stor, "released", "released", versions...)
	envtesting.AssertUploadFakeToolsVersions(c, stor, "devel", "devel", versions...)
	s.DefaultToolsStorage = stor

	s.PatchValue(&juju.JujuPublicKey, sstesting.SignedMetadataPublicKey)
	err = bootstrap.Bootstrap(modelcmd.BootstrapContext(ctx), environ, bootstrap.BootstrapParams{})
	c.Assert(err, jc.ErrorIsNil)

	s.BackingState = environ.(GetStater).GetStateInAPIServer()

	s.State, err = newState(environ, s.BackingState.MongoConnectionInfo())
	c.Assert(err, jc.ErrorIsNil)

	apiInfo, err := environs.APIInfo(environ)
	c.Assert(err, jc.ErrorIsNil)
	apiInfo.Tag = s.AdminUserTag(c)
	apiInfo.Password = environ.Config().AdminSecret()
	s.APIState, err = api.Open(apiInfo, api.DialOpts{})
	c.Assert(err, jc.ErrorIsNil)

	err = s.State.SetAPIHostPorts(s.APIState.APIHostPorts())
	c.Assert(err, jc.ErrorIsNil)

	// Make sure the controller store has the controller api endpoint address set
	controller, err := s.ControllerStore.ControllerByName(ControllerName)
	c.Assert(err, jc.ErrorIsNil)
	controller.APIEndpoints = []string{s.APIState.APIHostPorts()[0][0].String()}
	err = s.ControllerStore.UpdateController(ControllerName, *controller)
	c.Assert(err, jc.ErrorIsNil)
	err = modelcmd.WriteCurrentController(ControllerName)
	c.Assert(err, jc.ErrorIsNil)

	s.Environ = environ

	// Insert expected values...
	servingInfo := state.StateServingInfo{
		PrivateKey:   testing.ServerKey,
		Cert:         testing.ServerCert,
		CAPrivateKey: testing.CAKey,
		SharedSecret: "really, really secret",
		APIPort:      4321,
		StatePort:    1234,
	}
	s.State.SetStateServingInfo(servingInfo)
}