github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/scheduler/annotate.go (about) 1 package scheduler 2 3 import ( 4 "strconv" 5 6 "github.com/hashicorp/nomad/nomad/structs" 7 ) 8 9 const ( 10 AnnotationForcesCreate = "forces create" 11 AnnotationForcesDestroy = "forces destroy" 12 AnnotationForcesInplaceUpdate = "forces in-place update" 13 AnnotationForcesDestructiveUpdate = "forces create/destroy update" 14 ) 15 16 // UpdateTypes denote the type of update to occur against the task group. 17 const ( 18 UpdateTypeIgnore = "ignore" 19 UpdateTypeCreate = "create" 20 UpdateTypeDestroy = "destroy" 21 UpdateTypeMigrate = "migrate" 22 UpdateTypeCanary = "canary" 23 UpdateTypeInplaceUpdate = "in-place update" 24 UpdateTypeDestructiveUpdate = "create/destroy update" 25 ) 26 27 // Annotate takes the diff between the old and new version of a Job, the 28 // scheduler's plan annotations and will add annotations to the diff to aide 29 // human understanding of the plan. 30 // 31 // Currently the things that are annotated are: 32 // * Task group changes will be annotated with: 33 // * Count up and count down changes 34 // * Update counts (creates, destroys, migrates, etc) 35 // * Task changes will be annotated with: 36 // * forces create/destroy update 37 // * forces in-place update 38 func Annotate(diff *structs.JobDiff, annotations *structs.PlanAnnotations) error { 39 tgDiffs := diff.TaskGroups 40 if len(tgDiffs) == 0 { 41 return nil 42 } 43 44 for _, tgDiff := range tgDiffs { 45 if err := annotateTaskGroup(tgDiff, annotations); err != nil { 46 return err 47 } 48 } 49 50 return nil 51 } 52 53 // annotateTaskGroup takes a task group diff and annotates it. 54 func annotateTaskGroup(diff *structs.TaskGroupDiff, annotations *structs.PlanAnnotations) error { 55 // Annotate the updates 56 if annotations != nil { 57 tg, ok := annotations.DesiredTGUpdates[diff.Name] 58 if ok { 59 if diff.Updates == nil { 60 diff.Updates = make(map[string]uint64, 6) 61 } 62 63 if tg.Ignore != 0 { 64 diff.Updates[UpdateTypeIgnore] = tg.Ignore 65 } 66 if tg.Place != 0 { 67 diff.Updates[UpdateTypeCreate] = tg.Place 68 } 69 if tg.Migrate != 0 { 70 diff.Updates[UpdateTypeMigrate] = tg.Migrate 71 } 72 if tg.Stop != 0 { 73 diff.Updates[UpdateTypeDestroy] = tg.Stop 74 } 75 if tg.Canary != 0 { 76 diff.Updates[UpdateTypeCanary] = tg.Canary 77 } 78 if tg.InPlaceUpdate != 0 { 79 diff.Updates[UpdateTypeInplaceUpdate] = tg.InPlaceUpdate 80 } 81 if tg.DestructiveUpdate != 0 { 82 diff.Updates[UpdateTypeDestructiveUpdate] = tg.DestructiveUpdate 83 } 84 } 85 } 86 87 // Annotate the count 88 if err := annotateCountChange(diff); err != nil { 89 return err 90 } 91 92 // Annotate the tasks. 93 taskDiffs := diff.Tasks 94 if len(taskDiffs) == 0 { 95 return nil 96 } 97 98 for _, taskDiff := range taskDiffs { 99 annotateTask(taskDiff, diff) 100 } 101 102 return nil 103 } 104 105 // annotateCountChange takes a task group diff and annotates the count 106 // parameter. 107 func annotateCountChange(diff *structs.TaskGroupDiff) error { 108 var countDiff *structs.FieldDiff 109 for _, diff := range diff.Fields { 110 if diff.Name == "Count" { 111 countDiff = diff 112 break 113 } 114 } 115 116 // Didn't find 117 if countDiff == nil { 118 return nil 119 } 120 var oldV, newV int 121 var err error 122 if countDiff.Old == "" { 123 oldV = 0 124 } else { 125 oldV, err = strconv.Atoi(countDiff.Old) 126 if err != nil { 127 return err 128 } 129 } 130 131 if countDiff.New == "" { 132 newV = 0 133 } else { 134 newV, err = strconv.Atoi(countDiff.New) 135 if err != nil { 136 return err 137 } 138 } 139 140 if oldV < newV { 141 countDiff.Annotations = append(countDiff.Annotations, AnnotationForcesCreate) 142 } else if newV < oldV { 143 countDiff.Annotations = append(countDiff.Annotations, AnnotationForcesDestroy) 144 } 145 146 return nil 147 } 148 149 // annotateCountChange takes a task diff and annotates it. 150 func annotateTask(diff *structs.TaskDiff, parent *structs.TaskGroupDiff) { 151 if diff.Type == structs.DiffTypeNone { 152 return 153 } 154 155 // The whole task group is changing 156 if parent.Type == structs.DiffTypeAdded || parent.Type == structs.DiffTypeDeleted { 157 if diff.Type == structs.DiffTypeAdded { 158 diff.Annotations = append(diff.Annotations, AnnotationForcesCreate) 159 return 160 } else if diff.Type == structs.DiffTypeDeleted { 161 diff.Annotations = append(diff.Annotations, AnnotationForcesDestroy) 162 return 163 } 164 } 165 166 // All changes to primitive fields result in a destructive update except 167 // KillTimeout 168 destructive := false 169 for _, fDiff := range diff.Fields { 170 switch fDiff.Name { 171 case "KillTimeout": 172 continue 173 default: 174 destructive = true 175 break 176 } 177 } 178 179 // Object changes that can be done in-place are log configs, services, 180 // constraints. 181 if !destructive { 182 for _, oDiff := range diff.Objects { 183 switch oDiff.Name { 184 case "LogConfig", "Service", "Constraint": 185 continue 186 default: 187 destructive = true 188 break 189 } 190 } 191 } 192 193 if destructive { 194 diff.Annotations = append(diff.Annotations, AnnotationForcesDestructiveUpdate) 195 } else { 196 diff.Annotations = append(diff.Annotations, AnnotationForcesInplaceUpdate) 197 } 198 }