216 lines
5.1 KiB
Go
216 lines
5.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package scheduler
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
)
|
|
|
|
const (
|
|
AnnotationForcesCreate = "forces create"
|
|
AnnotationForcesDestroy = "forces destroy"
|
|
AnnotationForcesInplaceUpdate = "forces in-place update"
|
|
AnnotationForcesDestructiveUpdate = "forces create/destroy update"
|
|
)
|
|
|
|
// UpdateTypes denote the type of update to occur against the task group.
|
|
const (
|
|
UpdateTypeIgnore = "ignore"
|
|
UpdateTypeCreate = "create"
|
|
UpdateTypeDestroy = "destroy"
|
|
UpdateTypeMigrate = "migrate"
|
|
UpdateTypeCanary = "canary"
|
|
UpdateTypeInplaceUpdate = "in-place update"
|
|
UpdateTypeDestructiveUpdate = "create/destroy update"
|
|
)
|
|
|
|
// Annotate takes the diff between the old and new version of a Job, the
|
|
// scheduler's plan annotations and will add annotations to the diff to aide
|
|
// human understanding of the plan.
|
|
//
|
|
// Currently the things that are annotated are:
|
|
// * Task group changes will be annotated with:
|
|
// - Count up and count down changes
|
|
// - Update counts (creates, destroys, migrates, etc)
|
|
//
|
|
// * Task changes will be annotated with:
|
|
// - forces create/destroy update
|
|
// - forces in-place update
|
|
func Annotate(diff *structs.JobDiff, annotations *structs.PlanAnnotations) error {
|
|
tgDiffs := diff.TaskGroups
|
|
if len(tgDiffs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, tgDiff := range tgDiffs {
|
|
if err := annotateTaskGroup(tgDiff, annotations); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// annotateTaskGroup takes a task group diff and annotates it.
|
|
func annotateTaskGroup(diff *structs.TaskGroupDiff, annotations *structs.PlanAnnotations) error {
|
|
// Annotate the updates
|
|
if annotations != nil {
|
|
tg, ok := annotations.DesiredTGUpdates[diff.Name]
|
|
if ok {
|
|
if diff.Updates == nil {
|
|
diff.Updates = make(map[string]uint64, 6)
|
|
}
|
|
|
|
if tg.Ignore != 0 {
|
|
diff.Updates[UpdateTypeIgnore] = tg.Ignore
|
|
}
|
|
if tg.Place != 0 {
|
|
diff.Updates[UpdateTypeCreate] = tg.Place
|
|
}
|
|
if tg.Migrate != 0 {
|
|
diff.Updates[UpdateTypeMigrate] = tg.Migrate
|
|
}
|
|
if tg.Stop != 0 {
|
|
diff.Updates[UpdateTypeDestroy] = tg.Stop
|
|
}
|
|
if tg.Canary != 0 {
|
|
diff.Updates[UpdateTypeCanary] = tg.Canary
|
|
}
|
|
if tg.InPlaceUpdate != 0 {
|
|
diff.Updates[UpdateTypeInplaceUpdate] = tg.InPlaceUpdate
|
|
}
|
|
if tg.DestructiveUpdate != 0 {
|
|
diff.Updates[UpdateTypeDestructiveUpdate] = tg.DestructiveUpdate
|
|
}
|
|
}
|
|
}
|
|
|
|
// Annotate the count
|
|
if err := annotateCountChange(diff); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Annotate the tasks.
|
|
taskDiffs := diff.Tasks
|
|
if len(taskDiffs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, taskDiff := range taskDiffs {
|
|
annotateTask(taskDiff, diff)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// annotateCountChange takes a task group diff and annotates the count
|
|
// parameter.
|
|
func annotateCountChange(diff *structs.TaskGroupDiff) error {
|
|
var countDiff *structs.FieldDiff
|
|
for _, diff := range diff.Fields {
|
|
if diff.Name == "Count" {
|
|
countDiff = diff
|
|
break
|
|
}
|
|
}
|
|
|
|
// Didn't find
|
|
if countDiff == nil {
|
|
return nil
|
|
}
|
|
var oldV, newV int
|
|
var err error
|
|
if countDiff.Old == "" {
|
|
oldV = 0
|
|
} else {
|
|
oldV, err = strconv.Atoi(countDiff.Old)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if countDiff.New == "" {
|
|
newV = 0
|
|
} else {
|
|
newV, err = strconv.Atoi(countDiff.New)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if oldV < newV {
|
|
countDiff.Annotations = append(countDiff.Annotations, AnnotationForcesCreate)
|
|
} else if newV < oldV {
|
|
countDiff.Annotations = append(countDiff.Annotations, AnnotationForcesDestroy)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// annotateCountChange takes a task diff and annotates it.
|
|
func annotateTask(diff *structs.TaskDiff, parent *structs.TaskGroupDiff) {
|
|
if diff.Type == structs.DiffTypeNone {
|
|
return
|
|
}
|
|
|
|
// The whole task group is changing
|
|
if parent.Type == structs.DiffTypeAdded || parent.Type == structs.DiffTypeDeleted {
|
|
if diff.Type == structs.DiffTypeAdded {
|
|
diff.Annotations = append(diff.Annotations, AnnotationForcesCreate)
|
|
return
|
|
} else if diff.Type == structs.DiffTypeDeleted {
|
|
diff.Annotations = append(diff.Annotations, AnnotationForcesDestroy)
|
|
return
|
|
}
|
|
}
|
|
|
|
// All changes to primitive fields result in a destructive update except
|
|
// KillTimeout
|
|
destructive := false
|
|
FieldsLoop:
|
|
for _, fDiff := range diff.Fields {
|
|
switch fDiff.Name {
|
|
case "KillTimeout":
|
|
continue
|
|
default:
|
|
destructive = true
|
|
break FieldsLoop
|
|
}
|
|
}
|
|
|
|
// Object changes that can be done in-place are log configs, services,
|
|
// constraints.
|
|
|
|
if !destructive {
|
|
ObjectsLoop:
|
|
for _, oDiff := range diff.Objects {
|
|
switch oDiff.Name {
|
|
case "Service", "Constraint":
|
|
continue
|
|
case "LogConfig":
|
|
for _, fDiff := range oDiff.Fields {
|
|
switch fDiff.Name {
|
|
// force a destructive update if logger was enabled or disabled
|
|
case "Disabled":
|
|
destructive = true
|
|
break ObjectsLoop
|
|
}
|
|
}
|
|
continue
|
|
default:
|
|
destructive = true
|
|
break ObjectsLoop
|
|
}
|
|
}
|
|
}
|
|
|
|
if destructive {
|
|
diff.Annotations = append(diff.Annotations, AnnotationForcesDestructiveUpdate)
|
|
} else {
|
|
diff.Annotations = append(diff.Annotations, AnnotationForcesInplaceUpdate)
|
|
}
|
|
}
|