func waitForUnitStarted(stateConn *state.State, unit *state.Unit, c *gc.C) { timeout := time.After(5 * time.Second) for { select { case <-timeout: c.Fatalf("no activity detected") case <-time.After(coretesting.ShortWait): err := unit.Refresh() c.Assert(err, gc.IsNil) st, info, data, err := unit.Status() c.Assert(err, gc.IsNil) switch st { case params.StatusPending, params.StatusInstalled: c.Logf("waiting...") continue case params.StatusStarted: c.Logf("started!") return case params.StatusDown: stateConn.StartSync() c.Logf("unit is still down") default: c.Fatalf("unexpected status %s %s %v", st, info, data) } } } }
// initBootstrapUser creates the initial admin user for the database, and sets // the initial password. func initBootstrapUser(st *state.State, passwordHash string) error { logger.Debugf("adding admin user") // Set up initial authentication. u, err := st.AddUser("admin", "") if err != nil { return err } // Note that at bootstrap time, the password is set to // the hash of its actual value. The first time a client // connects to mongo, it changes the mongo password // to the original password. logger.Debugf("setting password hash for admin user") // TODO(jam): http://pad.lv/1248839 // We could teach bootstrap how to generate a custom salt and apply // that to the hash that was generated. At which point we'd need to set // it here. For now, we pass "" so that on first login we will create a // new salt, but the fixed-salt password is still available from // cloud-init. if err := u.SetPasswordHash(passwordHash, ""); err != nil { return err } if err := st.SetAdminMongoPassword(passwordHash); err != nil { return err } return nil }
// 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 secrets and API endpoints. auth, err := environs.NewEnvironAuthenticator(env) if err != nil { return nil, err } stateInfo, apiInfo, err := auth.SetupAuthentication(machine) if err != nil { return nil, err } // Find requested networks. includeNetworks, excludeNetworks, err := machine.RequestedNetworks() if err != nil { return nil, err } mcfg := environs.NewMachineConfig(machineId, nonce, includeNetworks, excludeNetworks, stateInfo, 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 }
func isRemoved(st *state.State, name string) func(*gc.C) bool { return func(c *gc.C) bool { _, err := st.Unit(name) if errors.IsNotFound(err) { return true } c.Assert(err, gc.IsNil) return false } }
// New returns a new worker that maintains the mongo replica set // with respect to the given state. func New(st *state.State) (worker.Worker, error) { cfg, err := st.EnvironConfig() if err != nil { return nil, err } return newWorker(&stateShim{ State: st, mongoPort: cfg.StatePort(), apiPort: cfg.APIPort(), }, newPublisher(st)), nil }
func opClientServiceExpose(c *gc.C, st *api.State, mst *state.State) (func(), error) { err := st.Client().ServiceExpose("wordpress") if err != nil { return func() {}, err } return func() { svc, err := mst.Service("wordpress") c.Assert(err, gc.IsNil) svc.ClearExposed() }, nil }
// GetStorage creates an Environ from the config in state and returns // its storage interface. func GetStorage(st *state.State) (storage.Storage, error) { envConfig, err := st.EnvironConfig() if err != nil { return nil, fmt.Errorf("cannot get environment config: %v", err) } env, err := New(envConfig) if err != nil { return nil, fmt.Errorf("cannot access environment: %v", err) } return env.Storage(), nil }
func initUsersAndBootstrapMachine(c ConfigSetter, st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) { if err := initBootstrapUser(st, c.OldPassword()); err != nil { return nil, fmt.Errorf("cannot initialize bootstrap user: %v", err) } if err := st.SetEnvironConstraints(cfg.Constraints); err != nil { return nil, fmt.Errorf("cannot set initial environ constraints: %v", err) } m, err := initBootstrapMachine(c, st, cfg) if err != nil { return nil, fmt.Errorf("cannot initialize bootstrap machine: %v", err) } return m, nil }
func NewPeerRelation(c *gc.C, st *state.State) *PeerRelation { svc := state.AddTestingService(c, st, "riak", state.AddTestingCharm(c, st, "riak")) ep, err := svc.Endpoint("ring") c.Assert(err, gc.IsNil) rel, err := st.EndpointsRelation(ep) c.Assert(err, gc.IsNil) pr := &PeerRelation{rel: rel, svc: svc} pr.u0, pr.ru0 = addRU(c, svc, rel, nil) pr.u1, pr.ru1 = addRU(c, svc, rel, nil) pr.u2, pr.ru2 = addRU(c, svc, rel, nil) pr.u3, pr.ru3 = addRU(c, svc, rel, nil) return pr }
// NewConnFromState returns a Conn that uses an Environ // made by reading the environment configuration. // The resulting Conn uses the given State - closing // it will close that State. func NewConnFromState(st *state.State) (*Conn, error) { cfg, err := st.EnvironConfig() if err != nil { return nil, err } environ, err := environs.New(cfg) if err != nil { return nil, err } return &Conn{ Environ: environ, State: st, }, nil }
// AddStateServerMachine adds a "state server" machine to the state so // that State.Addresses and State.APIAddresses will work. It returns the // added machine. The addresses that those methods will return bear no // relation to the addresses actually used by the state and API servers. // It returns the addresses that will be returned by the State.Addresses // and State.APIAddresses methods, which will not bear any relation to // the be the addresses used by the state servers. func AddStateServerMachine(c *gc.C, st *state.State) *state.Machine { machine, err := st.AddMachine("quantal", state.JobManageEnviron) c.Assert(err, gc.IsNil) err = machine.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) c.Assert(err, gc.IsNil) hostPorts := [][]instance.HostPort{{{ Address: instance.NewAddress("0.1.2.3", instance.NetworkUnknown), Port: 1234, }}} err = st.SetAPIHostPorts(hostPorts) c.Assert(err, gc.IsNil) return machine }
// fetchAllDeployedCharms returns a map from service name to service // and a map from service name to unit name to unit. func fetchAllDeployedCharms(st *state.State) (map[string]*charm.URL, error) { deployedCharms := make(map[string]*charm.URL) services, err := st.AllServices() if err != nil { return nil, err } for _, s := range services { url, _ := s.CharmURL() // Record the basic charm information so it can be bulk processed later to // get the available revision numbers from the repo. baseCharm := url.WithRevision(-1) deployedCharms[baseCharm.String()] = baseCharm } return deployedCharms, nil }
// opRecvTimeout waits for any of the given kinds of operation to // be received from ops, and times out if not. func opRecvTimeout(c *gc.C, st *state.State, opc <-chan dummy.Operation, kinds ...dummy.Operation) dummy.Operation { st.StartSync() for { select { case op := <-opc: for _, k := range kinds { if reflect.TypeOf(op) == reflect.TypeOf(k) { return op } } c.Logf("discarding unknown event %#v", op) case <-time.After(15 * time.Second): c.Fatalf("time out wating for operation") } } }
// upgradeWorker runs the required upgrade operations to upgrade to the current Juju version. func (a *MachineAgent) upgradeWorker( apiState *api.State, jobs []params.MachineJob, agentConfig agent.Config, ) worker.Worker { return worker.NewSimpleWorker(func(stop <-chan struct{}) error { select { case <-a.upgradeComplete: // Our work is already done (we're probably being restarted // because the API connection has gone down), so do nothing. <-stop return nil default: } // If the machine agent is a state server, wait until state is opened. needsState := false for _, job := range jobs { if job == params.JobManageEnviron { needsState = true } } // We need a *state.State for upgrades. We open it independently // of StateWorker, because we have no guarantees about when // and how often StateWorker might run. var st *state.State if needsState { var err error info, ok := agentConfig.StateInfo() if !ok { return fmt.Errorf("no state info available") } st, err = state.Open(info, state.DialOpts{}, environs.NewStatePolicy()) if err != nil { return err } defer st.Close() } err := a.runUpgrades(st, apiState, jobs, agentConfig) if err != nil { return err } logger.Infof("upgrade to %v completed.", version.Current) close(a.upgradeComplete) <-stop return nil }) }
// initBootstrapMachine initializes the initial bootstrap machine in state. func initBootstrapMachine(c ConfigSetter, st *state.State, cfg BootstrapMachineConfig) (*state.Machine, error) { logger.Infof("initialising bootstrap machine with config: %+v", cfg) jobs := make([]state.MachineJob, len(cfg.Jobs)) for i, job := range cfg.Jobs { machineJob, err := state.MachineJobFromParams(job) if err != nil { return nil, fmt.Errorf("invalid bootstrap machine job %q: %v", job, err) } jobs[i] = machineJob } m, err := st.AddOneMachine(state.MachineTemplate{ Addresses: cfg.Addresses, Series: version.Current.Series, Nonce: state.BootstrapNonce, Constraints: cfg.Constraints, InstanceId: cfg.InstanceId, HardwareCharacteristics: cfg.Characteristics, Jobs: jobs, }) if err != nil { return nil, fmt.Errorf("cannot create bootstrap machine in state: %v", err) } if m.Id() != BootstrapMachineId { return nil, fmt.Errorf("bootstrap machine expected id 0, got %q", m.Id()) } // Read the machine agent's password and change it to // a new password (other agents will change their password // via the API connection). logger.Debugf("create new random password for machine %v", m.Id()) newPassword, err := utils.RandomPassword() if err != nil { return nil, err } if err := m.SetPassword(newPassword); err != nil { return nil, err } if err := m.SetMongoPassword(newPassword); err != nil { return nil, err } c.SetPassword(newPassword) return m, nil }
func checkCreds(st *state.State, c params.Creds) (taggedAuthenticator, error) { entity0, err := st.FindEntity(c.AuthTag) if err != nil && !errors.IsNotFound(err) { return nil, err } // We return the same error when an entity // does not exist as for a bad password, so that // we don't allow unauthenticated users to find information // about existing entities. entity, ok := entity0.(taggedAuthenticator) if !ok { return nil, common.ErrBadCreds } if err != nil || !entity.PasswordValid(c.Password) { return nil, common.ErrBadCreds } // Check if a machine agent is logging in with the right Nonce if err := checkForValidMachineAgent(entity, c); err != nil { return nil, err } return entity, nil }
// addServiceUnits adds a given number of units to a service. func addServiceUnits(state *state.State, args params.AddServiceUnits) ([]*state.Unit, error) { service, err := state.Service(args.ServiceName) if err != nil { return nil, err } if args.NumUnits < 1 { return nil, fmt.Errorf("must add at least one unit") } if args.NumUnits > 1 && args.ToMachineSpec != "" { return nil, fmt.Errorf("cannot use NumUnits with ToMachineSpec") } return juju.AddUnits(state, service, args.NumUnits, args.ToMachineSpec) }
// initAPIHostPorts sets the initial API host/port addresses in state. func initAPIHostPorts(c ConfigSetter, st *state.State, addrs []instance.Address, apiPort int) error { hostPorts := instance.AddressesWithPort(addrs, apiPort) return st.SetAPIHostPorts([][]instance.HostPort{hostPorts}) }
// SetAgentVersion sets the current agent version in the state's // environment configuration. // This is similar to state.SetEnvironAgentVersion but it doesn't require that // the environment have all agents at the same version already. func SetAgentVersion(st *state.State, vers version.Number) error { return st.UpdateEnvironConfig(map[string]interface{}{"agent-version": vers.String()}, nil, nil) }
func SetSSLHostnameVerification(c *gc.C, st *state.State, SSLHostnameVerification bool) { err := st.UpdateEnvironConfig(map[string]interface{}{"ssl-hostname-verification": SSLHostnameVerification}, nil, nil) c.Assert(err, gc.IsNil) }
// breakDummyProvider changes the environment config in state in a way // that causes the given environMethod of the dummy provider to return // an error, which is also returned as a message to be checked. func breakDummyProvider(c *gc.C, st *state.State, environMethod string) string { attrs := map[string]interface{}{"broken": environMethod} err := st.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, gc.IsNil) return fmt.Sprintf("dummy.%s is broken", environMethod) }
// Run initializes state for an environment. func (c *BootstrapCommand) Run(_ *cmd.Context) error { envCfg, err := config.New(config.NoDefaults, c.EnvConfig) if err != nil { return err } err = c.ReadConfig("machine-0") if err != nil { return err } agentConfig := c.CurrentConfig() // agent.Jobs is an optional field in the agent config, and was // introduced after 1.17.2. We default to allowing units on // machine-0 if missing. jobs := agentConfig.Jobs() if len(jobs) == 0 { jobs = []params.MachineJob{ params.JobManageEnviron, params.JobHostUnits, } } // Get the bootstrap machine's addresses from the provider. env, err := environs.New(envCfg) if err != nil { return err } instanceId := instance.Id(c.InstanceId) instances, err := env.Instances([]instance.Id{instanceId}) if err != nil { return err } addrs, err := instances[0].Addresses() if err != nil { return err } // Create system-identity file if err := agent.WriteSystemIdentityFile(agentConfig); err != nil { return err } // Generate a shared secret for the Mongo replica set, and write it out. sharedSecret, err := mongo.GenerateSharedSecret() if err != nil { return err } info, ok := agentConfig.StateServingInfo() if !ok { return fmt.Errorf("bootstrap machine config has no state serving info") } info.SharedSecret = sharedSecret err = c.ChangeConfig(func(agentConfig agent.ConfigSetter) { agentConfig.SetStateServingInfo(info) }) if err != nil { return fmt.Errorf("cannot write agent config: %v", err) } agentConfig = c.CurrentConfig() if err := c.startMongo(addrs, agentConfig); err != nil { return err } logger.Infof("started mongo") // Initialise state, and store any agent config (e.g. password) changes. var st *state.State var m *state.Machine err = nil writeErr := c.ChangeConfig(func(agentConfig agent.ConfigSetter) { st, m, err = agent.InitializeState( agentConfig, envCfg, agent.BootstrapMachineConfig{ Addresses: addrs, Constraints: c.Constraints, Jobs: jobs, InstanceId: instanceId, Characteristics: c.Hardware, SharedSecret: sharedSecret, }, state.DefaultDialOpts(), environs.NewStatePolicy(), ) }) if writeErr != nil { return fmt.Errorf("cannot write initial configuration: %v", err) } if err != nil { return err } defer st.Close() // bootstrap machine always gets the vote return m.SetHasVote(true) }