func (s *CloudInitSuite) TestFinishMachineConfigNonDefault(c *gc.C) { userTag := names.NewUserTag("not touched") attrs := dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", "ssl-hostname-verification": false, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) mcfg := &cloudinit.MachineConfig{ MongoInfo: &authentication.MongoInfo{Tag: userTag}, APIInfo: &api.Info{Tag: userTag}, } err = environs.FinishMachineConfig(mcfg, cfg, constraints.Value{}) c.Assert(err, gc.IsNil) c.Assert(mcfg, jc.DeepEquals, &cloudinit.MachineConfig{ AuthorizedKeys: "we-are-the-keys", AgentEnvironment: map[string]string{ agent.ProviderType: "dummy", agent.ContainerType: "", }, MongoInfo: &authentication.MongoInfo{Tag: userTag}, APIInfo: &api.Info{Tag: userTag}, DisableSSLHostnameVerification: true, PreferIPv6: true, }) }
func (s *configureSuite) getCloudConfig(c *gc.C, stateServer bool, vers version.Binary) *cloudinit.Config { var mcfg *envcloudinit.MachineConfig var err error if stateServer { mcfg, err = environs.NewBootstrapMachineConfig(constraints.Value{}, vers.Series) c.Assert(err, gc.IsNil) mcfg.InstanceId = "instance-id" mcfg.Jobs = []params.MachineJob{params.JobManageEnviron, params.JobHostUnits} } else { mcfg, err = environs.NewMachineConfig("0", "ya", imagemetadata.ReleasedStream, vers.Series, nil, nil, nil) c.Assert(err, gc.IsNil) mcfg.Jobs = []params.MachineJob{params.JobHostUnits} } mcfg.Tools = &tools.Tools{ Version: vers, URL: "http://testing.invalid/tools.tar.gz", } environConfig := testConfig(c, stateServer, vers) err = environs.FinishMachineConfig(mcfg, environConfig) c.Assert(err, gc.IsNil) cloudcfg := cloudinit.New() udata, err := envcloudinit.NewUserdataConfig(mcfg, cloudcfg) c.Assert(err, gc.IsNil) err = udata.Configure() c.Assert(err, gc.IsNil) return cloudcfg }
// MachineConfig returns information from the environment config that is // needed for machine cloud-init (for non-state servers only). // It is exposed for testing purposes. // TODO(rog) fix environs/manual tests so they do not need to // call this, or move this elsewhere. func MachineConfig(st *state.State, machineId, nonce, dataDir string) (*cloudinit.MachineConfig, error) { environConfig, err := st.EnvironConfig() if err != nil { return nil, err } // Get the machine so we can get its series and arch. // If the Arch is not set in hardware-characteristics, // an error is returned. machine, err := st.Machine(machineId) if err != nil { return nil, err } hc, err := machine.HardwareCharacteristics() if err != nil { return nil, err } if hc.Arch == nil { return nil, fmt.Errorf("arch is not set for %q", machine.Tag()) } // Find the appropriate tools information. env, err := environs.New(environConfig) if err != nil { return nil, err } tools, err := findInstanceTools(env, machine.Series(), *hc.Arch) if err != nil { return nil, err } // Find the API endpoints. apiInfo, err := environs.APIInfo(env) if err != nil { return nil, err } auth := authentication.NewAuthenticator(st.MongoConnectionInfo(), apiInfo) mongoInfo, apiInfo, err := auth.SetupAuthentication(machine) if err != nil { return nil, err } // Find requested networks. networks, err := machine.RequestedNetworks() if err != nil { return nil, err } mcfg := environs.NewMachineConfig(machineId, nonce, networks, mongoInfo, apiInfo) if dataDir != "" { mcfg.DataDir = dataDir } mcfg.Tools = tools err = environs.FinishMachineConfig(mcfg, environConfig, constraints.Value{}) if err != nil { return nil, err } return mcfg, nil }
// 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) (arch, series string, _ environs.BootstrapFinalizer, 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. series = config.PreferredSeries(env.Config()) availableTools, err := args.AvailableTools.Match(coretools.Filter{Series: series}) if err != nil { return "", "", nil, 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 "", "", nil, fmt.Errorf("no SSH client available") } machineConfig, err := environs.NewBootstrapMachineConfig(args.Constraints, series) if err != nil { return "", "", nil, err } machineConfig.EnableOSRefreshUpdate = env.Config().EnableOSRefreshUpdate() machineConfig.EnableOSUpgrade = env.Config().EnableOSUpgrade() fmt.Fprintln(ctx.GetStderr(), "Launching instance") inst, hw, _, err := env.StartInstance(environs.StartInstanceParams{ Constraints: args.Constraints, Tools: availableTools, MachineConfig: machineConfig, Placement: args.Placement, }) if err != nil { return "", "", nil, fmt.Errorf("cannot start bootstrap instance: %v", err) } fmt.Fprintf(ctx.GetStderr(), " - %s\n", inst.Id()) err = SaveState(env.Storage(), &BootstrapState{ StateInstances: []instance.Id{inst.Id()}, }) if err != nil { return "", "", nil, fmt.Errorf("cannot save state: %v", err) } finalize := func(ctx environs.BootstrapContext, mcfg *cloudinit.MachineConfig) error { mcfg.InstanceId = inst.Id() mcfg.HardwareCharacteristics = hw if err := environs.FinishMachineConfig(mcfg, env.Config()); err != nil { return err } return FinishBootstrap(ctx, client, inst, mcfg) } return *hw.Arch, series, finalize, nil }
func (s *CloudInitSuite) TestFinishInstanceConfig(c *gc.C) { userTag := names.NewUserTag("not-touched") expectedMcfg := &cloudinit.MachineConfig{ AuthorizedKeys: "we-are-the-keys", AgentEnvironment: map[string]string{ agent.ProviderType: "dummy", agent.ContainerType: "", }, MongoInfo: &mongo.MongoInfo{Tag: userTag}, APIInfo: &api.Info{Tag: userTag}, DisableSSLHostnameVerification: false, PreferIPv6: true, EnableOSRefreshUpdate: true, EnableOSUpgrade: true, } cfg, err := config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", })) c.Assert(err, gc.IsNil) mcfg := &cloudinit.MachineConfig{ MongoInfo: &mongo.MongoInfo{Tag: userTag}, APIInfo: &api.Info{Tag: userTag}, } err = environs.FinishMachineConfig(mcfg, cfg) c.Assert(err, gc.IsNil) c.Assert(mcfg, jc.DeepEquals, expectedMcfg) // Test when updates/upgrades are set to false. cfg, err = config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", "enable-os-refresh-update": false, "enable-os-upgrade": false, })) c.Assert(err, gc.IsNil) err = environs.FinishMachineConfig(mcfg, cfg) c.Assert(err, gc.IsNil) expectedMcfg.EnableOSRefreshUpdate = false expectedMcfg.EnableOSUpgrade = false c.Assert(mcfg, jc.DeepEquals, expectedMcfg) }
func (*cloudinitSuite) createMachineConfig(c *gc.C, environConfig *config.Config) *cloudinit.MachineConfig { machineId := "42" machineNonce := "fake-nonce" stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, machineNonce, nil, stateInfo, apiInfo) machineConfig.Tools = &tools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", } err := environs.FinishMachineConfig(machineConfig, environConfig, constraints.Value{}) c.Assert(err, gc.IsNil) return machineConfig }
func (*cloudinitSuite) createMachineConfig(c *gc.C, environConfig *config.Config) *cloudinit.MachineConfig { machineId := "42" machineNonce := "fake-nonce" stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig, err := environs.NewMachineConfig(machineId, machineNonce, imagemetadata.ReleasedStream, "quantal", nil, stateInfo, apiInfo) c.Assert(err, gc.IsNil) machineConfig.Tools = &tools.Tools{ Version: version.MustParseBinary("2.3.4-quantal-amd64"), URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz", } err = environs.FinishMachineConfig(machineConfig, environConfig) c.Assert(err, gc.IsNil) return machineConfig }
func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) { attrs := dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", "admin-secret": "lisboan-pork", "agent-version": "1.2.3", "state-server": false, }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) oldAttrs := cfg.AllAttrs() mcfg := &cloudinit.MachineConfig{ Bootstrap: true, } cons := constraints.MustParse("mem=1T cpu-power=999999999") err = environs.FinishMachineConfig(mcfg, cfg, cons) c.Assert(err, gc.IsNil) c.Check(mcfg.AuthorizedKeys, gc.Equals, "we-are-the-keys") c.Check(mcfg.DisableSSLHostnameVerification, jc.IsFalse) password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt) c.Check(mcfg.APIInfo, gc.DeepEquals, &api.Info{ Password: password, CACert: testing.CACert, }) c.Check(mcfg.StateInfo, gc.DeepEquals, &state.Info{ Password: password, Info: mongo.Info{CACert: testing.CACert}, }) c.Check(mcfg.StateServingInfo.StatePort, gc.Equals, cfg.StatePort()) c.Check(mcfg.StateServingInfo.APIPort, gc.Equals, cfg.APIPort()) c.Check(mcfg.Constraints, gc.DeepEquals, cons) oldAttrs["ca-private-key"] = "" oldAttrs["admin-secret"] = "" c.Check(mcfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs) srvCertPEM := mcfg.StateServingInfo.Cert srvKeyPEM := mcfg.StateServingInfo.PrivateKey _, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM) c.Check(err, gc.IsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now()) c.Assert(err, gc.IsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(9, 0, 0)) c.Assert(err, gc.IsNil) err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(10, 0, 1)) c.Assert(err, gc.NotNil) }
func (s *configureSuite) getCloudConfig(c *gc.C, stateServer bool, vers version.Binary) *cloudinit.Config { var mcfg *envcloudinit.MachineConfig if stateServer { mcfg = environs.NewBootstrapMachineConfig("private-key") mcfg.InstanceId = "instance-id" mcfg.Jobs = []params.MachineJob{params.JobManageEnviron, params.JobHostUnits} } else { mcfg = environs.NewMachineConfig("0", "ya", nil, nil, nil) mcfg.Jobs = []params.MachineJob{params.JobHostUnits} } mcfg.Tools = &tools.Tools{ Version: vers, URL: "file:///var/lib/juju/storage/" + envtools.StorageName(vers), } environConfig := testConfig(c, stateServer, vers) err := environs.FinishMachineConfig(mcfg, environConfig, constraints.Value{}) c.Assert(err, gc.IsNil) cloudcfg := cloudinit.New() err = envcloudinit.Configure(mcfg, cloudcfg) c.Assert(err, gc.IsNil) return cloudcfg }
func createContainer(c *gc.C, manager container.Manager, machineId string) instance.Instance { machineNonce := "fake-nonce" stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, machineNonce, nil, stateInfo, apiInfo) network := container.BridgeNetworkConfig("virbr0") machineConfig.Tools = &tools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", } environConfig := dummyConfig(c) err := environs.FinishMachineConfig(machineConfig, environConfig, constraints.Value{}) c.Assert(err, gc.IsNil) inst, hardware, err := manager.CreateContainer(machineConfig, "precise", network) c.Assert(err, gc.IsNil) c.Assert(hardware, gc.NotNil) expected := fmt.Sprintf("arch=%s cpu-cores=1 mem=512M root-disk=8192M", version.Current.Arch) c.Assert(hardware.String(), gc.Equals, expected) return inst }
func (s *CloudInitSuite) TestFinishInstanceConfig(c *gc.C) { attrs := dummySampleConfig().Merge(testing.Attrs{ "authorized-keys": "we-are-the-keys", }) cfg, err := config.New(config.NoDefaults, attrs) c.Assert(err, gc.IsNil) mcfg := &cloudinit.MachineConfig{ StateInfo: &state.Info{Tag: "not touched"}, APIInfo: &api.Info{Tag: "not touched"}, } err = environs.FinishMachineConfig(mcfg, cfg, constraints.Value{}) c.Assert(err, gc.IsNil) c.Assert(mcfg, gc.DeepEquals, &cloudinit.MachineConfig{ AuthorizedKeys: "we-are-the-keys", AgentEnvironment: map[string]string{ agent.ProviderType: "dummy", agent.ContainerType: "", }, StateInfo: &state.Info{Tag: "not touched"}, APIInfo: &api.Info{Tag: "not touched"}, DisableSSLHostnameVerification: false, }) }
// NewManualBootstrapEnviron wraps a LocalStorageEnviron with another which // overrides the Bootstrap method; when Bootstrap is invoked, the specified // host will be manually bootstrapped. // // InitUbuntuUser is expected to have been executed successfully against // the host being bootstrapped. func Bootstrap(args BootstrapArgs) (err error) { if args.Host == "" { return errors.New("host argument is empty") } if args.Environ == nil { return errors.New("environ argument is nil") } if args.DataDir == "" { return errors.New("data-dir argument is empty") } if args.Series == "" { return errors.New("series argument is empty") } if args.HardwareCharacteristics == nil { return errors.New("hardware characteristics argument is empty") } if len(args.PossibleTools) == 0 { return errors.New("possible tools is empty") } provisioned, err := checkProvisioned(args.Host) if err != nil { return fmt.Errorf("failed to check provisioned status: %v", err) } if provisioned { return ErrProvisioned } // Filter tools based on detected series/arch. logger.Infof("Filtering possible tools: %v", args.PossibleTools) possibleTools, err := args.PossibleTools.Match(tools.Filter{ Arch: *args.HardwareCharacteristics.Arch, Series: args.Series, }) if err != nil { return err } // Store the state file. If provisioning fails, we'll remove the file. logger.Infof("Saving bootstrap state file to bootstrap storage") bootstrapStorage := args.Environ.Storage() err = bootstrap.SaveState( bootstrapStorage, &bootstrap.BootstrapState{ StateInstances: []instance.Id{BootstrapInstanceId}, }, ) if err != nil { return err } defer func() { if err != nil { logger.Errorf("bootstrapping failed, removing state file: %v", err) bootstrapStorage.Remove(bootstrap.StateFile) } }() // If the tools are on the machine already, get a file:// scheme tools URL. tools := *possibleTools[0] storageDir := args.Environ.StorageDir() toolsStorageName := envtools.StorageName(tools.Version) if url, _ := bootstrapStorage.URL(toolsStorageName); url == tools.URL { tools.URL = fmt.Sprintf("file://%s/%s", storageDir, toolsStorageName) } // Add the local storage configuration. agentEnv, err := localstorage.StoreConfig(args.Environ) if err != nil { return err } privateKey, err := common.GenerateSystemSSHKey(args.Environ) if err != nil { return err } // Finally, provision the machine agent. mcfg := environs.NewBootstrapMachineConfig(privateKey) mcfg.InstanceId = BootstrapInstanceId mcfg.HardwareCharacteristics = args.HardwareCharacteristics if args.DataDir != "" { mcfg.DataDir = args.DataDir } mcfg.Tools = &tools err = environs.FinishMachineConfig(mcfg, args.Environ.Config(), constraints.Value{}) if err != nil { return err } for k, v := range agentEnv { mcfg.AgentEnvironment[k] = v } return provisionMachineAgent(args.Host, mcfg, args.Context.GetStderr()) }
// MachineConfig returns information from the environment config that // is needed for machine cloud-init (for non-state servers only). It // is exposed for testing purposes. // TODO(rog) fix environs/manual tests so they do not need to call this, or move this elsewhere. func MachineConfig(st *state.State, machineId, nonce, dataDir string) (*cloudinit.MachineConfig, error) { environConfig, err := st.EnvironConfig() if err != nil { return nil, err } // Get the machine so we can get its series and arch. // If the Arch is not set in hardware-characteristics, // an error is returned. machine, err := st.Machine(machineId) if err != nil { return nil, err } hc, err := machine.HardwareCharacteristics() if err != nil { return nil, err } if hc.Arch == nil { return nil, fmt.Errorf("arch is not set for %q", machine.Tag()) } // Find the appropriate tools information. agentVersion, ok := environConfig.AgentVersion() if !ok { return nil, errors.New("no agent version set in environment configuration") } environment, err := st.Environment() if err != nil { return nil, err } urlGetter := common.NewToolsURLGetter(environment.UUID(), st) toolsFinder := common.NewToolsFinder(st, st, urlGetter) findToolsResult, err := toolsFinder.FindTools(params.FindToolsParams{ Number: agentVersion, MajorVersion: -1, MinorVersion: -1, Series: machine.Series(), Arch: *hc.Arch, }) if err != nil { return nil, err } if findToolsResult.Error != nil { return nil, findToolsResult.Error } tools := findToolsResult.List[0] // Find the API endpoints. env, err := environs.New(environConfig) if err != nil { return nil, err } apiInfo, err := environs.APIInfo(env) if err != nil { return nil, err } auth := authentication.NewAuthenticator(st.MongoConnectionInfo(), apiInfo) mongoInfo, apiInfo, err := auth.SetupAuthentication(machine) if err != nil { return nil, err } // Find requested networks. networks, err := machine.RequestedNetworks() if err != nil { return nil, err } mcfg, err := environs.NewMachineConfig(machineId, nonce, env.Config().ImageStream(), machine.Series(), networks, mongoInfo, apiInfo) if err != nil { return nil, err } if dataDir != "" { mcfg.DataDir = dataDir } mcfg.Tools = tools err = environs.FinishMachineConfig(mcfg, environConfig) if err != nil { return nil, err } return mcfg, nil }