17bd930ca9
When the server restarts for the upgrade, it loads the `structs.Job` from the Raft snapshot/logs. The jobspec has long since been parsed, so none of the guards around the default value are in play. The empty field value for `Enabled` is the zero value, which is false. This doesn't impact any running allocation because we don't replace running allocations when either the client or server restart. But as soon as any allocation gets rescheduled (ex. you drain all your clients during upgrades), it'll be using the `structs.Job` that the server has, which has `Enabled = false`, and logs will not be collected. This changeset fixes the bug by adding a new field `Disabled` which defaults to false (so that the zero value works), and deprecates the old field. Fixes #17076
469 lines
11 KiB
Go
469 lines
11 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package scheduler
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/nomad/ci"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
)
|
|
|
|
func TestAnnotateTaskGroup_Updates(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
annotations := &structs.PlanAnnotations{
|
|
DesiredTGUpdates: map[string]*structs.DesiredUpdates{
|
|
"foo": {
|
|
Ignore: 1,
|
|
Place: 2,
|
|
Migrate: 3,
|
|
Stop: 4,
|
|
InPlaceUpdate: 5,
|
|
DestructiveUpdate: 6,
|
|
Canary: 7,
|
|
},
|
|
},
|
|
}
|
|
|
|
tgDiff := &structs.TaskGroupDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "foo",
|
|
}
|
|
expected := &structs.TaskGroupDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "foo",
|
|
Updates: map[string]uint64{
|
|
UpdateTypeIgnore: 1,
|
|
UpdateTypeCreate: 2,
|
|
UpdateTypeMigrate: 3,
|
|
UpdateTypeDestroy: 4,
|
|
UpdateTypeInplaceUpdate: 5,
|
|
UpdateTypeDestructiveUpdate: 6,
|
|
UpdateTypeCanary: 7,
|
|
},
|
|
}
|
|
|
|
if err := annotateTaskGroup(tgDiff, annotations); err != nil {
|
|
t.Fatalf("annotateTaskGroup(%#v, %#v) failed: %#v", tgDiff, annotations, err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(tgDiff, expected) {
|
|
t.Fatalf("got %#v, want %#v", tgDiff, expected)
|
|
}
|
|
}
|
|
|
|
func TestAnnotateCountChange_NonEdited(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tg := &structs.TaskGroupDiff{}
|
|
tgOrig := &structs.TaskGroupDiff{}
|
|
annotateCountChange(tg)
|
|
if !reflect.DeepEqual(tgOrig, tg) {
|
|
t.Fatalf("annotateCountChange(%#v) should not have caused any annotation: %#v", tgOrig, tg)
|
|
}
|
|
}
|
|
|
|
func TestAnnotateCountChange(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
up := &structs.FieldDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "Count",
|
|
Old: "1",
|
|
New: "3",
|
|
}
|
|
down := &structs.FieldDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "Count",
|
|
Old: "3",
|
|
New: "1",
|
|
}
|
|
tgUp := &structs.TaskGroupDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Fields: []*structs.FieldDiff{up},
|
|
}
|
|
tgDown := &structs.TaskGroupDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Fields: []*structs.FieldDiff{down},
|
|
}
|
|
|
|
// Test the up case
|
|
if err := annotateCountChange(tgUp); err != nil {
|
|
t.Fatalf("annotateCountChange(%#v) failed: %v", tgUp, err)
|
|
}
|
|
countDiff := tgUp.Fields[0]
|
|
if len(countDiff.Annotations) != 1 || countDiff.Annotations[0] != AnnotationForcesCreate {
|
|
t.Fatalf("incorrect annotation: %#v", tgUp)
|
|
}
|
|
|
|
// Test the down case
|
|
if err := annotateCountChange(tgDown); err != nil {
|
|
t.Fatalf("annotateCountChange(%#v) failed: %v", tgDown, err)
|
|
}
|
|
countDiff = tgDown.Fields[0]
|
|
if len(countDiff.Annotations) != 1 || countDiff.Annotations[0] != AnnotationForcesDestroy {
|
|
t.Fatalf("incorrect annotation: %#v", tgDown)
|
|
}
|
|
}
|
|
|
|
func TestAnnotateTask_NonEdited(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
tgd := &structs.TaskGroupDiff{Type: structs.DiffTypeNone}
|
|
td := &structs.TaskDiff{Type: structs.DiffTypeNone}
|
|
tdOrig := &structs.TaskDiff{Type: structs.DiffTypeNone}
|
|
annotateTask(td, tgd)
|
|
if !reflect.DeepEqual(tdOrig, td) {
|
|
t.Fatalf("annotateTask(%#v) should not have caused any annotation: %#v", tdOrig, td)
|
|
}
|
|
}
|
|
|
|
func TestAnnotateTask(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
cases := []struct {
|
|
Diff *structs.TaskDiff
|
|
Parent *structs.TaskGroupDiff
|
|
Desired string
|
|
}{
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "Driver",
|
|
Old: "docker",
|
|
New: "exec",
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "User",
|
|
Old: "alice",
|
|
New: "bob",
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Env[foo]",
|
|
Old: "foo",
|
|
New: "bar",
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Meta[foo]",
|
|
Old: "foo",
|
|
New: "bar",
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Objects: []*structs.ObjectDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Artifact",
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "GetterOptions[bam]",
|
|
Old: "",
|
|
New: "baz",
|
|
},
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "GetterSource",
|
|
Old: "",
|
|
New: "bam",
|
|
},
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "RelativeDest",
|
|
Old: "",
|
|
New: "bam",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Objects: []*structs.ObjectDiff{
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "Resources",
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "CPU",
|
|
Old: "100",
|
|
New: "200",
|
|
},
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "DiskMB",
|
|
Old: "100",
|
|
New: "200",
|
|
},
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "MemoryMB",
|
|
Old: "100",
|
|
New: "200",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Objects: []*structs.ObjectDiff{
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "Config",
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "bam[1]",
|
|
Old: "b",
|
|
New: "c",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Objects: []*structs.ObjectDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Constraint",
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "LTarget",
|
|
Old: "",
|
|
New: "baz",
|
|
},
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Operand",
|
|
Old: "",
|
|
New: "baz",
|
|
},
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "RTarget",
|
|
Old: "",
|
|
New: "baz",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesInplaceUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Objects: []*structs.ObjectDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "LogConfig",
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "MaxFileSizeMB",
|
|
Old: "",
|
|
New: "10",
|
|
},
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "MaxFiles",
|
|
Old: "",
|
|
New: "1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesInplaceUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Objects: []*structs.ObjectDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "LogConfig",
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Disabled",
|
|
Old: "true",
|
|
New: "false",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Objects: []*structs.ObjectDiff{
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "Service",
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "PortLabel",
|
|
Old: "baz",
|
|
New: "baz2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesInplaceUpdate,
|
|
},
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeEdited,
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeEdited,
|
|
Name: "KillTimeout",
|
|
Old: "200",
|
|
New: "2000000",
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesInplaceUpdate,
|
|
},
|
|
// Task deleted new parent
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeDeleted,
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Driver",
|
|
Old: "",
|
|
New: "exec",
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeAdded},
|
|
Desired: AnnotationForcesDestroy,
|
|
},
|
|
// Task Added new parent
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeAdded,
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Driver",
|
|
Old: "",
|
|
New: "exec",
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeAdded},
|
|
Desired: AnnotationForcesCreate,
|
|
},
|
|
// Task deleted existing parent
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeDeleted,
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Driver",
|
|
Old: "",
|
|
New: "exec",
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
// Task Added existing parent
|
|
{
|
|
Diff: &structs.TaskDiff{
|
|
Type: structs.DiffTypeAdded,
|
|
Fields: []*structs.FieldDiff{
|
|
{
|
|
Type: structs.DiffTypeAdded,
|
|
Name: "Driver",
|
|
Old: "",
|
|
New: "exec",
|
|
},
|
|
},
|
|
},
|
|
Parent: &structs.TaskGroupDiff{Type: structs.DiffTypeEdited},
|
|
Desired: AnnotationForcesDestructiveUpdate,
|
|
},
|
|
}
|
|
|
|
for i, c := range cases {
|
|
annotateTask(c.Diff, c.Parent)
|
|
if len(c.Diff.Annotations) != 1 || c.Diff.Annotations[0] != c.Desired {
|
|
t.Fatalf("case %d not properly annotated; got %s, want %s", i+1, c.Diff.Annotations[0], c.Desired)
|
|
}
|
|
}
|
|
}
|