func (suite *StateSuite) TestLoadStateIntegratesWithSaveState(c *gc.C) { storage := suite.newStorage(c) state := bootstrap.BootstrapState{ StateInstances: []instance.Id{instance.Id("an-instance-id")}, } err := bootstrap.SaveState(storage, &state) c.Assert(err, gc.IsNil) storedState, err := bootstrap.LoadState(storage) c.Assert(err, gc.IsNil) c.Check(*storedState, gc.DeepEquals, state) }
// 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) }
func (suite *StateSuite) TestSaveStateWritesStateFile(c *gc.C) { stor := suite.newStorage(c) state := bootstrap.BootstrapState{ StateInstances: []instance.Id{instance.Id("an-instance-id")}, } marshaledState, err := goyaml.Marshal(state) c.Assert(err, gc.IsNil) err = bootstrap.SaveState(stor, &state) c.Assert(err, gc.IsNil) loadedState, err := storage.Get(stor, bootstrap.StateFile) c.Assert(err, gc.IsNil) content, err := ioutil.ReadAll(loadedState) c.Assert(err, gc.IsNil) c.Check(content, gc.DeepEquals, marshaledState) }
func (suite *environSuite) TestStateInfo(c *gc.C) { env := suite.makeEnviron() hostname := "test" input := `{"system_id": "system_id", "hostname": "` + hostname + `"}` node := suite.testMAASObject.TestServer.NewNode(input) testInstance := &maasInstance{maasObject: &node, environ: suite.makeEnviron()} err := bootstrap.SaveState( env.Storage(), &bootstrap.BootstrapState{StateInstances: []instance.Id{testInstance.Id()}}) c.Assert(err, gc.IsNil) stateInfo, apiInfo, err := env.StateInfo() c.Assert(err, gc.IsNil) cfg := env.Config() statePortSuffix := fmt.Sprintf(":%d", cfg.StatePort()) apiPortSuffix := fmt.Sprintf(":%d", cfg.APIPort()) c.Assert(stateInfo.Addrs, gc.DeepEquals, []string{hostname + statePortSuffix}) c.Assert(apiInfo.Addrs, gc.DeepEquals, []string{hostname + apiPortSuffix}) }
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 }