github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/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  FieldsLoop:
   170  	for _, fDiff := range diff.Fields {
   171  		switch fDiff.Name {
   172  		case "KillTimeout":
   173  			continue
   174  		default:
   175  			destructive = true
   176  			break FieldsLoop
   177  		}
   178  	}
   179  
   180  	// Object changes that can be done in-place are log configs, services,
   181  	// constraints.
   182  
   183  	if !destructive {
   184  	ObjectsLoop:
   185  		for _, oDiff := range diff.Objects {
   186  			switch oDiff.Name {
   187  			case "LogConfig", "Service", "Constraint":
   188  				continue
   189  			default:
   190  				destructive = true
   191  				break ObjectsLoop
   192  			}
   193  		}
   194  	}
   195  
   196  	if destructive {
   197  		diff.Annotations = append(diff.Annotations, AnnotationForcesDestructiveUpdate)
   198  	} else {
   199  		diff.Annotations = append(diff.Annotations, AnnotationForcesInplaceUpdate)
   200  	}
   201  }