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