Пример #1
0
// Destroy ensures that the relation will be removed at some point; if no units
// are currently in scope, it will be removed immediately.
func (r *Relation) Destroy() (err error) {
	defer trivial.ErrorContextf(&err, "cannot destroy relation %q", r)
	defer func() {
		if err == nil {
			// This is a white lie; the document might actually be removed.
			r.doc.Life = Dying
		}
	}()
	rel := &Relation{r.st, r.doc}
	// In this context, aborted transactions indicate that the number of units
	// in scope have changed between 0 and not-0. The chances of 5 successive
	// attempts each hitting this change -- which is itself an unlikely one --
	// are considered to be extremely small.
	for attempt := 0; attempt < 5; attempt++ {
		ops, _, err := rel.destroyOps("")
		if err == errAlreadyDying {
			return nil
		} else if err != nil {
			return err
		}
		if err := rel.st.runner.Run(ops, "", nil); err != txn.ErrAborted {
			return err
		}
		if err := rel.Refresh(); IsNotFound(err) {
			return nil
		} else if err != nil {
			return err
		}
	}
	return ErrExcessiveContention
}
Пример #2
0
// RemoveUnit removes the given unit from s.
func (s *Service) RemoveUnit(u *Unit) (err error) {
	defer trivial.ErrorContextf(&err, "cannot remove unit %q", u)
	if u.doc.Life != Dead {
		return errors.New("unit is not dead")
	}
	if u.doc.Service != s.doc.Name {
		return fmt.Errorf("unit is not assigned to service %q", s)
	}
	svc := &Service{s.st, s.doc}
	unit := &Unit{u.st, u.doc}
	for i := 0; i < 5; i++ {
		ops := svc.removeUnitOps(unit)
		if err := svc.st.runner.Run(ops, "", nil); err != txn.ErrAborted {
			return err
		}
		if err := unit.Refresh(); IsNotFound(err) {
			return nil
		} else if err != nil {
			return err
		}
		if err := svc.Refresh(); IsNotFound(err) {
			return nil
		} else if err != nil {
			return err
		}
	}
	return ErrExcessiveContention
}
Пример #3
0
// AddUnitSubordinateTo adds a new subordinate unit to the service, subordinate
// to principal. It does not verify relation state sanity or pre-existence of
// other subordinates of the same service; is deprecated; and only continues
// to exist for the convenience of certain tests, which are themselves due for
// overhaul.
func (s *Service) AddUnitSubordinateTo(principal *Unit) (unit *Unit, err error) {
	log.Printf("state: Service.AddUnitSubordinateTo is DEPRECATED; subordinate units should be created only as a side-effect of a principal entering relation scope")
	defer trivial.ErrorContextf(&err, "cannot add unit to service %q as a subordinate of %q", s, principal)
	ch, _, err := s.Charm()
	if err != nil {
		return nil, err
	}
	if !ch.Meta().Subordinate {
		return nil, fmt.Errorf("service is not a subordinate")
	}
	if !principal.IsPrincipal() {
		return nil, fmt.Errorf("unit is not a principal")
	}
	name, ops, err := s.addUnitOps(principal.doc.Name, false)
	if err != nil {
		return nil, err
	}
	if err = s.st.runner.Run(ops, "", nil); err == nil {
		return s.Unit(name)
	} else if err != txn.ErrAborted {
		return nil, err
	}
	if alive, err := isAlive(s.st.services, s.doc.Name); err != nil {
		return nil, err
	} else if !alive {
		return nil, fmt.Errorf("service is not alive")
	}
	if alive, err := isAlive(s.st.units, principal.doc.Name); err != nil {
		return nil, err
	} else if !alive {
		return nil, fmt.Errorf("principal unit is not alive")
	}
	return nil, fmt.Errorf("inconsistent state")
}
Пример #4
0
// validate returns an error if the state violates expectations.
func (st State) validate() (err error) {
	defer trivial.ErrorContextf(&err, "invalid uniter state")
	hasHook := st.Hook != nil
	hasCharm := st.CharmURL != nil
	switch st.Op {
	case Install:
		if hasHook {
			return fmt.Errorf("unexpected hook info")
		}
		fallthrough
	case Upgrade:
		if !hasCharm {
			return fmt.Errorf("missing charm URL")
		}
	case Continue, RunHook:
		if !hasHook {
			return fmt.Errorf("missing hook info")
		} else if hasCharm {
			return fmt.Errorf("unexpected charm URL")
		}
	default:
		return fmt.Errorf("unknown operation %q", st.Op)
	}
	switch st.OpStep {
	case Queued, Pending, Done:
	default:
		return fmt.Errorf("unknown operation step %q", st.OpStep)
	}
	if hasHook {
		return st.Hook.Validate()
	}
	return nil
}
Пример #5
0
// AssignUnit places the unit on a machine. Depending on the policy, and the
// state of the environment, this may lead to new instances being launched
// within the environment.
func (st *State) AssignUnit(u *Unit, policy AssignmentPolicy) (err error) {
	if !u.IsPrincipal() {
		return fmt.Errorf("subordinate unit %q cannot be assigned directly to a machine", u)
	}
	defer trivial.ErrorContextf(&err, "cannot assign unit %q to machine", u)
	var m *Machine
	switch policy {
	case AssignLocal:
		m, err = st.Machine("0")
		if err != nil {
			return err
		}
		return u.AssignToMachine(m)
	case AssignUnused:
		if _, err = u.AssignToUnusedMachine(); err != noUnusedMachines {
			return err
		}
		for {
			// TODO(rog) take out a lease on the new machine
			// so that we don't have a race here.
			m, err := st.AddMachine(JobHostUnits)
			if err != nil {
				return err
			}
			err = u.assignToMachine(m, true)
			if err == inUseErr {
				// Someone else has grabbed the machine we've
				// just allocated, so try again.
				continue
			}
			return err
		}
	}
	panic(fmt.Errorf("unknown unit assignment policy: %q", policy))
}
Пример #6
0
// RemoveMachine removes the machine with the the given id.
func (st *State) RemoveMachine(id string) (err error) {
	defer trivial.ErrorContextf(&err, "cannot remove machine %s", id)
	m, err := st.Machine(id)
	if err != nil {
		return err
	}
	if m.doc.Life != Dead {
		return fmt.Errorf("machine is not dead")
	}
	sel := D{
		{"_id", id},
		{"life", Dead},
	}
	ops := []txn.Op{{
		C:      st.machines.Name,
		Id:     id,
		Assert: sel,
		Remove: true,
	}}
	if err := st.runner.Run(ops, "", nil); err != nil {
		// If aborted, the machine is either dead or recreated.
		return onAbort(err, nil)
	}
	return nil
}
Пример #7
0
// Write atomically writes to disk the relation state change in hi.
// It must be called after the respective hook was executed successfully.
// Write doesn't validate hi but guarantees that successive writes of
// the same hi are idempotent.
func (d *StateDir) Write(hi hook.Info) (err error) {
	defer trivial.ErrorContextf(&err, "failed to write %q hook info for %q on state directory", hi.Kind, hi.RemoteUnit)
	if hi.Kind == hook.RelationBroken {
		return d.Remove()
	}
	name := strings.Replace(hi.RemoteUnit, "/", "-", 1)
	path := filepath.Join(d.path, name)
	if hi.Kind == hook.RelationDeparted {
		if err = os.Remove(path); err != nil && !os.IsNotExist(err) {
			return err
		}
		// If atomic delete succeeded, update own state.
		delete(d.state.Members, hi.RemoteUnit)
		return nil
	}
	di := diskInfo{&hi.ChangeVersion, hi.Kind == hook.RelationJoined}
	if err := trivial.WriteYaml(path, &di); err != nil {
		return err
	}
	// If write was successful, update own state.
	d.state.Members[hi.RemoteUnit] = hi.ChangeVersion
	if hi.Kind == hook.RelationJoined {
		d.state.ChangedPending = hi.RemoteUnit
	} else {
		d.state.ChangedPending = ""
	}
	return nil
}
Пример #8
0
// DestroyUnits removes the specified units from the state.
func (conn *Conn) DestroyUnits(names ...string) (err error) {
	defer trivial.ErrorContextf(&err, "cannot destroy units")
	var units []*state.Unit
	for _, name := range names {
		unit, err := conn.State.Unit(name)
		switch {
		case state.IsNotFound(err):
			return fmt.Errorf("unit %q is not alive", name)
		case err != nil:
			return err
		case unit.Life() != state.Alive:
			return fmt.Errorf("unit %q is not alive", name)
		case unit.IsPrincipal():
			units = append(units, unit)
		default:
			return fmt.Errorf("unit %q is a subordinate", name)
		}
	}
	for _, unit := range units {
		if err := unit.EnsureDying(); err != nil {
			return err
		}
	}
	return nil
}
Пример #9
0
// SetResolved marks the unit as having had any previous state transition
// problems resolved, and informs the unit that it may attempt to
// reestablish normal workflow. The resolved mode parameter informs
// whether to attempt to reexecute previous failed hooks or to continue
// as if they had succeeded before.
func (u *Unit) SetResolved(mode ResolvedMode) (err error) {
	defer trivial.ErrorContextf(&err, "cannot set resolved mode for unit %q", u)
	switch mode {
	case ResolvedNone, ResolvedRetryHooks, ResolvedNoHooks:
	default:
		return fmt.Errorf("invalid error resolution mode: %q", mode)
	}
	assert := append(notDeadDoc, D{{"resolved", ResolvedNone}}...)
	ops := []txn.Op{{
		C:      u.st.units.Name,
		Id:     u.doc.Name,
		Assert: assert,
		Update: D{{"$set", D{{"resolved", mode}}}},
	}}
	if err := u.st.runner.Run(ops, "", nil); err != nil {
		if err == txn.ErrAborted {
			// Find which assertion failed so we can give a
			// more specific error.
			u1, err := u.st.Unit(u.Name())
			if err != nil {
				return err
			}
			if u1.Life() != Alive {
				return errNotAlive
			}
			return fmt.Errorf("already resolved")
		}
		return err
	}
	u.doc.Resolved = mode
	return nil
}
Пример #10
0
// Validate returns an error if the supplied hook.Info does not represent
// a valid change to the relation state. Hooks must always be validated
// against the current state before they are run, to ensure that the system
// meets its guarantees about hook execution order.
func (s *State) Validate(hi hook.Info) (err error) {
	defer trivial.ErrorContextf(&err, "inappropriate %q for %q", hi.Kind, hi.RemoteUnit)
	if hi.RelationId != s.RelationId {
		return fmt.Errorf("expected relation %d, got relation %d", s.RelationId, hi.RelationId)
	}
	if s.Members == nil {
		return fmt.Errorf(`relation is broken and cannot be changed further`)
	}
	unit, kind := hi.RemoteUnit, hi.Kind
	if kind == hook.RelationBroken {
		if len(s.Members) == 0 {
			return nil
		}
		return fmt.Errorf(`cannot run "relation-broken" while units still present`)
	}
	if s.ChangedPending != "" {
		if unit != s.ChangedPending || kind != hook.RelationChanged {
			return fmt.Errorf(`expected "relation-changed" for %q`, s.ChangedPending)
		}
	} else if _, joined := s.Members[unit]; joined && kind == hook.RelationJoined {
		return fmt.Errorf("unit already joined")
	} else if !joined && kind != hook.RelationJoined {
		return fmt.Errorf("unit has not joined")
	}
	return nil
}
Пример #11
0
func (u *Uniter) init(name string) (err error) {
	defer trivial.ErrorContextf(&err, "failed to initialize uniter for unit %q", name)
	u.unit, err = u.st.Unit(name)
	if err != nil {
		return err
	}
	ename := u.unit.EntityName()
	u.toolsDir = environs.AgentToolsDir(u.dataDir, ename)
	if err := EnsureJujucSymlinks(u.toolsDir); err != nil {
		return err
	}
	u.baseDir = filepath.Join(u.dataDir, "agents", ename)
	u.relationsDir = filepath.Join(u.baseDir, "state", "relations")
	if err := os.MkdirAll(u.relationsDir, 0755); err != nil {
		return err
	}
	u.service, err = u.st.Service(u.unit.ServiceName())
	if err != nil {
		return err
	}
	u.relationers = map[int]*Relationer{}
	u.relationHooks = make(chan hook.Info)
	u.charm = charm.NewGitDir(filepath.Join(u.baseDir, "charm"))
	u.bundles = charm.NewBundlesDir(filepath.Join(u.baseDir, "state", "bundles"))
	u.deployer = charm.NewDeployer(filepath.Join(u.baseDir, "state", "deployer"))
	u.sf = NewStateFile(filepath.Join(u.baseDir, "state", "uniter"))
	u.rand = rand.New(rand.NewSource(time.Now().Unix()))
	return nil
}
Пример #12
0
// Destroy ensures that the service and all its relations will be removed at
// some point; if the service has no units, and no relation involving the
// service has any units in scope, they are all removed immediately.
func (s *Service) Destroy() (err error) {
	defer trivial.ErrorContextf(&err, "cannot destroy service %q", s)
	defer func() {
		if err != nil {
			// This is a white lie; the document might actually be removed.
			s.doc.Life = Dying
		}
	}()
	svc := &Service{s.st, s.doc}
	for i := 0; i < 5; i++ {
		ops, err := svc.destroyOps()
		switch {
		case err == errRefresh:
		case err == errAlreadyDying:
			return nil
		case err != nil:
			return err
		default:
			if err := svc.st.runner.Run(ops, "", nil); err != txn.ErrAborted {
				return err
			}
		}
		if err := svc.Refresh(); IsNotFound(err) {
			return nil
		} else if err != nil {
			return err
		}
	}
	return ErrExcessiveContention
}
Пример #13
0
// ReadAllStateDirs loads and returns every StateDir persisted directly inside
// the supplied dirPath. If dirPath does not exist, no error is returned.
func ReadAllStateDirs(dirPath string) (dirs map[int]*StateDir, err error) {
	defer trivial.ErrorContextf(&err, "cannot load relations state from %q", dirPath)
	if _, err := os.Stat(dirPath); os.IsNotExist(err) {
		return nil, nil
	} else if err != nil {
		return nil, err
	}
	fis, err := ioutil.ReadDir(dirPath)
	if err != nil {
		return nil, err
	}
	dirs = map[int]*StateDir{}
	for _, fi := range fis {
		// Entries with integer names must be directories containing StateDir
		// data; all other names will be ignored.
		relationId, err := strconv.Atoi(fi.Name())
		if err != nil {
			// This doesn't look like a relation.
			continue
		}
		dir, err := ReadStateDir(dirPath, relationId)
		if err != nil {
			return nil, err
		}
		dirs[relationId] = dir
	}
	return dirs, nil
}
Пример #14
0
func verifyConfig(cfg *MachineConfig) (err error) {
	defer trivial.ErrorContextf(&err, "invalid machine configuration")
	if !state.IsMachineId(cfg.MachineId) {
		return fmt.Errorf("invalid machine id")
	}
	if cfg.ProviderType == "" {
		return fmt.Errorf("missing provider type")
	}
	if cfg.DataDir == "" {
		return fmt.Errorf("missing var directory")
	}
	if cfg.Tools == nil {
		return fmt.Errorf("missing tools")
	}
	if cfg.Tools.URL == "" {
		return fmt.Errorf("missing tools URL")
	}
	if cfg.StateInfo == nil {
		return fmt.Errorf("missing state info")
	}
	if len(cfg.StateInfo.CACert) == 0 {
		return fmt.Errorf("missing CA certificate")
	}
	if cfg.StateServer {
		if cfg.InstanceIdAccessor == "" {
			return fmt.Errorf("missing instance id accessor")
		}
		if cfg.Config == nil {
			return fmt.Errorf("missing environment configuration")
		}
		if cfg.StateInfo.EntityName != "" {
			return fmt.Errorf("entity name must be blank when starting a state server")
		}
		if len(cfg.StateServerCert) == 0 {
			return fmt.Errorf("missing state server certificate")
		}
		if len(cfg.StateServerKey) == 0 {
			return fmt.Errorf("missing state server private key")
		}
	} else {
		if len(cfg.StateInfo.Addrs) == 0 {
			return fmt.Errorf("missing state hosts")
		}
		if cfg.StateInfo.EntityName != state.MachineEntityName(cfg.MachineId) {
			return fmt.Errorf("entity name must match started machine")
		}
	}
	for _, r := range cfg.StateInfo.Password {
		if r == '\'' || r == '\\' || r < 32 {
			return fmt.Errorf("password has disallowed characters")
		}
	}
	return nil
}
Пример #15
0
// Relations returns a Relation for every relation the service is in.
func (s *Service) Relations() (relations []*Relation, err error) {
	defer trivial.ErrorContextf(&err, "can't get relations for service %q", s)
	docs := []relationDoc{}
	err = s.st.relations.Find(D{{"endpoints.servicename", s.doc.Name}}).All(&docs)
	if err != nil {
		return nil, err
	}
	for _, v := range docs {
		relations = append(relations, newRelation(s.st, &v))
	}
	return relations, nil
}
Пример #16
0
// ReadSettings returns a map holding the settings of the unit with the
// supplied name within this relation. An error will be returned if the
// relation no longer exists, or if the unit's service is not part of the
// relation, or the settings are invalid; but mere non-existence of the
// unit is not grounds for an error, because the unit settings are
// guaranteed to persist for the lifetime of the relation, regardless
// of the lifetime of the unit.
func (ru *RelationUnit) ReadSettings(uname string) (m map[string]interface{}, err error) {
	defer trivial.ErrorContextf(&err, "cannot read settings for unit %q in relation %q", uname, ru.relation)
	if !IsUnitName(uname) {
		return nil, fmt.Errorf("%q is not a valid unit name", uname)
	}
	key, err := ru.key(uname)
	if err != nil {
		return nil, err
	}
	node, err := readSettings(ru.st, key)
	if err != nil {
		return nil, err
	}
	return node.Map(), nil
}
Пример #17
0
// ReadStateDir loads a StateDir from the subdirectory of dirPath named
// for the supplied RelationId. If the directory does not exist, no error
// is returned,
func ReadStateDir(dirPath string, relationId int) (d *StateDir, err error) {
	d = &StateDir{
		filepath.Join(dirPath, strconv.Itoa(relationId)),
		State{relationId, map[string]int64{}, ""},
	}
	defer trivial.ErrorContextf(&err, "cannot load relation state from %q", d.path)
	if _, err := os.Stat(d.path); os.IsNotExist(err) {
		return d, nil
	} else if err != nil {
		return nil, err
	}
	fis, err := ioutil.ReadDir(d.path)
	if err != nil {
		return nil, err
	}
	for _, fi := range fis {
		// Entries with names ending in "-" followed by an integer must be
		// files containing valid unit data; all other names are ignored.
		name := fi.Name()
		i := strings.LastIndex(name, "-")
		if i == -1 {
			continue
		}
		svcName := name[:i]
		unitId := name[i+1:]
		if _, err := strconv.Atoi(unitId); err != nil {
			continue
		}
		unitName := svcName + "/" + unitId
		var info diskInfo
		if err = trivial.ReadYaml(filepath.Join(d.path, name), &info); err != nil {
			return nil, fmt.Errorf("invalid unit file %q: %v", name, err)
		}
		if info.ChangeVersion == nil {
			return nil, fmt.Errorf(`invalid unit file %q: "changed-version" not set`, name)
		}
		d.state.Members[unitName] = *info.ChangeVersion
		if info.ChangedPending {
			if d.state.ChangedPending != "" {
				return nil, fmt.Errorf("%q and %q both have pending changed hooks", d.state.ChangedPending, unitName)
			}
			d.state.ChangedPending = unitName
		}
	}
	return d, nil
}
Пример #18
0
// SetAgentTools sets the tools that the agent is currently running.
func (u *Unit) SetAgentTools(t *Tools) (err error) {
	defer trivial.ErrorContextf(&err, "cannot set agent tools for unit %q", u)
	if t.Series == "" || t.Arch == "" {
		return fmt.Errorf("empty series or arch")
	}
	ops := []txn.Op{{
		C:      u.st.units.Name,
		Id:     u.doc.Name,
		Assert: notDeadDoc,
		Update: D{{"$set", D{{"tools", t}}}},
	}}
	if err := u.st.runner.Run(ops, "", nil); err != nil {
		return onAbort(err, errNotAlive)
	}
	tools := *t
	u.doc.Tools = &tools
	return nil
}
Пример #19
0
// AddUnit adds a new principal unit to the service.
func (s *Service) AddUnit() (unit *Unit, err error) {
	defer trivial.ErrorContextf(&err, "cannot add unit to service %q", s)
	name, ops, err := s.addUnitOps("", false)
	if err != nil {
		return nil, err
	}
	if err := s.st.runner.Run(ops, "", nil); err == txn.ErrAborted {
		if alive, err := isAlive(s.st.services, s.doc.Name); err != nil {
			return nil, err
		} else if !alive {
			return nil, fmt.Errorf("service is not alive")
		}
		return nil, fmt.Errorf("inconsistent state")
	} else if err != nil {
		return nil, err
	}
	return s.Unit(name)
}
Пример #20
0
// deathFailureReason returns an error indicating why the machine may have
// failed to advance its lifecycle to Dying or Dead. If deathFailureReason
// returns no error, it is possible that the condition that caused the txn
// failure no longer holds; it does not automatically indicate bad state.
func (m *Machine) deathFailureReason(life Life) (err error) {
	if m, err = m.st.Machine(m.doc.Id); err != nil {
		return err
	}
	defer trivial.ErrorContextf(&err, "machine %s cannot become %s", m, life)
	for _, j := range m.doc.Jobs {
		if j == JobManageEnviron {
			// If and when we enable multiple JobManageEnviron machines, the
			// restriction will become "there must be at least one machine
			// with this job", and this will need to change.
			return fmt.Errorf("required by environment")
		}
	}
	if len(m.doc.Principals) != 0 {
		return fmt.Errorf("unit %q is assigned to it", m.doc.Principals[0])
	}
	return nil
}
Пример #21
0
// SetAgentTools sets the tools that the agent is currently running.
func (m *Machine) SetAgentTools(t *Tools) (err error) {
	defer trivial.ErrorContextf(&err, "cannot set agent tools for machine %v", m)
	if t.Series == "" || t.Arch == "" {
		return fmt.Errorf("empty series or arch")
	}
	ops := []txn.Op{{
		C:      m.st.machines.Name,
		Id:     m.doc.Id,
		Assert: notDeadDoc,
		Update: D{{"$set", D{{"tools", t}}}},
	}}
	if err := m.st.runner.Run(ops, "", nil); err != nil {
		return onAbort(err, errNotAlive)
	}
	tools := *t
	m.doc.Tools = &tools
	return nil
}
Пример #22
0
// WaitAgentAlive blocks until the respective agent is alive.
func (u *Unit) WaitAgentAlive(timeout time.Duration) (err error) {
	defer trivial.ErrorContextf(&err, "waiting for agent of unit %q", u)
	ch := make(chan presence.Change)
	u.st.pwatcher.Watch(u.globalKey(), ch)
	defer u.st.pwatcher.Unwatch(u.globalKey(), ch)
	for i := 0; i < 2; i++ {
		select {
		case change := <-ch:
			if change.Alive {
				return nil
			}
		case <-time.After(timeout):
			return fmt.Errorf("still not alive after timeout")
		case <-u.st.pwatcher.Dead():
			return u.st.pwatcher.Err()
		}
	}
	panic(fmt.Sprintf("presence reported dead status twice in a row for unit %q", u))
}
Пример #23
0
// addMachine implements AddMachine and InjectMachine.
func (st *State) addMachine(instanceId InstanceId, jobs []MachineJob) (m *Machine, err error) {
	defer trivial.ErrorContextf(&err, "cannot add a new machine")
	if len(jobs) == 0 {
		return nil, fmt.Errorf("no jobs specified")
	}
	jset := make(map[MachineJob]bool)
	for _, j := range jobs {
		if jset[j] {
			return nil, fmt.Errorf("duplicate job: %s", j)
		}
		jset[j] = true
	}
	seq, err := st.sequence("machine")
	if err != nil {
		return nil, err
	}
	id := strconv.Itoa(seq)
	mdoc := machineDoc{
		Id:   id,
		Life: Alive,
		Jobs: jobs,
	}
	if instanceId != "" {
		mdoc.InstanceId = instanceId
	}
	ops := []txn.Op{{
		C:      st.machines.Name,
		Id:     id,
		Assert: txn.DocMissing,
		Insert: mdoc,
	}}
	err = st.runner.Run(ops, "", nil)
	if err != nil {
		return nil, err
	}
	// Refresh to pick the txn-revno.
	m = newMachine(st, &mdoc)
	if err = m.Refresh(); err != nil {
		return nil, err
	}
	return m, nil
}
Пример #24
0
// Units returns all the units that have been assigned to the machine.
func (m *Machine) Units() (units []*Unit, err error) {
	defer trivial.ErrorContextf(&err, "cannot get units assigned to machine %v", m)
	pudocs := []unitDoc{}
	err = m.st.units.Find(D{{"machineid", m.doc.Id}}).All(&pudocs)
	if err != nil {
		return nil, err
	}
	for _, pudoc := range pudocs {
		units = append(units, newUnit(m.st, &pudoc))
		docs := []unitDoc{}
		err = m.st.units.Find(D{{"principal", pudoc.Name}}).All(&docs)
		if err != nil {
			return nil, err
		}
		for _, doc := range docs {
			units = append(units, newUnit(m.st, &doc))
		}
	}
	return units, nil
}
Пример #25
0
// download fetches the supplied charm and checks that it has the correct sha256
// hash, then copies it into the directory. If a value is received on abort, the
// download will be stopped.
func (d *BundlesDir) download(sch *state.Charm, abort <-chan struct{}) (err error) {
	defer trivial.ErrorContextf(&err, "failed to download charm %q from %q", sch.URL(), sch.BundleURL())
	dir := d.downloadsPath()
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	burl := sch.BundleURL().String()
	log.Printf("worker/uniter/charm: downloading %s from %s", sch.URL(), burl)
	dl := downloader.New(burl, dir)
	defer dl.Stop()
	for {
		select {
		case <-abort:
			log.Printf("worker/uniter/charm: download aborted")
			return fmt.Errorf("aborted")
		case st := <-dl.Done():
			if st.Err != nil {
				return st.Err
			}
			log.Printf("worker/uniter/charm: download complete")
			defer st.File.Close()
			hash := sha256.New()
			if _, err = io.Copy(hash, st.File); err != nil {
				return err
			}
			actualSha256 := hex.EncodeToString(hash.Sum(nil))
			if actualSha256 != sch.BundleSha256() {
				return fmt.Errorf(
					"expected sha256 %q, got %q", sch.BundleSha256(), actualSha256,
				)
			}
			log.Printf("worker/uniter/charm: download verified")
			if err := os.MkdirAll(d.path, 0755); err != nil {
				return err
			}
			return os.Rename(st.File.Name(), d.bundlePath(sch))
		}
	}
	panic("unreachable")
}
Пример #26
0
// ClosePort sets the policy of the port with protocol and number to be closed.
func (u *Unit) ClosePort(protocol string, number int) (err error) {
	port := Port{Protocol: protocol, Number: number}
	defer trivial.ErrorContextf(&err, "cannot close port %v for unit %q", port, u)
	ops := []txn.Op{{
		C:      u.st.units.Name,
		Id:     u.doc.Name,
		Assert: notDeadDoc,
		Update: D{{"$pull", D{{"ports", port}}}},
	}}
	err = u.st.runner.Run(ops, "", nil)
	if err != nil {
		return onAbort(err, errNotAlive)
	}
	newPorts := make([]Port, 0, len(u.doc.Ports))
	for _, p := range u.doc.Ports {
		if p != port {
			newPorts = append(newPorts, p)
		}
	}
	u.doc.Ports = newPorts
	return nil
}
Пример #27
0
// ensureDead advances the specified entity's life status to Dead, if necessary.
// Preconditions can be supplied in assertOps; if the preconditions fail, the error
// will contain assertMsg. If the entity is not found, no error is returned.
func ensureDead(st *State, coll *mgo.Collection, id interface{}, desc string, assertOps []txn.Op, assertMsg string) (err error) {
	defer trivial.ErrorContextf(&err, "cannot finish termination of %s %#v", desc, id)
	ops := append(assertOps, txn.Op{
		C:      coll.Name,
		Id:     id,
		Update: D{{"$set", D{{"life", Dead}}}},
	})
	if err = st.runner.Run(ops, "", nil); err == nil {
		return nil
	} else if err != txn.ErrAborted {
		return err
	}
	var doc struct{ Life }
	if err = coll.FindId(id).One(&doc); err == mgo.ErrNotFound {
		return nil
	} else if err != nil {
		return err
	} else if doc.Life != Dead {
		return fmt.Errorf(assertMsg)
	}
	return nil
}
Пример #28
0
// fetchMetadata fetches a single atom of data from the ec2 instance metadata service.
// http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/AESDG-chapter-instancedata.html
func fetchMetadata(name string) (value string, err error) {
	uri := fmt.Sprintf("%s/2011-01-01/meta-data/%s", metadataHost, name)
	defer trivial.ErrorContextf(&err, "cannot get %q", uri)
	for a := shortAttempt.Start(); a.Next(); {
		var resp *http.Response
		resp, err = http.Get(uri)
		if err != nil {
			continue
		}
		defer resp.Body.Close()
		if resp.StatusCode != http.StatusOK {
			err = fmt.Errorf("bad http response %v", resp.Status)
			continue
		}
		var data []byte
		data, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			continue
		}
		return strings.TrimSpace(string(data)), nil
	}
	return
}
Пример #29
0
// OpenPort sets the policy of the port with protocol and number to be opened.
func (u *Unit) OpenPort(protocol string, number int) (err error) {
	port := Port{Protocol: protocol, Number: number}
	defer trivial.ErrorContextf(&err, "cannot open port %v for unit %q", port, u)
	ops := []txn.Op{{
		C:      u.st.units.Name,
		Id:     u.doc.Name,
		Assert: notDeadDoc,
		Update: D{{"$addToSet", D{{"ports", port}}}},
	}}
	err = u.st.runner.Run(ops, "", nil)
	if err != nil {
		return onAbort(err, errNotAlive)
	}
	found := false
	for _, p := range u.doc.Ports {
		if p == port {
			break
		}
	}
	if !found {
		u.doc.Ports = append(u.doc.Ports, port)
	}
	return nil
}
Пример #30
0
// AddRelation creates a new relation with the given endpoints.
func (st *State) AddRelation(endpoints ...Endpoint) (r *Relation, err error) {
	defer trivial.ErrorContextf(&err, "cannot add relation %q", relationKey(endpoints))
	switch len(endpoints) {
	case 1:
		if endpoints[0].RelationRole != RolePeer {
			return nil, fmt.Errorf("single endpoint must be a peer relation")
		}
	case 2:
		if !endpoints[0].CanRelateTo(endpoints[1]) {
			return nil, fmt.Errorf("endpoints do not relate")
		}
	default:
		return nil, fmt.Errorf("cannot relate %d endpoints", len(endpoints))
	}

	ops := []txn.Op{}
	var scope charm.RelationScope
	for _, v := range endpoints {
		if v.RelationScope == charm.ScopeContainer {
			scope = charm.ScopeContainer
		}
		ops = append(ops, txn.Op{
			C:      st.services.Name,
			Id:     v.ServiceName,
			Assert: isAliveDoc,
			Update: D{{"$inc", D{{"relationcount", 1}}}},
		})
	}
	if scope == charm.ScopeContainer {
		for i := range endpoints {
			endpoints[i].RelationScope = scope
		}
	}
	id, err := st.sequence("relation")
	if err != nil {
		return nil, err
	}
	doc := relationDoc{
		Key:       relationKey(endpoints),
		Id:        id,
		Endpoints: endpoints,
		Life:      Alive,
	}
	ops = append(ops, txn.Op{
		C:      st.relations.Name,
		Id:     doc.Key,
		Assert: txn.DocMissing,
		Insert: doc,
	})
	err = st.runner.Run(ops, "", nil)
	if err == txn.ErrAborted {
		for _, ep := range endpoints {
			svc, err := st.Service(ep.ServiceName)
			if IsNotFound(err) || svc.Life() != Alive {
				return nil, fmt.Errorf("service %q is not alive", ep.ServiceName)
			} else if err != nil {
				return nil, err
			}
		}
		return nil, fmt.Errorf("relation already exists")
	} else if err != nil {
		return nil, err
	}
	return newRelation(st, &doc), nil
}