// startBootstrapInstance starts the bootstrap instance for this environment. func (env *azureEnviron) startBootstrapInstance(cons constraints.Value) (instance.Instance, error) { // The bootstrap instance gets machine id "0". This is not related to // instance ids or anything in Azure. Juju assigns the machine ID. const machineID = "0" // Create an empty bootstrap state file so we can get its URL. // It will be updated with the instance id and hardware characteristics after the // bootstrap instance is started. err := env.Storage().Put(environs.StateFile, strings.NewReader(""), 0) if err != nil { return nil, fmt.Errorf("cannot create bootstrap state file: %v", err) } stateFileURL, err := env.Storage().URL(environs.StateFile) if err != nil { return nil, fmt.Errorf("cannot get URL for bootstrap state file: %v", err) } machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL) logger.Debugf("bootstrapping environment %q", env.Name()) possibleTools, err := environs.FindBootstrapTools(env, cons) if err != nil { return nil, err } inst, err := env.internalStartInstance(cons, possibleTools, machineConfig) if err != nil { return nil, fmt.Errorf("cannot start bootstrap instance: %v", err) } return inst, nil }
// startBootstrapNode starts the juju bootstrap node for this environment. func (env *maasEnviron) startBootstrapNode(cons constraints.Value) (instance.Instance, error) { // The bootstrap instance gets machine id "0". This is not related to // instance ids or MAAS system ids. Juju assigns the machine ID. const machineID = "0" // Create an empty bootstrap state file so we can get its URL. // If will be updated with the instance id and hardware characteristics // after the bootstrap instance is started. reader := strings.NewReader("") err := env.Storage().Put(environs.StateFile, reader, int64(0)) if err != nil { return nil, fmt.Errorf("cannot create bootstrap state file: %v", err) } stateFileURL, err := env.Storage().URL(environs.StateFile) if err != nil { return nil, fmt.Errorf("cannot create bootstrap state file: %v", err) } logger.Debugf("bootstrapping environment %q", env.Name()) possibleTools, err := environs.FindBootstrapTools(env, cons) if err != nil { return nil, err } err = environs.CheckToolsSeries(possibleTools, env.Config().DefaultSeries()) if err != nil { return nil, err } machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL) inst, err := env.internalStartInstance(cons, possibleTools, machineConfig) if err != nil { return nil, fmt.Errorf("cannot start bootstrap instance: %v", err) } return inst, nil }
func (e *environ) Bootstrap(cons constraints.Value) error { 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") } possibleTools, err := environs.FindBootstrapTools(e, cons) if err != nil { return err } log.Infof("environs/dummy: would pick tools from %s", possibleTools) cfg, err := environs.BootstrapConfig(e.Config()) if err != nil { return fmt.Errorf("cannot make bootstrap config: %v", err) } e.state.mu.Lock() defer e.state.mu.Unlock() if e.state.bootstrapped { return fmt.Errorf("environment is already bootstrapped") } 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()) if err != nil { panic(err) } if err := st.SetEnvironConstraints(cons); err != nil { panic(err) } if err := st.SetAdminMongoPassword(utils.PasswordHash(password)); err != nil { panic(err) } _, err = st.AddUser("admin", password) if err != nil { panic(err) } e.state.apiServer, err = apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert), []byte(testing.ServerKey)) if err != nil { panic(err) } e.state.apiState = st } e.state.bootstrapped = true e.state.ops <- OpBootstrap{Env: e.state.name, Constraints: cons} return nil }
// TODO(bug 1199847): This work can be shared between providers. func (e *environ) Bootstrap(cons constraints.Value) error { // The bootstrap instance gets machine id "0". This is not related // to instance ids. Juju assigns the machine ID. const machineID = "0" log.Infof("environs/openstack: bootstrapping environment %q", e.name) if err := environs.VerifyBootstrapInit(e, shortAttempt); err != nil { return err } possibleTools, err := environs.FindBootstrapTools(e, cons) if err != nil { return err } err = environs.CheckToolsSeries(possibleTools, e.Config().DefaultSeries()) if err != nil { return err } // The client's authentication may have been reset by FindBootstrapTools() if the agent-version // attribute was updated so we need to re-authenticate. This will be a no-op if already authenticated. // An authenticated client is needed for the URL() call below. err = e.client.Authenticate() if err != nil { return err } stateFileURL, err := environs.CreateStateFile(e.Storage()) if err != nil { return err } machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL) // TODO(wallyworld) - save bootstrap machine metadata inst, characteristics, err := e.internalStartInstance(cons, possibleTools, machineConfig) if err != nil { return fmt.Errorf("cannot start bootstrap instance: %v", err) } err = environs.SaveState(e.Storage(), &environs.BootstrapState{ StateInstances: []instance.Id{inst.Id()}, Characteristics: []instance.HardwareCharacteristics{*characteristics}, }) if err != nil { // ignore error on StopInstance because the previous error is // more important. e.StopInstances([]instance.Instance{inst}) return fmt.Errorf("cannot save state: %v", err) } // TODO make safe in the case of racing Bootstraps // If two Bootstraps are called concurrently, there's // no way to use Swift to make sure that only one succeeds. // Perhaps consider using SimpleDB for state storage // which would enable that possibility. return nil }
func (env *localEnviron) setupLocalMachineAgent(cons constraints.Value) error { dataDir := env.config.rootDir() toolList, err := environs.FindBootstrapTools(env, cons) if err != nil { return err } // ensure we have at least one valid tools if len(toolList) == 0 { return fmt.Errorf("No bootstrap tools found") } // unpack the first tools into the agent dir. agentTools := toolList[0] logger.Debugf("tools: %#v", agentTools) // brutally abuse our knowledge of storage to directly open the file toolsUrl, err := url.Parse(agentTools.URL) toolsLocation := filepath.Join(env.config.storageDir(), toolsUrl.Path) logger.Infof("tools location: %v", toolsLocation) toolsFile, err := os.Open(toolsLocation) defer toolsFile.Close() // Again, brutally abuse our knowledge here. // The tools that FindBootstrapTools has returned us are based on the // default series in the config. However we are running potentially on a // different series. When the machine agent is started, it will be // looking based on the current series, so we need to override the series // returned in the tools to be the current series. agentTools.Version.Series = version.CurrentSeries() err = tools.UnpackTools(dataDir, agentTools, toolsFile) machineId := "0" // Always machine 0 tag := names.MachineTag(machineId) toolsDir := tools.SharedToolsDir(dataDir, agentTools.Version) logDir := env.config.logDir() logConfig := "--debug" // TODO(thumper): specify loggo config machineEnvironment := map[string]string{ "USER": env.config.user, "HOME": os.Getenv("HOME"), osenv.JujuProviderType: env.config.Type(), osenv.JujuStorageDir: env.config.storageDir(), osenv.JujuStorageAddr: env.config.storageAddr(), osenv.JujuSharedStorageDir: env.config.sharedStorageDir(), osenv.JujuSharedStorageAddr: env.config.sharedStorageAddr(), } agent := upstart.MachineAgentUpstartService( env.machineAgentServiceName(), toolsDir, dataDir, logDir, tag, machineId, logConfig, machineEnvironment) agent.InitDir = upstartScriptLocation logger.Infof("installing service %s to %s", env.machineAgentServiceName(), agent.InitDir) if err := agent.Install(); err != nil { logger.Errorf("could not install machine agent service: %v", err) return err } return nil }
// ensureToolsAvailability verifies the tools are available. If no tools are // found, it will automatically synchronize them. func (c *BootstrapCommand) ensureToolsAvailability(env environs.Environ, ctx *cmd.Context) error { // Capture possible logging while syncing and write it on the screen. loggo.RegisterWriter("bootstrap", sync.NewSyncLogWriter(ctx.Stdout, ctx.Stderr), loggo.INFO) defer loggo.RemoveWriter("bootstrap") // Try to find bootstrap tools. _, err := environs.FindBootstrapTools(env, c.Constraints) if errors.IsNotFoundError(err) { // Not tools available, so synchronize. sctx := &sync.SyncContext{ EnvName: c.EnvName, Source: c.Source, } if err = syncTools(sctx); err != nil { return err } // Synchronization done, try again. _, err = environs.FindBootstrapTools(env, c.Constraints) } else if err != nil { return err } return err }
// TODO(bug 1199847): Much of this work can be shared between providers. func (e *environ) Bootstrap(cons constraints.Value) error { // The bootstrap instance gets machine id "0". This is not related to // instance ids. Juju assigns the machine ID. const machineID = "0" log.Infof("environs/ec2: bootstrapping environment %q", e.name) // If the state file exists, it might actually have just been // removed by Destroy, and eventual consistency has not caught // up yet, so we retry to verify if that is happening. if err := environs.VerifyBootstrapInit(e, shortAttempt); err != nil { return err } possibleTools, err := environs.FindBootstrapTools(e, cons) if err != nil { return err } err = environs.CheckToolsSeries(possibleTools, e.Config().DefaultSeries()) if err != nil { return err } stateFileURL, err := e.Storage().URL(environs.StateFile) if err != nil { return fmt.Errorf("cannot create bootstrap state file: %v", err) } machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL) // TODO(wallyworld) - save bootstrap machine metadata inst, characteristics, err := e.internalStartInstance(cons, possibleTools, machineConfig) if err != nil { return fmt.Errorf("cannot start bootstrap instance: %v", err) } err = environs.SaveState(e.Storage(), &environs.BootstrapState{ StateInstances: []instance.Id{inst.Id()}, Characteristics: []instance.HardwareCharacteristics{*characteristics}, }) if err != nil { // ignore error on StopInstance because the previous error is // more important. e.StopInstances([]instance.Instance{inst}) return fmt.Errorf("cannot save state: %v", err) } // TODO make safe in the case of racing Bootstraps // If two Bootstraps are called concurrently, there's // no way to use S3 to make sure that only one succeeds. // Perhaps consider using SimpleDB for state storage // which would enable that possibility. return nil }
func (s *ToolsSuite) TestFindBootstrapTools(c *C) { for i, test := range findBootstrapToolsTests { c.Logf("\ntest %d: %s", i, test.info) attrs := map[string]interface{}{ "development": test.development, "default-series": test.defaultSeries, } if test.agentVersion != version.Zero { attrs["agent-version"] = test.agentVersion.String() } s.Reset(c, attrs) version.Current = test.cliVersion available := s.uploadPrivate(c, test.available...) if len(available) > 0 { // These should never be chosen. s.uploadPublic(c, vAll...) } cons := constraints.MustParse(test.constraints) actual, err := environs.FindBootstrapTools(s.env, cons) if test.err != nil { if len(actual) > 0 { c.Logf(actual.String()) } c.Check(err, DeepEquals, &errors.NotFoundError{test.err, ""}) continue } expect := map[version.Binary]string{} unique := map[version.Number]bool{} for _, expected := range test.expect { expect[expected] = available[expected] unique[expected.Number] = true } c.Check(actual.URLs(), DeepEquals, expect) for expectAgentVersion := range unique { agentVersion, ok := s.env.Config().AgentVersion() c.Check(ok, Equals, true) c.Check(agentVersion, Equals, expectAgentVersion) } } }