// primitiveObjectDiff returns a diff of the passed objects' primitive fields. // The filter field can be used to exclude fields from the diff. The name is the // name of the objects. If contextual is set, non-changed fields will also be // stored in the object diff. func primitiveObjectDiff(old, new interface{}, filter []string, name string, contextual bool) *ObjectDiff { oldPrimitiveFlat := flatmap.Flatten(old, filter, true) newPrimitiveFlat := flatmap.Flatten(new, filter, true) delete(oldPrimitiveFlat, "") delete(newPrimitiveFlat, "") diff := &ObjectDiff{Name: name} diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) var added, deleted, edited bool for _, f := range diff.Fields { switch f.Type { case DiffTypeEdited: edited = true break case DiffTypeDeleted: deleted = true case DiffTypeAdded: added = true } } if edited || added && deleted { diff.Type = DiffTypeEdited } else if added { diff.Type = DiffTypeAdded } else if deleted { diff.Type = DiffTypeDeleted } else { return nil } return diff }
// serviceDiff returns the diff of two service objects. If contextual diff is // enabled, all fields will be returned, even if no diff occurred. func serviceDiff(old, new *Service, contextual bool) *ObjectDiff { diff := &ObjectDiff{Type: DiffTypeNone, Name: "Service"} var oldPrimitiveFlat, newPrimitiveFlat map[string]string if reflect.DeepEqual(old, new) { return nil } else if old == nil { old = &Service{} diff.Type = DiffTypeAdded newPrimitiveFlat = flatmap.Flatten(new, nil, true) } else if new == nil { new = &Service{} diff.Type = DiffTypeDeleted oldPrimitiveFlat = flatmap.Flatten(old, nil, true) } else { diff.Type = DiffTypeEdited oldPrimitiveFlat = flatmap.Flatten(old, nil, true) newPrimitiveFlat = flatmap.Flatten(new, nil, true) } // Diff the primitive fields. diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) // Checks diffs if cDiffs := serviceCheckDiffs(old.Checks, new.Checks, contextual); cDiffs != nil { diff.Objects = append(diff.Objects, cDiffs...) } return diff }
// Diff returns a diff of two resource objects. If contextual diff is enabled, // non-changed fields will still be returned. func (r *Resources) Diff(other *Resources, contextual bool) *ObjectDiff { diff := &ObjectDiff{Type: DiffTypeNone, Name: "Resources"} var oldPrimitiveFlat, newPrimitiveFlat map[string]string if reflect.DeepEqual(r, other) { return nil } else if r == nil { r = &Resources{} diff.Type = DiffTypeAdded newPrimitiveFlat = flatmap.Flatten(other, nil, true) } else if other == nil { other = &Resources{} diff.Type = DiffTypeDeleted oldPrimitiveFlat = flatmap.Flatten(r, nil, true) } else { diff.Type = DiffTypeEdited oldPrimitiveFlat = flatmap.Flatten(r, nil, true) newPrimitiveFlat = flatmap.Flatten(other, nil, true) } // Diff the primitive fields. diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) // Network Resources diff if nDiffs := networkResourceDiffs(r.Networks, other.Networks, contextual); nDiffs != nil { diff.Objects = append(diff.Objects, nDiffs...) } return diff }
// vaultDiff returns the diff of two vault objects. If contextual diff is // enabled, all fields will be returned, even if no diff occurred. func vaultDiff(old, new *Vault, contextual bool) *ObjectDiff { diff := &ObjectDiff{Type: DiffTypeNone, Name: "Vault"} var oldPrimitiveFlat, newPrimitiveFlat map[string]string if reflect.DeepEqual(old, new) { return nil } else if old == nil { old = &Vault{} diff.Type = DiffTypeAdded newPrimitiveFlat = flatmap.Flatten(new, nil, true) } else if new == nil { new = &Vault{} diff.Type = DiffTypeDeleted oldPrimitiveFlat = flatmap.Flatten(old, nil, true) } else { diff.Type = DiffTypeEdited oldPrimitiveFlat = flatmap.Flatten(old, nil, true) newPrimitiveFlat = flatmap.Flatten(new, nil, true) } // Diff the primitive fields. diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) // Policies diffs if setDiff := stringSetDiff(old.Policies, new.Policies, "Policies", contextual); setDiff != nil { diff.Objects = append(diff.Objects, setDiff) } return diff }
// Diff returns a diff of two task groups. If contextual diff is enabled, // objects' fields will be stored even if no diff occurred as long as one field // changed. func (tg *TaskGroup) Diff(other *TaskGroup, contextual bool) (*TaskGroupDiff, error) { diff := &TaskGroupDiff{Type: DiffTypeNone} var oldPrimitiveFlat, newPrimitiveFlat map[string]string filter := []string{"Name"} if tg == nil && other == nil { return diff, nil } else if tg == nil { tg = &TaskGroup{} diff.Type = DiffTypeAdded diff.Name = other.Name newPrimitiveFlat = flatmap.Flatten(other, filter, true) } else if other == nil { other = &TaskGroup{} diff.Type = DiffTypeDeleted diff.Name = tg.Name oldPrimitiveFlat = flatmap.Flatten(tg, filter, true) } else { if !reflect.DeepEqual(tg, other) { diff.Type = DiffTypeEdited } if tg.Name != other.Name { return nil, fmt.Errorf("can not diff task groups with different names: %q and %q", tg.Name, other.Name) } diff.Name = other.Name oldPrimitiveFlat = flatmap.Flatten(tg, filter, true) newPrimitiveFlat = flatmap.Flatten(other, filter, true) } // Diff the primitive fields. diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, false) // Constraints diff conDiff := primitiveObjectSetDiff( interfaceSlice(tg.Constraints), interfaceSlice(other.Constraints), []string{"str"}, "Constraint", contextual) if conDiff != nil { diff.Objects = append(diff.Objects, conDiff...) } // Restart policy diff rDiff := primitiveObjectDiff(tg.RestartPolicy, other.RestartPolicy, nil, "RestartPolicy", contextual) if rDiff != nil { diff.Objects = append(diff.Objects, rDiff) } // Tasks diff tasks, err := taskDiffs(tg.Tasks, other.Tasks, contextual) if err != nil { return nil, err } diff.Tasks = tasks return diff, nil }
// configDiff returns the diff of two Task Config objects. If contextual diff is // enabled, all fields will be returned, even if no diff occurred. func configDiff(old, new map[string]interface{}, contextual bool) *ObjectDiff { diff := &ObjectDiff{Type: DiffTypeNone, Name: "Config"} if reflect.DeepEqual(old, new) { return nil } else if len(old) == 0 { diff.Type = DiffTypeAdded } else if len(new) == 0 { diff.Type = DiffTypeDeleted } else { diff.Type = DiffTypeEdited } // Diff the primitive fields. oldPrimitiveFlat := flatmap.Flatten(old, nil, false) newPrimitiveFlat := flatmap.Flatten(new, nil, false) diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) return diff }
// Diff returns a diff of two network resources. If contextual diff is enabled, // non-changed fields will still be returned. func (r *NetworkResource) Diff(other *NetworkResource, contextual bool) *ObjectDiff { diff := &ObjectDiff{Type: DiffTypeNone, Name: "Network"} var oldPrimitiveFlat, newPrimitiveFlat map[string]string filter := []string{"Device", "CIDR", "IP"} if reflect.DeepEqual(r, other) { return nil } else if r == nil { r = &NetworkResource{} diff.Type = DiffTypeAdded newPrimitiveFlat = flatmap.Flatten(other, filter, true) } else if other == nil { other = &NetworkResource{} diff.Type = DiffTypeDeleted oldPrimitiveFlat = flatmap.Flatten(r, filter, true) } else { diff.Type = DiffTypeEdited oldPrimitiveFlat = flatmap.Flatten(r, filter, true) newPrimitiveFlat = flatmap.Flatten(other, filter, true) } // Diff the primitive fields. diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) // Port diffs resPorts := portDiffs(r.ReservedPorts, other.ReservedPorts, false, contextual) dynPorts := portDiffs(r.DynamicPorts, other.DynamicPorts, true, contextual) if resPorts != nil { diff.Objects = append(diff.Objects, resPorts...) } if dynPorts != nil { diff.Objects = append(diff.Objects, dynPorts...) } return diff }
// serviceCheckDiff returns the diff of two service check objects. If contextual // diff is enabled, all fields will be returned, even if no diff occurred. func serviceCheckDiff(old, new *ServiceCheck, contextual bool) *ObjectDiff { diff := &ObjectDiff{Type: DiffTypeNone, Name: "Check"} var oldPrimitiveFlat, newPrimitiveFlat map[string]string if reflect.DeepEqual(old, new) { return nil } else if old == nil { old = &ServiceCheck{} diff.Type = DiffTypeAdded newPrimitiveFlat = flatmap.Flatten(new, nil, true) } else if new == nil { new = &ServiceCheck{} diff.Type = DiffTypeDeleted oldPrimitiveFlat = flatmap.Flatten(old, nil, true) } else { diff.Type = DiffTypeEdited oldPrimitiveFlat = flatmap.Flatten(old, nil, true) newPrimitiveFlat = flatmap.Flatten(new, nil, true) } // Diff the primitive fields. diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) return diff }
// Diff returns a diff of two jobs and a potential error if the Jobs are not // diffable. If contextual diff is enabled, objects within the job will contain // field information even if unchanged. func (j *Job) Diff(other *Job, contextual bool) (*JobDiff, error) { diff := &JobDiff{Type: DiffTypeNone} var oldPrimitiveFlat, newPrimitiveFlat map[string]string filter := []string{"ID", "Status", "StatusDescription", "CreateIndex", "ModifyIndex", "JobModifyIndex"} // Have to treat this special since it is a struct literal, not a pointer var jUpdate, otherUpdate *UpdateStrategy if j == nil && other == nil { return diff, nil } else if j == nil { j = &Job{} otherUpdate = &other.Update diff.Type = DiffTypeAdded newPrimitiveFlat = flatmap.Flatten(other, filter, true) diff.ID = other.ID } else if other == nil { other = &Job{} jUpdate = &j.Update diff.Type = DiffTypeDeleted oldPrimitiveFlat = flatmap.Flatten(j, filter, true) diff.ID = j.ID } else { if j.ID != other.ID { return nil, fmt.Errorf("can not diff jobs with different IDs: %q and %q", j.ID, other.ID) } jUpdate = &j.Update otherUpdate = &other.Update oldPrimitiveFlat = flatmap.Flatten(j, filter, true) newPrimitiveFlat = flatmap.Flatten(other, filter, true) diff.ID = other.ID } // Diff the primitive fields. diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, false) // Datacenters diff if setDiff := stringSetDiff(j.Datacenters, other.Datacenters, "Datacenters"); setDiff != nil { diff.Objects = append(diff.Objects, setDiff) } // Constraints diff conDiff := primitiveObjectSetDiff( interfaceSlice(j.Constraints), interfaceSlice(other.Constraints), []string{"str"}, "Constraint", contextual) if conDiff != nil { diff.Objects = append(diff.Objects, conDiff...) } // Task groups diff tgs, err := taskGroupDiffs(j.TaskGroups, other.TaskGroups, contextual) if err != nil { return nil, err } diff.TaskGroups = tgs // Update diff if uDiff := primitiveObjectDiff(jUpdate, otherUpdate, nil, "Update", contextual); uDiff != nil { diff.Objects = append(diff.Objects, uDiff) } // Periodic diff if pDiff := primitiveObjectDiff(j.Periodic, other.Periodic, nil, "Periodic", contextual); pDiff != nil { diff.Objects = append(diff.Objects, pDiff) } // If the job is not a delete or add, determine if there are edits. if diff.Type == DiffTypeNone { tgEdit := false for _, tg := range diff.TaskGroups { if tg.Type != DiffTypeNone { tgEdit = true break } } if tgEdit || len(diff.Fields)+len(diff.Objects) != 0 { diff.Type = DiffTypeEdited } } return diff, nil }
// Diff returns a diff of two tasks. If contextual diff is enabled, objects // within the task will contain field information even if unchanged. func (t *Task) Diff(other *Task, contextual bool) (*TaskDiff, error) { diff := &TaskDiff{Type: DiffTypeNone} var oldPrimitiveFlat, newPrimitiveFlat map[string]string filter := []string{"Name", "Config"} if t == nil && other == nil { return diff, nil } else if t == nil { t = &Task{} diff.Type = DiffTypeAdded diff.Name = other.Name newPrimitiveFlat = flatmap.Flatten(other, filter, true) } else if other == nil { other = &Task{} diff.Type = DiffTypeDeleted diff.Name = t.Name oldPrimitiveFlat = flatmap.Flatten(t, filter, true) } else { if !reflect.DeepEqual(t, other) { diff.Type = DiffTypeEdited } if t.Name != other.Name { return nil, fmt.Errorf("can not diff tasks with different names: %q and %q", t.Name, other.Name) } diff.Name = other.Name oldPrimitiveFlat = flatmap.Flatten(t, filter, true) newPrimitiveFlat = flatmap.Flatten(other, filter, true) } // Diff the primitive fields. diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, false) // Constraints diff conDiff := primitiveObjectSetDiff( interfaceSlice(t.Constraints), interfaceSlice(other.Constraints), []string{"str"}, "Constraint", contextual) if conDiff != nil { diff.Objects = append(diff.Objects, conDiff...) } // Config diff if cDiff := configDiff(t.Config, other.Config, contextual); cDiff != nil { diff.Objects = append(diff.Objects, cDiff) } // Resources diff if rDiff := t.Resources.Diff(other.Resources, contextual); rDiff != nil { diff.Objects = append(diff.Objects, rDiff) } // LogConfig diff lDiff := primitiveObjectDiff(t.LogConfig, other.LogConfig, nil, "LogConfig", contextual) if lDiff != nil { diff.Objects = append(diff.Objects, lDiff) } // Artifacts diff diffs := primitiveObjectSetDiff( interfaceSlice(t.Artifacts), interfaceSlice(other.Artifacts), nil, "Artifact", contextual) if diffs != nil { diff.Objects = append(diff.Objects, diffs...) } // Services diff if sDiffs := serviceDiffs(t.Services, other.Services, contextual); sDiffs != nil { diff.Objects = append(diff.Objects, sDiffs...) } return diff, nil }