// UpdateEngineVersion implements the ClusterRegistry interface func (r *EtcdRegistry) UpdateEngineVersion(from, to int) error { key := r.engineVersionPath() strTo := strconv.Itoa(to) strFrom := strconv.Itoa(from) var req etcd.Action req = &etcd.Set{ Key: key, Value: strTo, PreviousValue: strFrom, } _, err := r.etcd.Do(req) if err == nil { return nil } else if !etcd.IsKeyNotFound(err) { return err } req = &etcd.Create{ Key: key, Value: strTo, } _, err = r.etcd.Do(req) return err }
// statesByMUSKey returns a map of all UnitStates stored in the registry indexed by MUSKey func (r *EtcdRegistry) statesByMUSKey() (map[MUSKey]*unit.UnitState, error) { mus := make(map[MUSKey]*unit.UnitState) req := etcd.Get{ Key: path.Join(r.keyPrefix, statesPrefix), Recursive: true, } res, err := r.etcd.Do(&req) if err != nil && !etcd.IsKeyNotFound(err) { return nil, err } if res != nil { for _, dir := range res.Node.Nodes { _, name := path.Split(dir.Key) for _, node := range dir.Nodes { _, machID := path.Split(node.Key) var usm unitStateModel if err := unmarshal(node.Value, &usm); err != nil { log.Errorf("Error unmarshalling UnitState(%s) from Machine(%s): %v", name, machID, err) continue } us := modelToUnitState(&usm, name) if us != nil { key := MUSKey{name, machID} mus[key] = us } } } } return mus, nil }
// ScheduledUnit retrieves the ScheduledUnit by the given name from the Registry. // Returns nil if no such ScheduledUnit exists, and any error encountered. func (r *EtcdRegistry) ScheduledUnit(name string) (*job.ScheduledUnit, error) { req := etcd.Get{ Key: path.Join(r.keyPrefix, jobPrefix, name), Recursive: true, } res, err := r.etcd.Do(&req) if err != nil { if etcd.IsKeyNotFound(err) { err = nil } return nil, err } su := job.ScheduledUnit{ Name: name, TargetMachineID: dirToTargetMachineID(res.Node), } var us *unit.UnitState if len(su.TargetMachineID) > 0 { us, err = r.getUnitState(name, su.TargetMachineID) if err != nil { return nil, err } } js := determineJobState(dirToHeartbeat(res.Node), su.TargetMachineID, us) su.State = &js return &su, nil }
// getUnitByHash retrieves from the Registry the Unit associated with the given Hash func (r *EtcdRegistry) getUnitByHash(hash unit.Hash) *unit.UnitFile { req := etcd.Get{ Key: r.hashedUnitPath(hash), Recursive: true, } resp, err := r.etcd.Do(&req) if err != nil { if etcd.IsKeyNotFound(err) { err = nil } return nil } var um unitModel if err := unmarshal(resp.Node.Value, &um); err != nil { log.Errorf("error unmarshaling Unit(%s): %v", hash, err) return nil } u, err := unit.NewUnitFile(um.Raw) if err != nil { log.Errorf("error parsing Unit(%s): %v", hash, err) return nil } return u }
func (r *EtcdRegistry) Machines() (machines []machine.MachineState, err error) { req := etcd.Get{ Key: path.Join(r.keyPrefix, machinePrefix), Sorted: true, Recursive: true, } resp, err := r.etcd.Do(&req) if err != nil { if etcd.IsKeyNotFound(err) { err = nil } return } for _, node := range resp.Node.Nodes { for _, obj := range node.Nodes { if !strings.HasSuffix(obj.Key, "/object") { continue } var mach machine.MachineState err = unmarshal(obj.Value, &mach) if err != nil { return } machines = append(machines, mach) } } return }
func (r *EtcdRegistry) RemoveMachineState(machID string) error { req := etcd.Delete{ Key: path.Join(r.keyPrefix, machinePrefix, machID, "object"), } _, err := r.etcd.Do(&req) if etcd.IsKeyNotFound(err) { err = nil } return err }
// Schedule returns all ScheduledUnits known by fleet, ordered by name func (r *EtcdRegistry) Schedule() ([]job.ScheduledUnit, error) { req := etcd.Get{ Key: path.Join(r.keyPrefix, jobPrefix), Sorted: true, Recursive: true, } res, err := r.etcd.Do(&req) if err != nil { if etcd.IsKeyNotFound(err) { err = nil } return nil, err } heartbeats := make(map[string]string) uMap := make(map[string]*job.ScheduledUnit) for _, dir := range res.Node.Nodes { _, name := path.Split(dir.Key) u := &job.ScheduledUnit{ Name: name, TargetMachineID: dirToTargetMachineID(&dir), } heartbeats[name] = dirToHeartbeat(&dir) uMap[name] = u } states, err := r.statesByMUSKey() if err != nil { return nil, err } var sortable sort.StringSlice // Determine the JobState of each ScheduledUnit for name, su := range uMap { sortable = append(sortable, name) key := MUSKey{ machID: su.TargetMachineID, name: name, } us := states[key] js := determineJobState(heartbeats[name], su.TargetMachineID, us) su.State = &js } sortable.Sort() units := make([]job.ScheduledUnit, 0, len(sortable)) for _, name := range sortable { units = append(units, *uMap[name]) } return units, nil }
func (r *EtcdRegistry) UnscheduleUnit(name, machID string) error { req := etcd.Delete{ Key: r.jobTargetAgentPath(name), PreviousValue: machID, } _, err := r.etcd.Do(&req) if etcd.IsKeyNotFound(err) { err = nil } return err }
// Delete the state from the Registry for the given Job's Unit func (r *EtcdRegistry) RemoveUnitState(jobName string) error { // TODO(jonboulle): consider https://github.com/coreos/fleet/issues/465 legacyKey := r.legacyUnitStatePath(jobName) req := etcd.Delete{ Key: legacyKey, } _, err := r.etcd.Do(&req) if err != nil && !etcd.IsKeyNotFound(err) { return err } // TODO(jonboulle): deal properly with multiple states newKey := r.unitStatesNamespace(jobName) req = etcd.Delete{ Key: newKey, Recursive: true, } _, err = r.etcd.Do(&req) if err != nil && !etcd.IsKeyNotFound(err) { return err } return nil }
// Unit retrieves the Unit by the given name from the Registry. Returns nil if // no such Unit exists, and any error encountered. func (r *EtcdRegistry) Unit(name string) (*job.Unit, error) { req := etcd.Get{ Key: path.Join(r.keyPrefix, jobPrefix, name), Recursive: true, } res, err := r.etcd.Do(&req) if err != nil { if etcd.IsKeyNotFound(err) { err = nil } return nil, err } return r.dirToUnit(res.Node) }
// EngineVersion implements the ClusterRegistry interface func (r *EtcdRegistry) EngineVersion() (int, error) { req := etcd.Get{ Key: r.engineVersionPath(), } res, err := r.etcd.Do(&req) if err != nil { // no big deal, either the cluster is new or is just // upgrading from old unversioned code if etcd.IsKeyNotFound(err) { err = nil } return 0, err } return strconv.Atoi(res.Node.Value) }
// DestroyUnit removes a Job object from the repository. It does not yet remove underlying // UnitFiles from the repository. func (r *EtcdRegistry) DestroyUnit(name string) error { req := etcd.Delete{ Key: path.Join(r.keyPrefix, jobPrefix, name), Recursive: true, } _, err := r.etcd.Do(&req) if err != nil { if etcd.IsKeyNotFound(err) { err = errors.New("job does not exist") } return err } // TODO(jonboulle): add unit reference counting and actually destroying Units return nil }
// LatestDaemonVersion attempts to retrieve the latest version of fleetd // that has been registered in the Registry. It returns the version if // it can be determined (or nil otherwise), and any error encountered. func (r *EtcdRegistry) LatestDaemonVersion() (*semver.Version, error) { machs, err := r.Machines() if err != nil { if etcd.IsKeyNotFound(err) { err = nil } return nil, err } var lv *semver.Version for _, m := range machs { v, err := semver.NewVersion(m.Version) if err != nil { continue } else if lv == nil || lv.LessThan(*v) { lv = v } } return lv, nil }
// getUnitState retrieves the current UnitState, if any exists, for the // given unit that originates from the indicated machine func (r *EtcdRegistry) getUnitState(uName, machID string) (*unit.UnitState, error) { req := etcd.Get{ Key: r.unitStatePath(machID, uName), } res, err := r.etcd.Do(&req) if err != nil { if etcd.IsKeyNotFound(err) { err = nil } return nil, err } var usm unitStateModel if err := unmarshal(res.Node.Value, &usm); err != nil { return nil, err } return modelToUnitState(&usm, uName), nil }
// Units lists all Units stored in the Registry, ordered by name. This includes both global and non-global units. func (r *EtcdRegistry) Units() ([]job.Unit, error) { req := etcd.Get{ Key: path.Join(r.keyPrefix, jobPrefix), Sorted: true, Recursive: true, } res, err := r.etcd.Do(&req) if err != nil { if etcd.IsKeyNotFound(err) { err = nil } return nil, err } uMap := make(map[string]*job.Unit) for _, dir := range res.Node.Nodes { u, err := r.dirToUnit(&dir) if err != nil { log.Errorf("Failed to parse Unit from etcd: %v", err) continue } if u == nil { continue } uMap[u.Name] = u } var sortable sort.StringSlice for name, _ := range uMap { sortable = append(sortable, name) } sortable.Sort() units := make([]job.Unit, 0, len(sortable)) for _, name := range sortable { units = append(units, *uMap[name]) } return units, nil }