func (r *EtcdRegistry) getJobFromModel(jm jobModel) *job.Job { var err error var unit *unit.Unit // New-style Jobs should have a populated UnitHash, and the contents of the Unit are stored separately in the Registry if !jm.UnitHash.Empty() { unit = r.getUnitByHash(jm.UnitHash) if unit == nil { log.Warningf("No Unit found in Registry for Job(%s)", jm.Name) return nil } if unit.Hash() != jm.UnitHash { log.Errorf("Unit Hash %s does not match expected %s for Job(%s)!", unit.Hash(), jm.UnitHash, jm.Name) return nil } } else { // Old-style Jobs had "Payloads" instead of Units, also stored separately in the Registry unit, err = r.getUnitFromLegacyPayload(jm.Name) if err != nil { log.Errorf("Error retrieving legacy payload for Job(%s)", jm.Name) return nil } else if unit == nil { log.Warningf("No Payload found in Registry for Job(%s)", jm.Name) return nil } log.Infof("Migrating legacy Payload(%s)", jm.Name) if err := r.storeOrGetUnit(*unit); err != nil { log.Warningf("Unable to migrate legacy Payload: %v", err) } } return job.NewJob(jm.Name, *unit) }
// Load writes the given Unit to disk, subscribing to relevant dbus // events, caching the Unit's Hash, and, if necessary, instructing the systemd // daemon to reload. func (m *systemdUnitManager) Load(name string, u unit.Unit) error { m.mutex.Lock() defer m.mutex.Unlock() err := m.writeUnit(name, u.String()) if err != nil { return err } m.hashes[name] = u.Hash() return m.daemonReload() }
func (ur *unitsResource) destroy(rw http.ResponseWriter, req *http.Request, item string) { if validateContentType(req) != nil { sendError(rw, http.StatusNotAcceptable, errors.New("application/json is only supported Content-Type")) return } var du schema.DeletableUnit dec := json.NewDecoder(req.Body) err := dec.Decode(&du) if err != nil { sendError(rw, http.StatusBadRequest, fmt.Errorf("unable to decode body: %v", err)) return } var u *unit.Unit if len(du.FileContents) > 0 { u, err = decodeUnitContents(du.FileContents) if err != nil { sendError(rw, http.StatusBadRequest, fmt.Errorf("invalid fileContents: %v", err)) return } } j, err := ur.reg.Job(item) if err != nil { log.Errorf("Failed fetching Job(%s): %v", item, err) sendError(rw, http.StatusInternalServerError, nil) return } if j == nil { sendError(rw, http.StatusNotFound, errors.New("unit does not exist")) return } if u != nil && u.Hash() != j.Unit.Hash() { sendError(rw, http.StatusConflict, errors.New("hash of provided fileContents does not match that of existing unit")) return } err = ur.reg.DestroyJob(item) if err != nil { log.Errorf("Failed destroying Job(%s): %v", item, err) sendError(rw, http.StatusInternalServerError, nil) return } rw.WriteHeader(http.StatusNoContent) }
func (ur *unitsResource) update(rw http.ResponseWriter, j *job.Job, ds job.JobState, cmp *unit.Unit) { // Assert that the Job's Unit matches the Unit in the request, if provided if cmp != nil && cmp.Hash() != j.Unit.Hash() { sendError(rw, http.StatusConflict, errors.New("hash of provided fileContents does not match that of existing unit")) return } err := ur.reg.SetJobTargetState(j.Name, ds) if err != nil { log.Errorf("Failed setting target state of Job(%s): %v", j.Name, err) sendError(rw, http.StatusInternalServerError, nil) return } rw.WriteHeader(http.StatusNoContent) }
func (r *EtcdRegistry) getJobFromObjectNode(node *etcd.Node) (*job.Job, error) { var err error var jm jobModel if err = unmarshal(node.Value, &jm); err != nil { return nil, err } var unit *unit.Unit // New-style Jobs should have a populated UnitHash, and the contents of the Unit are stored separately in the Registry if !jm.UnitHash.Empty() { unit = r.getUnitByHash(jm.UnitHash) if unit == nil { log.Warningf("No Unit found in Registry for Job(%s)", jm.Name) return nil, nil } if unit.Hash() != jm.UnitHash { log.Errorf("Unit Hash %s does not match expected %s for Job(%s)!", unit.Hash(), jm.UnitHash, jm.Name) return nil, nil } } else { // Old-style Jobs had "Payloads" instead of Units, also stored separately in the Registry unit, err = r.getUnitFromLegacyPayload(jm.Name) if err != nil { log.Errorf("Error retrieving legacy payload for Job(%s)", jm.Name) return nil, nil } else if unit == nil { log.Warningf("No Payload found in Registry for Job(%s)", jm.Name) return nil, nil } log.Infof("Migrating legacy Payload(%s)", jm.Name) if err := r.storeOrGetUnit(*unit); err != nil { log.Warningf("Unable to migrate legacy Payload: %v", err) } jm.UnitHash = unit.Hash() log.Infof("Updating Job(%s) with legacy payload Hash(%s)", jm.Name, jm.UnitHash) if err := r.updateJobObjectNode(&jm, node.ModifiedIndex); err != nil { log.Warningf("Unable to update Job(%s) with legacy payload Hash(%s): %v", jm.Name, jm.UnitHash, err) } } return job.NewJob(jm.Name, *unit), nil }
// NewJob creates a new Job based on the given name and Unit. // The returned Job has a populated UnitHash and empty JobState and // UnitState. nil is returned on failure. func NewJob(name string, unit unit.Unit) *Job { return &Job{ Name: name, State: nil, Unit: unit, UnitHash: unit.Hash(), UnitState: nil, } }
func (r *EtcdRegistry) storeOrGetUnit(u unit.Unit) (err error) { json, err := marshal(u) if err != nil { return err } req := etcd.Create{ Key: r.hashedUnitPath(u.Hash()), Value: json, } _, err = r.etcd.Do(&req) // unit is already stored if err != nil && isNodeExist(err) { // TODO(jonboulle): verify more here? err = nil } return }
func (r *EtcdRegistry) storeOrGetUnit(u unit.Unit) (err error) { key := r.hashedUnitPath(u.Hash()) json, err := marshal(u) if err != nil { return err } log.V(3).Infof("Storing Unit(%s) in Registry: %s", u.Hash(), json) _, err = r.etcd.Create(key, json, 0) // unit is already stored if err != nil && err.(*goetcd.EtcdError).ErrorCode == etcd.EcodeNodeExist { log.V(2).Infof("Unit(%s) already exists in Registry", u.Hash()) // TODO(jonboulle): verify more here? err = nil } return }