// 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 }
// fetchMachines returns a map from top level machine id to machines, where machines[0] is the host // machine and machines[1..n] are any containers (including nested ones). // // If machineIds is non-nil, only machines whose IDs are in the set are returned. func fetchMachines(st *state.State, machineIds *set.Strings) (map[string][]*state.Machine, error) { v := make(map[string][]*state.Machine) machines, err := st.AllMachines() if err != nil { return nil, err } // AllMachines gives us machines sorted by id. for _, m := range machines { if machineIds != nil && !machineIds.Contains(m.Id()) { continue } parentId, ok := m.ParentId() if !ok { // Only top level host machines go directly into the machine map. v[m.Id()] = []*state.Machine{m} } else { topParentId := state.TopParentId(m.Id()) machines, ok := v[topParentId] if !ok { panic(fmt.Errorf("unexpected machine id %q", parentId)) } machines = append(machines, m) v[topParentId] = machines } } return v, nil }
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) } } } }
// updateAllMachines finds all machines and resets the stored state address // in each of them. The address does not include the port. func updateAllMachines(st *state.State, stateAddr string) error { machines, err := st.AllMachines() if err != nil { return err } pendingMachineCount := 0 done := make(chan error) for _, machine := range machines { // A newly resumed state server requires no updating, and more // than one state server is not yet support by this plugin. if machine.IsManager() || machine.Life() == state.Dead { continue } pendingMachineCount++ machine := machine go func() { err := runMachineUpdate(machine, setAgentAddressScript(stateAddr)) if err != nil { logger.Errorf("failed to update machine %s: %v", machine, err) } else { progress("updated machine %s", machine) } done <- err }() } err = nil for ; pendingMachineCount > 0; pendingMachineCount-- { if updateErr := <-done; updateErr != nil && err == nil { err = fmt.Errorf("machine update failed") } } return err }
// getAllUnitNames returns a sequence of valid Unit objects from state. If any // of the service names or unit names are not found, an error is returned. func getAllUnitNames(st *state.State, units, services []string) (result []*state.Unit, err error) { unitsSet := set.NewStrings(units...) for _, name := range services { service, err := st.Service(name) if err != nil { return nil, err } units, err := service.AllUnits() if err != nil { return nil, err } for _, unit := range units { unitsSet.Add(unit.Name()) } } for _, unitName := range unitsSet.Values() { unit, err := st.Unit(unitName) if err != nil { return nil, err } // We only operate on principal units, and only thise that have an // assigned machines. if unit.IsPrincipal() { if _, err := unit.AssignedMachineId(); err != nil { return nil, err } } else { return nil, fmt.Errorf("%s is not a principal unit", unit) } result = append(result, unit) } return result, 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 } }
// 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 }
// 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 }
// fetchNetworks returns a map from network name to network. func fetchNetworks(st *state.State) (map[string]*state.Network, error) { networks, err := st.AllNetworks() if err != nil { return nil, err } out := make(map[string]*state.Network) for _, n := range networks { out[n.Name()] = n } return out, nil }
// destroyInstances directly destroys all non-manager, // non-manual machine instances. func destroyInstances(st *state.State, machines []*state.Machine) error { var ids []instance.Id for _, m := range machines { if m.IsManager() { continue } manual, err := m.IsManual() if manual { continue } else if err != nil { return err } id, err := m.InstanceId() if err != nil { continue } ids = append(ids, id) } if len(ids) == 0 { return nil } envcfg, err := st.EnvironConfig() if err != nil { return err } env, err := environs.New(envcfg) if err != nil { return err } // TODO(axw) 2013-12-12 #1260171 // Modify InstanceBroker.StopInstances to take // a slice of IDs rather than Instances. instances, err := env.Instances(ids) switch err { case nil: default: return err case environs.ErrNoInstances: return nil case environs.ErrPartialInstances: var nonNilInstances []instance.Instance for i, inst := range instances { if inst == nil { logger.Warningf("unknown instance ID: %v", ids[i]) continue } nonNilInstances = append(nonNilInstances, inst) } instances = nonNilInstances } return env.StopInstances(instances) }
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 }
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 }
// DeployService takes a charm and various parameters and deploys it. func DeployService(st *state.State, args DeployServiceParams) (*state.Service, error) { if args.NumUnits > 1 && args.ToMachineSpec != "" { return nil, errors.New("cannot use --num-units with --to") } settings, err := args.Charm.Config().ValidateSettings(args.ConfigSettings) if err != nil { return nil, err } if args.Charm.Meta().Subordinate { if args.NumUnits != 0 || args.ToMachineSpec != "" { return nil, fmt.Errorf("subordinate service must be deployed without units") } if !constraints.IsEmpty(&args.Constraints) { return nil, fmt.Errorf("subordinate service must be deployed without constraints") } } if args.ServiceOwner == "" { args.ServiceOwner = "user-admin" } // TODO(fwereade): transactional State.AddService including settings, constraints // (minimumUnitCount, initialMachineIds?). service, err := st.AddService( args.ServiceName, args.ServiceOwner, args.Charm, args.IncludeNetworks, args.ExcludeNetworks, ) if err != nil { return nil, err } if len(settings) > 0 { if err := service.UpdateConfigSettings(settings); err != nil { return nil, err } } if args.Charm.Meta().Subordinate { return service, nil } if !constraints.IsEmpty(&args.Constraints) { if err := service.SetConstraints(args.Constraints); err != nil { return nil, err } } if args.NumUnits > 0 { if _, err := AddUnits(st, service, args.NumUnits, args.ToMachineSpec); err != nil { return nil, err } } return service, nil }
// 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 }
// 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 }
// 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 }
// 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 }) }
// fetchAllServicesAndUnits returns a map from service name to service, // a map from service name to unit name to unit, and a map from base charm URL to latest URL. func fetchAllServicesAndUnits( st *state.State, unitMatcher unitMatcher) ( map[string]*state.Service, map[string]map[string]*state.Unit, map[charm.URL]string, error) { svcMap := make(map[string]*state.Service) unitMap := make(map[string]map[string]*state.Unit) latestCharms := make(map[charm.URL]string) services, err := st.AllServices() if err != nil { return nil, nil, nil, err } for _, s := range services { units, err := s.AllUnits() if err != nil { return nil, nil, nil, err } svcUnitMap := make(map[string]*state.Unit) for _, u := range units { if !unitMatcher.matchUnit(u) { continue } svcUnitMap[u.Name()] = u } if unitMatcher.matchesAny() || len(svcUnitMap) > 0 { unitMap[s.Name()] = svcUnitMap svcMap[s.Name()] = s // Record the base URL for the service's charm so that // the latest store revision can be looked up. charmURL, _ := s.CharmURL() if charmURL.Schema == "cs" { latestCharms[*charmURL.WithRevision(-1)] = "" } } } for baseURL, _ := range latestCharms { ch, err := st.LatestPlaceholderCharm(&baseURL) if errors.IsNotFound(err) { continue } if err != nil { return nil, nil, nil, err } latestCharms[baseURL] = ch.String() } return svcMap, unitMap, latestCharms, nil }
// getAllUnits returns a list of all principal and subordinate units // assigned to the given machine. func getAllUnits(st *state.State, machineTag string) ([]string, error) { _, id, err := names.ParseTag(machineTag, names.MachineTagKind) if err != nil { return nil, err } machine, err := st.Machine(id) if err != nil { return nil, err } // Start a watcher on machine's units, read the initial event and stop it. watch := machine.WatchUnits() defer watch.Stop() if units, ok := <-watch.Changes(); ok { return units, nil } return nil, fmt.Errorf("cannot obtain units of machine %q: %v", machineTag, watch.Err()) }
// commonServiceInstances returns instances with // services in common with the specified machine. func commonServiceInstances(st *state.State, m *state.Machine) ([]instance.Id, error) { units, err := m.Units() if err != nil { return nil, err } var instanceIdSet set.Strings for _, unit := range units { if !unit.IsPrincipal() { continue } service, err := unit.Service() if err != nil { return nil, err } allUnits, err := service.AllUnits() if err != nil { return nil, err } for _, unit := range allUnits { machineId, err := unit.AssignedMachineId() if state.IsNotAssigned(err) { continue } else if err != nil { return nil, err } machine, err := st.Machine(machineId) if err != nil { return nil, err } instanceId, err := machine.InstanceId() if err == nil { instanceIdSet.Add(string(instanceId)) } else if state.IsNotProvisionedError(err) { continue } else { return nil, err } } } instanceIds := make([]instance.Id, instanceIdSet.Size()) // Sort values to simplify testing. for i, instanceId := range instanceIdSet.SortedValues() { instanceIds[i] = instance.Id(instanceId) } return instanceIds, 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 }
// environManagerInstances returns all environ manager instances. func environManagerInstances(st *state.State) ([]instance.Id, error) { info, err := st.StateServerInfo() if err != nil { return nil, err } instances := make([]instance.Id, 0, len(info.MachineIds)) for _, id := range info.MachineIds { machine, err := st.Machine(id) if err != nil { return nil, err } instanceId, err := machine.InstanceId() if err == nil { instances = append(instances, instanceId) } else if !state.IsNotProvisionedError(err) { return nil, err } } return instances, 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) }
// NewEnvironObserver waits for the state to have a valid environment // configuration and returns a new environment observer. While waiting // for the first environment configuration, it will return with // tomb.ErrDying if it receives a value on dying. func NewEnvironObserver(st *state.State) (*EnvironObserver, error) { config, err := st.EnvironConfig() if err != nil { return nil, err } environ, err := environs.New(config) if err != nil { return nil, fmt.Errorf("cannot make Environ: %v", err) } environWatcher := st.WatchForEnvironConfigChanges() obs := &EnvironObserver{ st: st, environ: environ, environWatcher: environWatcher, } go func() { defer obs.tomb.Done() defer watcher.Stop(environWatcher, &obs.tomb) obs.tomb.Kill(obs.loop()) }() return obs, nil }
// NewKeyManagerAPI creates a new server-side keyupdater API end point. func NewKeyManagerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*KeyManagerAPI, error) { // Only clients and environment managers can access the key manager service. if !authorizer.AuthClient() && !authorizer.AuthEnvironManager() { return nil, common.ErrPerm } // TODO(wallyworld) - replace stub with real canRead function // For now, only admins can read authorised ssh keys. getCanRead := func() (common.AuthFunc, error) { return func(tag string) bool { return authorizer.GetAuthTag() == "user-admin" }, nil } // TODO(wallyworld) - replace stub with real canWrite function // For now, only admins can write authorised ssh keys for users. // Machine agents can write the juju-system-key. getCanWrite := func() (common.AuthFunc, error) { return func(tag string) bool { // Are we a machine agent writing the Juju system key. if tag == config.JujuSystemKey { _, _, err := names.ParseTag(authorizer.GetAuthTag(), names.MachineTagKind) return err == nil } // Are we writing the auth key for a user. if _, err := st.User(tag); err != nil { return false } return authorizer.GetAuthTag() == "user-admin" }, nil } return &KeyManagerAPI{ state: st, resources: resources, authorizer: authorizer, getCanRead: getCanRead, getCanWrite: getCanWrite}, 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) }