github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/nomad/structs/diff_test.go (about)

     1  package structs
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hashicorp/nomad/helper"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestJobDiff(t *testing.T) {
    13  	cases := []struct {
    14  		Old, New   *Job
    15  		Expected   *JobDiff
    16  		Error      bool
    17  		Contextual bool
    18  	}{
    19  		{
    20  			Old: nil,
    21  			New: nil,
    22  			Expected: &JobDiff{
    23  				Type: DiffTypeNone,
    24  			},
    25  		},
    26  		{
    27  			// Different IDs
    28  			Old: &Job{
    29  				ID: "foo",
    30  			},
    31  			New: &Job{
    32  				ID: "bar",
    33  			},
    34  			Error: true,
    35  		},
    36  		{
    37  			// Primitive only that is the same
    38  			Old: &Job{
    39  				Region:    "foo",
    40  				ID:        "foo",
    41  				Name:      "foo",
    42  				Type:      "batch",
    43  				Priority:  10,
    44  				AllAtOnce: true,
    45  				Meta: map[string]string{
    46  					"foo": "bar",
    47  				},
    48  			},
    49  			New: &Job{
    50  				Region:    "foo",
    51  				ID:        "foo",
    52  				Name:      "foo",
    53  				Type:      "batch",
    54  				Priority:  10,
    55  				AllAtOnce: true,
    56  				Meta: map[string]string{
    57  					"foo": "bar",
    58  				},
    59  			},
    60  			Expected: &JobDiff{
    61  				Type: DiffTypeNone,
    62  				ID:   "foo",
    63  			},
    64  		},
    65  		{
    66  			// Primitive only that is has diffs
    67  			Old: &Job{
    68  				Region:    "foo",
    69  				ID:        "foo",
    70  				Name:      "foo",
    71  				Type:      "batch",
    72  				Priority:  10,
    73  				AllAtOnce: true,
    74  				Meta: map[string]string{
    75  					"foo": "bar",
    76  				},
    77  			},
    78  			New: &Job{
    79  				Region:    "bar",
    80  				ID:        "foo",
    81  				Name:      "bar",
    82  				Type:      "system",
    83  				Priority:  100,
    84  				AllAtOnce: false,
    85  				Meta: map[string]string{
    86  					"foo": "baz",
    87  				},
    88  			},
    89  			Expected: &JobDiff{
    90  				Type: DiffTypeEdited,
    91  				ID:   "foo",
    92  				Fields: []*FieldDiff{
    93  					{
    94  						Type: DiffTypeEdited,
    95  						Name: "AllAtOnce",
    96  						Old:  "true",
    97  						New:  "false",
    98  					},
    99  					{
   100  						Type: DiffTypeEdited,
   101  						Name: "Meta[foo]",
   102  						Old:  "bar",
   103  						New:  "baz",
   104  					},
   105  					{
   106  						Type: DiffTypeEdited,
   107  						Name: "Name",
   108  						Old:  "foo",
   109  						New:  "bar",
   110  					},
   111  					{
   112  						Type: DiffTypeEdited,
   113  						Name: "Priority",
   114  						Old:  "10",
   115  						New:  "100",
   116  					},
   117  					{
   118  						Type: DiffTypeEdited,
   119  						Name: "Region",
   120  						Old:  "foo",
   121  						New:  "bar",
   122  					},
   123  					{
   124  						Type: DiffTypeEdited,
   125  						Name: "Type",
   126  						Old:  "batch",
   127  						New:  "system",
   128  					},
   129  				},
   130  			},
   131  		},
   132  		{
   133  			// Primitive only deleted job
   134  			Old: &Job{
   135  				Region:    "foo",
   136  				ID:        "foo",
   137  				Name:      "foo",
   138  				Type:      "batch",
   139  				Priority:  10,
   140  				AllAtOnce: true,
   141  				Meta: map[string]string{
   142  					"foo": "bar",
   143  				},
   144  			},
   145  			New: nil,
   146  			Expected: &JobDiff{
   147  				Type: DiffTypeDeleted,
   148  				ID:   "foo",
   149  				Fields: []*FieldDiff{
   150  					{
   151  						Type: DiffTypeDeleted,
   152  						Name: "AllAtOnce",
   153  						Old:  "true",
   154  						New:  "",
   155  					},
   156  					{
   157  						Type: DiffTypeDeleted,
   158  						Name: "Dispatched",
   159  						Old:  "false",
   160  						New:  "",
   161  					},
   162  					{
   163  						Type: DiffTypeDeleted,
   164  						Name: "Meta[foo]",
   165  						Old:  "bar",
   166  						New:  "",
   167  					},
   168  					{
   169  						Type: DiffTypeDeleted,
   170  						Name: "Name",
   171  						Old:  "foo",
   172  						New:  "",
   173  					},
   174  					{
   175  						Type: DiffTypeDeleted,
   176  						Name: "Priority",
   177  						Old:  "10",
   178  						New:  "",
   179  					},
   180  					{
   181  						Type: DiffTypeDeleted,
   182  						Name: "Region",
   183  						Old:  "foo",
   184  						New:  "",
   185  					},
   186  					{
   187  						Type: DiffTypeDeleted,
   188  						Name: "Stop",
   189  						Old:  "false",
   190  						New:  "",
   191  					},
   192  					{
   193  						Type: DiffTypeDeleted,
   194  						Name: "Type",
   195  						Old:  "batch",
   196  						New:  "",
   197  					},
   198  				},
   199  			},
   200  		},
   201  		{
   202  			// Primitive only added job
   203  			Old: nil,
   204  			New: &Job{
   205  				Region:    "foo",
   206  				ID:        "foo",
   207  				Name:      "foo",
   208  				Type:      "batch",
   209  				Priority:  10,
   210  				AllAtOnce: true,
   211  				Meta: map[string]string{
   212  					"foo": "bar",
   213  				},
   214  			},
   215  			Expected: &JobDiff{
   216  				Type: DiffTypeAdded,
   217  				ID:   "foo",
   218  				Fields: []*FieldDiff{
   219  					{
   220  						Type: DiffTypeAdded,
   221  						Name: "AllAtOnce",
   222  						Old:  "",
   223  						New:  "true",
   224  					},
   225  					{
   226  						Type: DiffTypeAdded,
   227  						Name: "Dispatched",
   228  						Old:  "",
   229  						New:  "false",
   230  					},
   231  					{
   232  						Type: DiffTypeAdded,
   233  						Name: "Meta[foo]",
   234  						Old:  "",
   235  						New:  "bar",
   236  					},
   237  					{
   238  						Type: DiffTypeAdded,
   239  						Name: "Name",
   240  						Old:  "",
   241  						New:  "foo",
   242  					},
   243  					{
   244  						Type: DiffTypeAdded,
   245  						Name: "Priority",
   246  						Old:  "",
   247  						New:  "10",
   248  					},
   249  					{
   250  						Type: DiffTypeAdded,
   251  						Name: "Region",
   252  						Old:  "",
   253  						New:  "foo",
   254  					},
   255  					{
   256  						Type: DiffTypeAdded,
   257  						Name: "Stop",
   258  						Old:  "",
   259  						New:  "false",
   260  					},
   261  					{
   262  						Type: DiffTypeAdded,
   263  						Name: "Type",
   264  						Old:  "",
   265  						New:  "batch",
   266  					},
   267  				},
   268  			},
   269  		},
   270  		{
   271  			// Map diff
   272  			Old: &Job{
   273  				Meta: map[string]string{
   274  					"foo": "foo",
   275  					"bar": "bar",
   276  				},
   277  			},
   278  			New: &Job{
   279  				Meta: map[string]string{
   280  					"bar": "bar",
   281  					"baz": "baz",
   282  				},
   283  			},
   284  			Expected: &JobDiff{
   285  				Type: DiffTypeEdited,
   286  				Fields: []*FieldDiff{
   287  					{
   288  						Type: DiffTypeAdded,
   289  						Name: "Meta[baz]",
   290  						Old:  "",
   291  						New:  "baz",
   292  					},
   293  					{
   294  						Type: DiffTypeDeleted,
   295  						Name: "Meta[foo]",
   296  						Old:  "foo",
   297  						New:  "",
   298  					},
   299  				},
   300  			},
   301  		},
   302  		{
   303  			// Datacenter diff both added and removed
   304  			Old: &Job{
   305  				Datacenters: []string{"foo", "bar"},
   306  			},
   307  			New: &Job{
   308  				Datacenters: []string{"baz", "bar"},
   309  			},
   310  			Expected: &JobDiff{
   311  				Type: DiffTypeEdited,
   312  				Objects: []*ObjectDiff{
   313  					{
   314  						Type: DiffTypeEdited,
   315  						Name: "Datacenters",
   316  						Fields: []*FieldDiff{
   317  							{
   318  								Type: DiffTypeAdded,
   319  								Name: "Datacenters",
   320  								Old:  "",
   321  								New:  "baz",
   322  							},
   323  							{
   324  								Type: DiffTypeDeleted,
   325  								Name: "Datacenters",
   326  								Old:  "foo",
   327  								New:  "",
   328  							},
   329  						},
   330  					},
   331  				},
   332  			},
   333  		},
   334  		{
   335  			// Datacenter diff just added
   336  			Old: &Job{
   337  				Datacenters: []string{"foo", "bar"},
   338  			},
   339  			New: &Job{
   340  				Datacenters: []string{"foo", "bar", "baz"},
   341  			},
   342  			Expected: &JobDiff{
   343  				Type: DiffTypeEdited,
   344  				Objects: []*ObjectDiff{
   345  					{
   346  						Type: DiffTypeAdded,
   347  						Name: "Datacenters",
   348  						Fields: []*FieldDiff{
   349  							{
   350  								Type: DiffTypeAdded,
   351  								Name: "Datacenters",
   352  								Old:  "",
   353  								New:  "baz",
   354  							},
   355  						},
   356  					},
   357  				},
   358  			},
   359  		},
   360  		{
   361  			// Datacenter diff just deleted
   362  			Old: &Job{
   363  				Datacenters: []string{"foo", "bar"},
   364  			},
   365  			New: &Job{
   366  				Datacenters: []string{"foo"},
   367  			},
   368  			Expected: &JobDiff{
   369  				Type: DiffTypeEdited,
   370  				Objects: []*ObjectDiff{
   371  					{
   372  						Type: DiffTypeDeleted,
   373  						Name: "Datacenters",
   374  						Fields: []*FieldDiff{
   375  							{
   376  								Type: DiffTypeDeleted,
   377  								Name: "Datacenters",
   378  								Old:  "bar",
   379  								New:  "",
   380  							},
   381  						},
   382  					},
   383  				},
   384  			},
   385  		},
   386  		{
   387  			// Datacenter contextual no change
   388  			Contextual: true,
   389  			Old: &Job{
   390  				Datacenters: []string{"foo", "bar"},
   391  			},
   392  			New: &Job{
   393  				Datacenters: []string{"foo", "bar"},
   394  			},
   395  			Expected: &JobDiff{
   396  				Type: DiffTypeNone,
   397  			},
   398  		},
   399  		{
   400  			// Datacenter contextual
   401  			Contextual: true,
   402  			Old: &Job{
   403  				Datacenters: []string{"foo", "bar"},
   404  			},
   405  			New: &Job{
   406  				Datacenters: []string{"foo", "bar", "baz"},
   407  			},
   408  			Expected: &JobDiff{
   409  				Type: DiffTypeEdited,
   410  				Objects: []*ObjectDiff{
   411  					{
   412  						Type: DiffTypeAdded,
   413  						Name: "Datacenters",
   414  						Fields: []*FieldDiff{
   415  							{
   416  								Type: DiffTypeAdded,
   417  								Name: "Datacenters",
   418  								Old:  "",
   419  								New:  "baz",
   420  							},
   421  							{
   422  								Type: DiffTypeNone,
   423  								Name: "Datacenters",
   424  								Old:  "bar",
   425  								New:  "bar",
   426  							},
   427  							{
   428  								Type: DiffTypeNone,
   429  								Name: "Datacenters",
   430  								Old:  "foo",
   431  								New:  "foo",
   432  							},
   433  						},
   434  					},
   435  				},
   436  			},
   437  		},
   438  		{
   439  			// Periodic added
   440  			Old: &Job{},
   441  			New: &Job{
   442  				Periodic: &PeriodicConfig{
   443  					Enabled:         false,
   444  					Spec:            "*/15 * * * * *",
   445  					SpecType:        "foo",
   446  					ProhibitOverlap: false,
   447  					TimeZone:        "Europe/Minsk",
   448  				},
   449  			},
   450  			Expected: &JobDiff{
   451  				Type: DiffTypeEdited,
   452  				Objects: []*ObjectDiff{
   453  					{
   454  						Type: DiffTypeAdded,
   455  						Name: "Periodic",
   456  						Fields: []*FieldDiff{
   457  							{
   458  								Type: DiffTypeAdded,
   459  								Name: "Enabled",
   460  								Old:  "",
   461  								New:  "false",
   462  							},
   463  							{
   464  								Type: DiffTypeAdded,
   465  								Name: "ProhibitOverlap",
   466  								Old:  "",
   467  								New:  "false",
   468  							},
   469  							{
   470  								Type: DiffTypeAdded,
   471  								Name: "Spec",
   472  								Old:  "",
   473  								New:  "*/15 * * * * *",
   474  							},
   475  							{
   476  								Type: DiffTypeAdded,
   477  								Name: "SpecType",
   478  								Old:  "",
   479  								New:  "foo",
   480  							},
   481  							{
   482  								Type: DiffTypeAdded,
   483  								Name: "TimeZone",
   484  								Old:  "",
   485  								New:  "Europe/Minsk",
   486  							},
   487  						},
   488  					},
   489  				},
   490  			},
   491  		},
   492  		{
   493  			// Periodic deleted
   494  			Old: &Job{
   495  				Periodic: &PeriodicConfig{
   496  					Enabled:         false,
   497  					Spec:            "*/15 * * * * *",
   498  					SpecType:        "foo",
   499  					ProhibitOverlap: false,
   500  					TimeZone:        "Europe/Minsk",
   501  				},
   502  			},
   503  			New: &Job{},
   504  			Expected: &JobDiff{
   505  				Type: DiffTypeEdited,
   506  				Objects: []*ObjectDiff{
   507  					{
   508  						Type: DiffTypeDeleted,
   509  						Name: "Periodic",
   510  						Fields: []*FieldDiff{
   511  							{
   512  								Type: DiffTypeDeleted,
   513  								Name: "Enabled",
   514  								Old:  "false",
   515  								New:  "",
   516  							},
   517  							{
   518  								Type: DiffTypeDeleted,
   519  								Name: "ProhibitOverlap",
   520  								Old:  "false",
   521  								New:  "",
   522  							},
   523  							{
   524  								Type: DiffTypeDeleted,
   525  								Name: "Spec",
   526  								Old:  "*/15 * * * * *",
   527  								New:  "",
   528  							},
   529  							{
   530  								Type: DiffTypeDeleted,
   531  								Name: "SpecType",
   532  								Old:  "foo",
   533  								New:  "",
   534  							},
   535  							{
   536  								Type: DiffTypeDeleted,
   537  								Name: "TimeZone",
   538  								Old:  "Europe/Minsk",
   539  								New:  "",
   540  							},
   541  						},
   542  					},
   543  				},
   544  			},
   545  		},
   546  		{
   547  			// Periodic edited
   548  			Old: &Job{
   549  				Periodic: &PeriodicConfig{
   550  					Enabled:         false,
   551  					Spec:            "*/15 * * * * *",
   552  					SpecType:        "foo",
   553  					ProhibitOverlap: false,
   554  					TimeZone:        "Europe/Minsk",
   555  				},
   556  			},
   557  			New: &Job{
   558  				Periodic: &PeriodicConfig{
   559  					Enabled:         true,
   560  					Spec:            "* * * * * *",
   561  					SpecType:        "cron",
   562  					ProhibitOverlap: true,
   563  					TimeZone:        "America/Los_Angeles",
   564  				},
   565  			},
   566  			Expected: &JobDiff{
   567  				Type: DiffTypeEdited,
   568  				Objects: []*ObjectDiff{
   569  					{
   570  						Type: DiffTypeEdited,
   571  						Name: "Periodic",
   572  						Fields: []*FieldDiff{
   573  							{
   574  								Type: DiffTypeEdited,
   575  								Name: "Enabled",
   576  								Old:  "false",
   577  								New:  "true",
   578  							},
   579  							{
   580  								Type: DiffTypeEdited,
   581  								Name: "ProhibitOverlap",
   582  								Old:  "false",
   583  								New:  "true",
   584  							},
   585  							{
   586  								Type: DiffTypeEdited,
   587  								Name: "Spec",
   588  								Old:  "*/15 * * * * *",
   589  								New:  "* * * * * *",
   590  							},
   591  							{
   592  								Type: DiffTypeEdited,
   593  								Name: "SpecType",
   594  								Old:  "foo",
   595  								New:  "cron",
   596  							},
   597  							{
   598  								Type: DiffTypeEdited,
   599  								Name: "TimeZone",
   600  								Old:  "Europe/Minsk",
   601  								New:  "America/Los_Angeles",
   602  							},
   603  						},
   604  					},
   605  				},
   606  			},
   607  		},
   608  		{
   609  			// Periodic edited with context
   610  			Contextual: true,
   611  			Old: &Job{
   612  				Periodic: &PeriodicConfig{
   613  					Enabled:         false,
   614  					Spec:            "*/15 * * * * *",
   615  					SpecType:        "foo",
   616  					ProhibitOverlap: false,
   617  					TimeZone:        "Europe/Minsk",
   618  				},
   619  			},
   620  			New: &Job{
   621  				Periodic: &PeriodicConfig{
   622  					Enabled:         true,
   623  					Spec:            "* * * * * *",
   624  					SpecType:        "foo",
   625  					ProhibitOverlap: false,
   626  					TimeZone:        "Europe/Minsk",
   627  				},
   628  			},
   629  			Expected: &JobDiff{
   630  				Type: DiffTypeEdited,
   631  				Objects: []*ObjectDiff{
   632  					{
   633  						Type: DiffTypeEdited,
   634  						Name: "Periodic",
   635  						Fields: []*FieldDiff{
   636  							{
   637  								Type: DiffTypeEdited,
   638  								Name: "Enabled",
   639  								Old:  "false",
   640  								New:  "true",
   641  							},
   642  							{
   643  								Type: DiffTypeNone,
   644  								Name: "ProhibitOverlap",
   645  								Old:  "false",
   646  								New:  "false",
   647  							},
   648  							{
   649  								Type: DiffTypeEdited,
   650  								Name: "Spec",
   651  								Old:  "*/15 * * * * *",
   652  								New:  "* * * * * *",
   653  							},
   654  							{
   655  								Type: DiffTypeNone,
   656  								Name: "SpecType",
   657  								Old:  "foo",
   658  								New:  "foo",
   659  							},
   660  							{
   661  								Type: DiffTypeNone,
   662  								Name: "TimeZone",
   663  								Old:  "Europe/Minsk",
   664  								New:  "Europe/Minsk",
   665  							},
   666  						},
   667  					},
   668  				},
   669  			},
   670  		},
   671  		{
   672  			// Constraints edited
   673  			Old: &Job{
   674  				Constraints: []*Constraint{
   675  					{
   676  						LTarget: "foo",
   677  						RTarget: "foo",
   678  						Operand: "foo",
   679  						str:     "foo",
   680  					},
   681  					{
   682  						LTarget: "bar",
   683  						RTarget: "bar",
   684  						Operand: "bar",
   685  						str:     "bar",
   686  					},
   687  				},
   688  			},
   689  			New: &Job{
   690  				Constraints: []*Constraint{
   691  					{
   692  						LTarget: "foo",
   693  						RTarget: "foo",
   694  						Operand: "foo",
   695  						str:     "foo",
   696  					},
   697  					{
   698  						LTarget: "baz",
   699  						RTarget: "baz",
   700  						Operand: "baz",
   701  						str:     "baz",
   702  					},
   703  				},
   704  			},
   705  			Expected: &JobDiff{
   706  				Type: DiffTypeEdited,
   707  				Objects: []*ObjectDiff{
   708  					{
   709  						Type: DiffTypeAdded,
   710  						Name: "Constraint",
   711  						Fields: []*FieldDiff{
   712  							{
   713  								Type: DiffTypeAdded,
   714  								Name: "LTarget",
   715  								Old:  "",
   716  								New:  "baz",
   717  							},
   718  							{
   719  								Type: DiffTypeAdded,
   720  								Name: "Operand",
   721  								Old:  "",
   722  								New:  "baz",
   723  							},
   724  							{
   725  								Type: DiffTypeAdded,
   726  								Name: "RTarget",
   727  								Old:  "",
   728  								New:  "baz",
   729  							},
   730  						},
   731  					},
   732  					{
   733  						Type: DiffTypeDeleted,
   734  						Name: "Constraint",
   735  						Fields: []*FieldDiff{
   736  							{
   737  								Type: DiffTypeDeleted,
   738  								Name: "LTarget",
   739  								Old:  "bar",
   740  								New:  "",
   741  							},
   742  							{
   743  								Type: DiffTypeDeleted,
   744  								Name: "Operand",
   745  								Old:  "bar",
   746  								New:  "",
   747  							},
   748  							{
   749  								Type: DiffTypeDeleted,
   750  								Name: "RTarget",
   751  								Old:  "bar",
   752  								New:  "",
   753  							},
   754  						},
   755  					},
   756  				},
   757  			},
   758  		},
   759  		{
   760  			// Affinities edited
   761  			Old: &Job{
   762  				Affinities: []*Affinity{
   763  					{
   764  						LTarget: "foo",
   765  						RTarget: "foo",
   766  						Operand: "foo",
   767  						Weight:  20,
   768  						str:     "foo",
   769  					},
   770  					{
   771  						LTarget: "bar",
   772  						RTarget: "bar",
   773  						Operand: "bar",
   774  						Weight:  20,
   775  						str:     "bar",
   776  					},
   777  				},
   778  			},
   779  			New: &Job{
   780  				Affinities: []*Affinity{
   781  					{
   782  						LTarget: "foo",
   783  						RTarget: "foo",
   784  						Operand: "foo",
   785  						Weight:  20,
   786  						str:     "foo",
   787  					},
   788  					{
   789  						LTarget: "baz",
   790  						RTarget: "baz",
   791  						Operand: "baz",
   792  						Weight:  20,
   793  						str:     "baz",
   794  					},
   795  				},
   796  			},
   797  			Expected: &JobDiff{
   798  				Type: DiffTypeEdited,
   799  				Objects: []*ObjectDiff{
   800  					{
   801  						Type: DiffTypeAdded,
   802  						Name: "Affinity",
   803  						Fields: []*FieldDiff{
   804  							{
   805  								Type: DiffTypeAdded,
   806  								Name: "LTarget",
   807  								Old:  "",
   808  								New:  "baz",
   809  							},
   810  							{
   811  								Type: DiffTypeAdded,
   812  								Name: "Operand",
   813  								Old:  "",
   814  								New:  "baz",
   815  							},
   816  							{
   817  								Type: DiffTypeAdded,
   818  								Name: "RTarget",
   819  								Old:  "",
   820  								New:  "baz",
   821  							},
   822  							{
   823  								Type: DiffTypeAdded,
   824  								Name: "Weight",
   825  								Old:  "",
   826  								New:  "20",
   827  							},
   828  						},
   829  					},
   830  					{
   831  						Type: DiffTypeDeleted,
   832  						Name: "Affinity",
   833  						Fields: []*FieldDiff{
   834  							{
   835  								Type: DiffTypeDeleted,
   836  								Name: "LTarget",
   837  								Old:  "bar",
   838  								New:  "",
   839  							},
   840  							{
   841  								Type: DiffTypeDeleted,
   842  								Name: "Operand",
   843  								Old:  "bar",
   844  								New:  "",
   845  							},
   846  							{
   847  								Type: DiffTypeDeleted,
   848  								Name: "RTarget",
   849  								Old:  "bar",
   850  								New:  "",
   851  							},
   852  							{
   853  								Type: DiffTypeDeleted,
   854  								Name: "Weight",
   855  								Old:  "20",
   856  								New:  "",
   857  							},
   858  						},
   859  					},
   860  				},
   861  			},
   862  		},
   863  		{
   864  			// Task groups edited
   865  			Old: &Job{
   866  				TaskGroups: []*TaskGroup{
   867  					{
   868  						Name:  "foo",
   869  						Count: 1,
   870  					},
   871  					{
   872  						Name:  "bar",
   873  						Count: 1,
   874  					},
   875  					{
   876  						Name:  "baz",
   877  						Count: 1,
   878  					},
   879  				},
   880  			},
   881  			New: &Job{
   882  				TaskGroups: []*TaskGroup{
   883  					{
   884  						Name:  "bar",
   885  						Count: 1,
   886  					},
   887  					{
   888  						Name:  "baz",
   889  						Count: 2,
   890  					},
   891  					{
   892  						Name:  "bam",
   893  						Count: 1,
   894  					},
   895  				},
   896  			},
   897  			Expected: &JobDiff{
   898  				Type: DiffTypeEdited,
   899  				TaskGroups: []*TaskGroupDiff{
   900  					{
   901  						Type: DiffTypeAdded,
   902  						Name: "bam",
   903  						Fields: []*FieldDiff{
   904  							{
   905  								Type: DiffTypeAdded,
   906  								Name: "Count",
   907  								Old:  "",
   908  								New:  "1",
   909  							},
   910  						},
   911  					},
   912  					{
   913  						Type: DiffTypeNone,
   914  						Name: "bar",
   915  					},
   916  					{
   917  						Type: DiffTypeEdited,
   918  						Name: "baz",
   919  						Fields: []*FieldDiff{
   920  							{
   921  								Type: DiffTypeEdited,
   922  								Name: "Count",
   923  								Old:  "1",
   924  								New:  "2",
   925  							},
   926  						},
   927  					},
   928  					{
   929  						Type: DiffTypeDeleted,
   930  						Name: "foo",
   931  						Fields: []*FieldDiff{
   932  							{
   933  								Type: DiffTypeDeleted,
   934  								Name: "Count",
   935  								Old:  "1",
   936  								New:  "",
   937  							},
   938  						},
   939  					},
   940  				},
   941  			},
   942  		},
   943  		{
   944  			// Parameterized Job added
   945  			Old: &Job{},
   946  			New: &Job{
   947  				ParameterizedJob: &ParameterizedJobConfig{
   948  					Payload:      DispatchPayloadRequired,
   949  					MetaOptional: []string{"foo"},
   950  					MetaRequired: []string{"bar"},
   951  				},
   952  			},
   953  			Expected: &JobDiff{
   954  				Type: DiffTypeEdited,
   955  				Objects: []*ObjectDiff{
   956  					{
   957  						Type: DiffTypeAdded,
   958  						Name: "ParameterizedJob",
   959  						Fields: []*FieldDiff{
   960  							{
   961  								Type: DiffTypeAdded,
   962  								Name: "Payload",
   963  								Old:  "",
   964  								New:  DispatchPayloadRequired,
   965  							},
   966  						},
   967  						Objects: []*ObjectDiff{
   968  							{
   969  								Type: DiffTypeAdded,
   970  								Name: "MetaOptional",
   971  								Fields: []*FieldDiff{
   972  									{
   973  										Type: DiffTypeAdded,
   974  										Name: "MetaOptional",
   975  										Old:  "",
   976  										New:  "foo",
   977  									},
   978  								},
   979  							},
   980  							{
   981  								Type: DiffTypeAdded,
   982  								Name: "MetaRequired",
   983  								Fields: []*FieldDiff{
   984  									{
   985  										Type: DiffTypeAdded,
   986  										Name: "MetaRequired",
   987  										Old:  "",
   988  										New:  "bar",
   989  									},
   990  								},
   991  							},
   992  						},
   993  					},
   994  				},
   995  			},
   996  		},
   997  		{
   998  			// Parameterized Job deleted
   999  			Old: &Job{
  1000  				ParameterizedJob: &ParameterizedJobConfig{
  1001  					Payload:      DispatchPayloadRequired,
  1002  					MetaOptional: []string{"foo"},
  1003  					MetaRequired: []string{"bar"},
  1004  				},
  1005  			},
  1006  			New: &Job{},
  1007  			Expected: &JobDiff{
  1008  				Type: DiffTypeEdited,
  1009  				Objects: []*ObjectDiff{
  1010  					{
  1011  						Type: DiffTypeDeleted,
  1012  						Name: "ParameterizedJob",
  1013  						Fields: []*FieldDiff{
  1014  							{
  1015  								Type: DiffTypeDeleted,
  1016  								Name: "Payload",
  1017  								Old:  DispatchPayloadRequired,
  1018  								New:  "",
  1019  							},
  1020  						},
  1021  						Objects: []*ObjectDiff{
  1022  							{
  1023  								Type: DiffTypeDeleted,
  1024  								Name: "MetaOptional",
  1025  								Fields: []*FieldDiff{
  1026  									{
  1027  										Type: DiffTypeDeleted,
  1028  										Name: "MetaOptional",
  1029  										Old:  "foo",
  1030  										New:  "",
  1031  									},
  1032  								},
  1033  							},
  1034  							{
  1035  								Type: DiffTypeDeleted,
  1036  								Name: "MetaRequired",
  1037  								Fields: []*FieldDiff{
  1038  									{
  1039  										Type: DiffTypeDeleted,
  1040  										Name: "MetaRequired",
  1041  										Old:  "bar",
  1042  										New:  "",
  1043  									},
  1044  								},
  1045  							},
  1046  						},
  1047  					},
  1048  				},
  1049  			},
  1050  		},
  1051  		{
  1052  			// Parameterized Job edited
  1053  			Old: &Job{
  1054  				ParameterizedJob: &ParameterizedJobConfig{
  1055  					Payload:      DispatchPayloadRequired,
  1056  					MetaOptional: []string{"foo"},
  1057  					MetaRequired: []string{"bar"},
  1058  				},
  1059  			},
  1060  			New: &Job{
  1061  				ParameterizedJob: &ParameterizedJobConfig{
  1062  					Payload:      DispatchPayloadOptional,
  1063  					MetaOptional: []string{"bam"},
  1064  					MetaRequired: []string{"bang"},
  1065  				},
  1066  			},
  1067  			Expected: &JobDiff{
  1068  				Type: DiffTypeEdited,
  1069  				Objects: []*ObjectDiff{
  1070  					{
  1071  						Type: DiffTypeEdited,
  1072  						Name: "ParameterizedJob",
  1073  						Fields: []*FieldDiff{
  1074  							{
  1075  								Type: DiffTypeEdited,
  1076  								Name: "Payload",
  1077  								Old:  DispatchPayloadRequired,
  1078  								New:  DispatchPayloadOptional,
  1079  							},
  1080  						},
  1081  						Objects: []*ObjectDiff{
  1082  							{
  1083  								Type: DiffTypeEdited,
  1084  								Name: "MetaOptional",
  1085  								Fields: []*FieldDiff{
  1086  									{
  1087  										Type: DiffTypeAdded,
  1088  										Name: "MetaOptional",
  1089  										Old:  "",
  1090  										New:  "bam",
  1091  									},
  1092  									{
  1093  										Type: DiffTypeDeleted,
  1094  										Name: "MetaOptional",
  1095  										Old:  "foo",
  1096  										New:  "",
  1097  									},
  1098  								},
  1099  							},
  1100  							{
  1101  								Type: DiffTypeEdited,
  1102  								Name: "MetaRequired",
  1103  								Fields: []*FieldDiff{
  1104  									{
  1105  										Type: DiffTypeAdded,
  1106  										Name: "MetaRequired",
  1107  										Old:  "",
  1108  										New:  "bang",
  1109  									},
  1110  									{
  1111  										Type: DiffTypeDeleted,
  1112  										Name: "MetaRequired",
  1113  										Old:  "bar",
  1114  										New:  "",
  1115  									},
  1116  								},
  1117  							},
  1118  						},
  1119  					},
  1120  				},
  1121  			},
  1122  		},
  1123  		{
  1124  			// Parameterized Job edited with context
  1125  			Contextual: true,
  1126  			Old: &Job{
  1127  				ParameterizedJob: &ParameterizedJobConfig{
  1128  					Payload:      DispatchPayloadRequired,
  1129  					MetaOptional: []string{"foo"},
  1130  					MetaRequired: []string{"bar"},
  1131  				},
  1132  			},
  1133  			New: &Job{
  1134  				ParameterizedJob: &ParameterizedJobConfig{
  1135  					Payload:      DispatchPayloadOptional,
  1136  					MetaOptional: []string{"foo"},
  1137  					MetaRequired: []string{"bar"},
  1138  				},
  1139  			},
  1140  			Expected: &JobDiff{
  1141  				Type: DiffTypeEdited,
  1142  				Objects: []*ObjectDiff{
  1143  					{
  1144  						Type: DiffTypeEdited,
  1145  						Name: "ParameterizedJob",
  1146  						Fields: []*FieldDiff{
  1147  							{
  1148  								Type: DiffTypeEdited,
  1149  								Name: "Payload",
  1150  								Old:  DispatchPayloadRequired,
  1151  								New:  DispatchPayloadOptional,
  1152  							},
  1153  						},
  1154  						Objects: []*ObjectDiff{
  1155  							{
  1156  								Type: DiffTypeNone,
  1157  								Name: "MetaOptional",
  1158  								Fields: []*FieldDiff{
  1159  									{
  1160  										Type: DiffTypeNone,
  1161  										Name: "MetaOptional",
  1162  										Old:  "foo",
  1163  										New:  "foo",
  1164  									},
  1165  								},
  1166  							},
  1167  							{
  1168  								Type: DiffTypeNone,
  1169  								Name: "MetaRequired",
  1170  								Fields: []*FieldDiff{
  1171  									{
  1172  										Type: DiffTypeNone,
  1173  										Name: "MetaRequired",
  1174  										Old:  "bar",
  1175  										New:  "bar",
  1176  									},
  1177  								},
  1178  							},
  1179  						},
  1180  					},
  1181  				},
  1182  			},
  1183  		},
  1184  
  1185  		{
  1186  			// Multiregion: region added
  1187  			Old: &Job{
  1188  				NomadTokenID: "abcdef",
  1189  				Multiregion: &Multiregion{
  1190  					Strategy: &MultiregionStrategy{
  1191  						MaxParallel: 1,
  1192  						OnFailure:   "fail_all",
  1193  					},
  1194  					Regions: []*MultiregionRegion{
  1195  						{
  1196  							Name:        "west",
  1197  							Count:       1,
  1198  							Datacenters: []string{"west-1"},
  1199  							Meta:        map[string]string{"region_code": "W"},
  1200  						},
  1201  					},
  1202  				},
  1203  			},
  1204  
  1205  			New: &Job{
  1206  				NomadTokenID: "12345",
  1207  				Multiregion: &Multiregion{
  1208  					Strategy: &MultiregionStrategy{
  1209  						MaxParallel: 2,
  1210  						OnFailure:   "fail_all",
  1211  					},
  1212  					Regions: []*MultiregionRegion{
  1213  						{
  1214  							Name:        "west",
  1215  							Count:       3,
  1216  							Datacenters: []string{"west-2"},
  1217  							Meta:        map[string]string{"region_code": "W"},
  1218  						},
  1219  						{
  1220  							Name:        "east",
  1221  							Count:       2,
  1222  							Datacenters: []string{"east-1", "east-2"},
  1223  							Meta:        map[string]string{"region_code": "E"},
  1224  						},
  1225  					},
  1226  				},
  1227  			},
  1228  			Expected: &JobDiff{
  1229  				Type: DiffTypeEdited,
  1230  				Objects: []*ObjectDiff{
  1231  					{
  1232  						Type: DiffTypeEdited,
  1233  						Name: "Multiregion",
  1234  						Objects: []*ObjectDiff{
  1235  							{
  1236  								Type: DiffTypeEdited,
  1237  								Name: "Region",
  1238  								Fields: []*FieldDiff{
  1239  									{
  1240  										Type: DiffTypeEdited,
  1241  										Name: "Count",
  1242  										Old:  "1",
  1243  										New:  "3",
  1244  									},
  1245  								},
  1246  								Objects: []*ObjectDiff{
  1247  									{
  1248  										Type: DiffTypeEdited,
  1249  										Name: "Datacenters",
  1250  										Fields: []*FieldDiff{
  1251  											{
  1252  												Type: DiffTypeAdded,
  1253  												Name: "Datacenters",
  1254  												Old:  "",
  1255  												New:  "west-2",
  1256  											},
  1257  											{
  1258  												Type: DiffTypeDeleted,
  1259  												Name: "Datacenters",
  1260  												Old:  "west-1",
  1261  												New:  "",
  1262  											},
  1263  										},
  1264  									},
  1265  								},
  1266  							},
  1267  							{
  1268  								Type: DiffTypeAdded,
  1269  								Name: "Region",
  1270  								Fields: []*FieldDiff{
  1271  									{
  1272  										Type: DiffTypeAdded,
  1273  										Name: "Count",
  1274  										Old:  "",
  1275  										New:  "2",
  1276  									},
  1277  									{
  1278  										Type: DiffTypeAdded,
  1279  										Name: "Meta[region_code]",
  1280  										Old:  "",
  1281  										New:  "E",
  1282  									},
  1283  									{
  1284  										Type: DiffTypeAdded,
  1285  										Name: "Name",
  1286  										Old:  "",
  1287  										New:  "east",
  1288  									},
  1289  								},
  1290  
  1291  								Objects: []*ObjectDiff{
  1292  									{
  1293  										Type: DiffTypeAdded,
  1294  										Name: "Datacenters",
  1295  										Fields: []*FieldDiff{
  1296  											{
  1297  												Type: DiffTypeAdded,
  1298  												Name: "Datacenters",
  1299  												Old:  "",
  1300  												New:  "east-1",
  1301  											},
  1302  											{
  1303  												Type: DiffTypeAdded,
  1304  												Name: "Datacenters",
  1305  												Old:  "",
  1306  												New:  "east-2",
  1307  											},
  1308  										},
  1309  									},
  1310  								},
  1311  							},
  1312  							{
  1313  								Type: DiffTypeEdited,
  1314  								Name: "Strategy",
  1315  								Fields: []*FieldDiff{
  1316  									{
  1317  										Type: DiffTypeEdited,
  1318  										Name: "MaxParallel",
  1319  										Old:  "1",
  1320  										New:  "2",
  1321  									},
  1322  								},
  1323  							},
  1324  						},
  1325  					},
  1326  				},
  1327  			},
  1328  		},
  1329  	}
  1330  
  1331  	for i, c := range cases {
  1332  		actual, err := c.Old.Diff(c.New, c.Contextual)
  1333  		if c.Error && err == nil {
  1334  			t.Fatalf("case %d: expected errored", i+1)
  1335  		} else if err != nil {
  1336  			if !c.Error {
  1337  				t.Fatalf("case %d: errored %#v", i+1, err)
  1338  			} else {
  1339  				continue
  1340  			}
  1341  		}
  1342  
  1343  		if !reflect.DeepEqual(actual, c.Expected) {
  1344  			t.Fatalf("case %d: got:\n%#v\n want:\n%#v\n",
  1345  				i+1, actual, c.Expected)
  1346  		}
  1347  	}
  1348  }
  1349  
  1350  func TestTaskGroupDiff(t *testing.T) {
  1351  	cases := []struct {
  1352  		TestCase   string
  1353  		Old, New   *TaskGroup
  1354  		Expected   *TaskGroupDiff
  1355  		ExpErr     bool
  1356  		Contextual bool
  1357  	}{
  1358  		{
  1359  			TestCase: "Empty",
  1360  			Old:      nil,
  1361  			New:      nil,
  1362  			Expected: &TaskGroupDiff{
  1363  				Type: DiffTypeNone,
  1364  			},
  1365  		},
  1366  		{
  1367  			TestCase: "Primitive only that has different names",
  1368  			Old: &TaskGroup{
  1369  				Name:  "foo",
  1370  				Count: 10,
  1371  				Meta: map[string]string{
  1372  					"foo": "bar",
  1373  				},
  1374  			},
  1375  			New: &TaskGroup{
  1376  				Name:  "bar",
  1377  				Count: 10,
  1378  				Meta: map[string]string{
  1379  					"foo": "bar",
  1380  				},
  1381  			},
  1382  			ExpErr: true,
  1383  		},
  1384  		{
  1385  			TestCase: "Primitive only that is the same",
  1386  			Old: &TaskGroup{
  1387  				Name:  "foo",
  1388  				Count: 10,
  1389  				Meta: map[string]string{
  1390  					"foo": "bar",
  1391  				},
  1392  			},
  1393  			New: &TaskGroup{
  1394  				Name:  "foo",
  1395  				Count: 10,
  1396  				Meta: map[string]string{
  1397  					"foo": "bar",
  1398  				},
  1399  			},
  1400  			Expected: &TaskGroupDiff{
  1401  				Type: DiffTypeNone,
  1402  				Name: "foo",
  1403  			},
  1404  		},
  1405  		{
  1406  			TestCase: "Primitive only that has diffs",
  1407  			Old: &TaskGroup{
  1408  				Name:  "foo",
  1409  				Count: 10,
  1410  				Meta: map[string]string{
  1411  					"foo": "bar",
  1412  				},
  1413  			},
  1414  			New: &TaskGroup{
  1415  				Name:  "foo",
  1416  				Count: 100,
  1417  				Meta: map[string]string{
  1418  					"foo": "baz",
  1419  				},
  1420  			},
  1421  			Expected: &TaskGroupDiff{
  1422  				Type: DiffTypeEdited,
  1423  				Name: "foo",
  1424  				Fields: []*FieldDiff{
  1425  					{
  1426  						Type: DiffTypeEdited,
  1427  						Name: "Count",
  1428  						Old:  "10",
  1429  						New:  "100",
  1430  					},
  1431  					{
  1432  						Type: DiffTypeEdited,
  1433  						Name: "Meta[foo]",
  1434  						Old:  "bar",
  1435  						New:  "baz",
  1436  					},
  1437  				},
  1438  			},
  1439  		},
  1440  		{
  1441  			TestCase: "Map diff",
  1442  			Old: &TaskGroup{
  1443  				Meta: map[string]string{
  1444  					"foo": "foo",
  1445  					"bar": "bar",
  1446  				},
  1447  			},
  1448  			New: &TaskGroup{
  1449  				Meta: map[string]string{
  1450  					"bar": "bar",
  1451  					"baz": "baz",
  1452  				},
  1453  			},
  1454  			Expected: &TaskGroupDiff{
  1455  				Type: DiffTypeEdited,
  1456  				Fields: []*FieldDiff{
  1457  					{
  1458  						Type: DiffTypeAdded,
  1459  						Name: "Meta[baz]",
  1460  						Old:  "",
  1461  						New:  "baz",
  1462  					},
  1463  					{
  1464  						Type: DiffTypeDeleted,
  1465  						Name: "Meta[foo]",
  1466  						Old:  "foo",
  1467  						New:  "",
  1468  					},
  1469  				},
  1470  			},
  1471  		},
  1472  		{
  1473  			TestCase: "Constraints edited",
  1474  			Old: &TaskGroup{
  1475  				Constraints: []*Constraint{
  1476  					{
  1477  						LTarget: "foo",
  1478  						RTarget: "foo",
  1479  						Operand: "foo",
  1480  						str:     "foo",
  1481  					},
  1482  					{
  1483  						LTarget: "bar",
  1484  						RTarget: "bar",
  1485  						Operand: "bar",
  1486  						str:     "bar",
  1487  					},
  1488  				},
  1489  			},
  1490  			New: &TaskGroup{
  1491  				Constraints: []*Constraint{
  1492  					{
  1493  						LTarget: "foo",
  1494  						RTarget: "foo",
  1495  						Operand: "foo",
  1496  						str:     "foo",
  1497  					},
  1498  					{
  1499  						LTarget: "baz",
  1500  						RTarget: "baz",
  1501  						Operand: "baz",
  1502  						str:     "baz",
  1503  					},
  1504  				},
  1505  			},
  1506  			Expected: &TaskGroupDiff{
  1507  				Type: DiffTypeEdited,
  1508  				Objects: []*ObjectDiff{
  1509  					{
  1510  						Type: DiffTypeAdded,
  1511  						Name: "Constraint",
  1512  						Fields: []*FieldDiff{
  1513  							{
  1514  								Type: DiffTypeAdded,
  1515  								Name: "LTarget",
  1516  								Old:  "",
  1517  								New:  "baz",
  1518  							},
  1519  							{
  1520  								Type: DiffTypeAdded,
  1521  								Name: "Operand",
  1522  								Old:  "",
  1523  								New:  "baz",
  1524  							},
  1525  							{
  1526  								Type: DiffTypeAdded,
  1527  								Name: "RTarget",
  1528  								Old:  "",
  1529  								New:  "baz",
  1530  							},
  1531  						},
  1532  					},
  1533  					{
  1534  						Type: DiffTypeDeleted,
  1535  						Name: "Constraint",
  1536  						Fields: []*FieldDiff{
  1537  							{
  1538  								Type: DiffTypeDeleted,
  1539  								Name: "LTarget",
  1540  								Old:  "bar",
  1541  								New:  "",
  1542  							},
  1543  							{
  1544  								Type: DiffTypeDeleted,
  1545  								Name: "Operand",
  1546  								Old:  "bar",
  1547  								New:  "",
  1548  							},
  1549  							{
  1550  								Type: DiffTypeDeleted,
  1551  								Name: "RTarget",
  1552  								Old:  "bar",
  1553  								New:  "",
  1554  							},
  1555  						},
  1556  					},
  1557  				},
  1558  			},
  1559  		},
  1560  		{
  1561  			TestCase: "Affinities edited",
  1562  			Old: &TaskGroup{
  1563  				Affinities: []*Affinity{
  1564  					{
  1565  						LTarget: "foo",
  1566  						RTarget: "foo",
  1567  						Operand: "foo",
  1568  						Weight:  20,
  1569  						str:     "foo",
  1570  					},
  1571  					{
  1572  						LTarget: "bar",
  1573  						RTarget: "bar",
  1574  						Operand: "bar",
  1575  						Weight:  20,
  1576  						str:     "bar",
  1577  					},
  1578  				},
  1579  			},
  1580  			New: &TaskGroup{
  1581  				Affinities: []*Affinity{
  1582  					{
  1583  						LTarget: "foo",
  1584  						RTarget: "foo",
  1585  						Operand: "foo",
  1586  						Weight:  20,
  1587  						str:     "foo",
  1588  					},
  1589  					{
  1590  						LTarget: "baz",
  1591  						RTarget: "baz",
  1592  						Operand: "baz",
  1593  						Weight:  20,
  1594  						str:     "baz",
  1595  					},
  1596  				},
  1597  			},
  1598  			Expected: &TaskGroupDiff{
  1599  				Type: DiffTypeEdited,
  1600  				Objects: []*ObjectDiff{
  1601  					{
  1602  						Type: DiffTypeAdded,
  1603  						Name: "Affinity",
  1604  						Fields: []*FieldDiff{
  1605  							{
  1606  								Type: DiffTypeAdded,
  1607  								Name: "LTarget",
  1608  								Old:  "",
  1609  								New:  "baz",
  1610  							},
  1611  							{
  1612  								Type: DiffTypeAdded,
  1613  								Name: "Operand",
  1614  								Old:  "",
  1615  								New:  "baz",
  1616  							},
  1617  							{
  1618  								Type: DiffTypeAdded,
  1619  								Name: "RTarget",
  1620  								Old:  "",
  1621  								New:  "baz",
  1622  							},
  1623  							{
  1624  								Type: DiffTypeAdded,
  1625  								Name: "Weight",
  1626  								Old:  "",
  1627  								New:  "20",
  1628  							},
  1629  						},
  1630  					},
  1631  					{
  1632  						Type: DiffTypeDeleted,
  1633  						Name: "Affinity",
  1634  						Fields: []*FieldDiff{
  1635  							{
  1636  								Type: DiffTypeDeleted,
  1637  								Name: "LTarget",
  1638  								Old:  "bar",
  1639  								New:  "",
  1640  							},
  1641  							{
  1642  								Type: DiffTypeDeleted,
  1643  								Name: "Operand",
  1644  								Old:  "bar",
  1645  								New:  "",
  1646  							},
  1647  							{
  1648  								Type: DiffTypeDeleted,
  1649  								Name: "RTarget",
  1650  								Old:  "bar",
  1651  								New:  "",
  1652  							},
  1653  							{
  1654  								Type: DiffTypeDeleted,
  1655  								Name: "Weight",
  1656  								Old:  "20",
  1657  								New:  "",
  1658  							},
  1659  						},
  1660  					},
  1661  				},
  1662  			},
  1663  		},
  1664  		{
  1665  			TestCase: "RestartPolicy added",
  1666  			Old:      &TaskGroup{},
  1667  			New: &TaskGroup{
  1668  				RestartPolicy: &RestartPolicy{
  1669  					Attempts: 1,
  1670  					Interval: 1 * time.Second,
  1671  					Delay:    1 * time.Second,
  1672  					Mode:     "fail",
  1673  				},
  1674  			},
  1675  			Expected: &TaskGroupDiff{
  1676  				Type: DiffTypeEdited,
  1677  				Objects: []*ObjectDiff{
  1678  					{
  1679  						Type: DiffTypeAdded,
  1680  						Name: "RestartPolicy",
  1681  						Fields: []*FieldDiff{
  1682  							{
  1683  								Type: DiffTypeAdded,
  1684  								Name: "Attempts",
  1685  								Old:  "",
  1686  								New:  "1",
  1687  							},
  1688  							{
  1689  								Type: DiffTypeAdded,
  1690  								Name: "Delay",
  1691  								Old:  "",
  1692  								New:  "1000000000",
  1693  							},
  1694  							{
  1695  								Type: DiffTypeAdded,
  1696  								Name: "Interval",
  1697  								Old:  "",
  1698  								New:  "1000000000",
  1699  							},
  1700  							{
  1701  								Type: DiffTypeAdded,
  1702  								Name: "Mode",
  1703  								Old:  "",
  1704  								New:  "fail",
  1705  							},
  1706  						},
  1707  					},
  1708  				},
  1709  			},
  1710  		},
  1711  		{
  1712  			TestCase: "RestartPolicy deleted",
  1713  			Old: &TaskGroup{
  1714  				RestartPolicy: &RestartPolicy{
  1715  					Attempts: 1,
  1716  					Interval: 1 * time.Second,
  1717  					Delay:    1 * time.Second,
  1718  					Mode:     "fail",
  1719  				},
  1720  			},
  1721  			New: &TaskGroup{},
  1722  			Expected: &TaskGroupDiff{
  1723  				Type: DiffTypeEdited,
  1724  				Objects: []*ObjectDiff{
  1725  					{
  1726  						Type: DiffTypeDeleted,
  1727  						Name: "RestartPolicy",
  1728  						Fields: []*FieldDiff{
  1729  							{
  1730  								Type: DiffTypeDeleted,
  1731  								Name: "Attempts",
  1732  								Old:  "1",
  1733  								New:  "",
  1734  							},
  1735  							{
  1736  								Type: DiffTypeDeleted,
  1737  								Name: "Delay",
  1738  								Old:  "1000000000",
  1739  								New:  "",
  1740  							},
  1741  							{
  1742  								Type: DiffTypeDeleted,
  1743  								Name: "Interval",
  1744  								Old:  "1000000000",
  1745  								New:  "",
  1746  							},
  1747  							{
  1748  								Type: DiffTypeDeleted,
  1749  								Name: "Mode",
  1750  								Old:  "fail",
  1751  								New:  "",
  1752  							},
  1753  						},
  1754  					},
  1755  				},
  1756  			},
  1757  		},
  1758  		{
  1759  			TestCase: "RestartPolicy edited",
  1760  			Old: &TaskGroup{
  1761  				RestartPolicy: &RestartPolicy{
  1762  					Attempts: 1,
  1763  					Interval: 1 * time.Second,
  1764  					Delay:    1 * time.Second,
  1765  					Mode:     "fail",
  1766  				},
  1767  			},
  1768  			New: &TaskGroup{
  1769  				RestartPolicy: &RestartPolicy{
  1770  					Attempts: 2,
  1771  					Interval: 2 * time.Second,
  1772  					Delay:    2 * time.Second,
  1773  					Mode:     "delay",
  1774  				},
  1775  			},
  1776  			Expected: &TaskGroupDiff{
  1777  				Type: DiffTypeEdited,
  1778  				Objects: []*ObjectDiff{
  1779  					{
  1780  						Type: DiffTypeEdited,
  1781  						Name: "RestartPolicy",
  1782  						Fields: []*FieldDiff{
  1783  							{
  1784  								Type: DiffTypeEdited,
  1785  								Name: "Attempts",
  1786  								Old:  "1",
  1787  								New:  "2",
  1788  							},
  1789  							{
  1790  								Type: DiffTypeEdited,
  1791  								Name: "Delay",
  1792  								Old:  "1000000000",
  1793  								New:  "2000000000",
  1794  							},
  1795  							{
  1796  								Type: DiffTypeEdited,
  1797  								Name: "Interval",
  1798  								Old:  "1000000000",
  1799  								New:  "2000000000",
  1800  							},
  1801  							{
  1802  								Type: DiffTypeEdited,
  1803  								Name: "Mode",
  1804  								Old:  "fail",
  1805  								New:  "delay",
  1806  							},
  1807  						},
  1808  					},
  1809  				},
  1810  			},
  1811  		},
  1812  		{
  1813  			TestCase:   "RestartPolicy edited with context",
  1814  			Contextual: true,
  1815  			Old: &TaskGroup{
  1816  				RestartPolicy: &RestartPolicy{
  1817  					Attempts: 1,
  1818  					Interval: 1 * time.Second,
  1819  					Delay:    1 * time.Second,
  1820  					Mode:     "fail",
  1821  				},
  1822  			},
  1823  			New: &TaskGroup{
  1824  				RestartPolicy: &RestartPolicy{
  1825  					Attempts: 2,
  1826  					Interval: 2 * time.Second,
  1827  					Delay:    1 * time.Second,
  1828  					Mode:     "fail",
  1829  				},
  1830  			},
  1831  			Expected: &TaskGroupDiff{
  1832  				Type: DiffTypeEdited,
  1833  				Objects: []*ObjectDiff{
  1834  					{
  1835  						Type: DiffTypeEdited,
  1836  						Name: "RestartPolicy",
  1837  						Fields: []*FieldDiff{
  1838  							{
  1839  								Type: DiffTypeEdited,
  1840  								Name: "Attempts",
  1841  								Old:  "1",
  1842  								New:  "2",
  1843  							},
  1844  							{
  1845  								Type: DiffTypeNone,
  1846  								Name: "Delay",
  1847  								Old:  "1000000000",
  1848  								New:  "1000000000",
  1849  							},
  1850  							{
  1851  								Type: DiffTypeEdited,
  1852  								Name: "Interval",
  1853  								Old:  "1000000000",
  1854  								New:  "2000000000",
  1855  							},
  1856  							{
  1857  								Type: DiffTypeNone,
  1858  								Name: "Mode",
  1859  								Old:  "fail",
  1860  								New:  "fail",
  1861  							},
  1862  						},
  1863  					},
  1864  				},
  1865  			},
  1866  		},
  1867  		{
  1868  			TestCase: "ReschedulePolicy added",
  1869  			Old:      &TaskGroup{},
  1870  			New: &TaskGroup{
  1871  				ReschedulePolicy: &ReschedulePolicy{
  1872  					Attempts:      1,
  1873  					Interval:      15 * time.Second,
  1874  					Delay:         5 * time.Second,
  1875  					MaxDelay:      20 * time.Second,
  1876  					DelayFunction: "exponential",
  1877  					Unlimited:     false,
  1878  				},
  1879  			},
  1880  			Expected: &TaskGroupDiff{
  1881  				Type: DiffTypeEdited,
  1882  				Objects: []*ObjectDiff{
  1883  					{
  1884  						Type: DiffTypeAdded,
  1885  						Name: "ReschedulePolicy",
  1886  						Fields: []*FieldDiff{
  1887  							{
  1888  								Type: DiffTypeAdded,
  1889  								Name: "Attempts",
  1890  								Old:  "",
  1891  								New:  "1",
  1892  							},
  1893  							{
  1894  								Type: DiffTypeAdded,
  1895  								Name: "Delay",
  1896  								Old:  "",
  1897  								New:  "5000000000",
  1898  							},
  1899  							{
  1900  								Type: DiffTypeAdded,
  1901  								Name: "DelayFunction",
  1902  								Old:  "",
  1903  								New:  "exponential",
  1904  							},
  1905  							{
  1906  								Type: DiffTypeAdded,
  1907  								Name: "Interval",
  1908  								Old:  "",
  1909  								New:  "15000000000",
  1910  							},
  1911  							{
  1912  								Type: DiffTypeAdded,
  1913  								Name: "MaxDelay",
  1914  								Old:  "",
  1915  								New:  "20000000000",
  1916  							},
  1917  							{
  1918  								Type: DiffTypeAdded,
  1919  								Name: "Unlimited",
  1920  								Old:  "",
  1921  								New:  "false",
  1922  							},
  1923  						},
  1924  					},
  1925  				},
  1926  			},
  1927  		},
  1928  		{
  1929  			TestCase: "ReschedulePolicy deleted",
  1930  			Old: &TaskGroup{
  1931  				ReschedulePolicy: &ReschedulePolicy{
  1932  					Attempts:      1,
  1933  					Interval:      15 * time.Second,
  1934  					Delay:         5 * time.Second,
  1935  					MaxDelay:      20 * time.Second,
  1936  					DelayFunction: "exponential",
  1937  					Unlimited:     false,
  1938  				},
  1939  			},
  1940  			New: &TaskGroup{},
  1941  			Expected: &TaskGroupDiff{
  1942  				Type: DiffTypeEdited,
  1943  				Objects: []*ObjectDiff{
  1944  					{
  1945  						Type: DiffTypeDeleted,
  1946  						Name: "ReschedulePolicy",
  1947  						Fields: []*FieldDiff{
  1948  							{
  1949  								Type: DiffTypeDeleted,
  1950  								Name: "Attempts",
  1951  								Old:  "1",
  1952  								New:  "",
  1953  							},
  1954  							{
  1955  								Type: DiffTypeDeleted,
  1956  								Name: "Delay",
  1957  								Old:  "5000000000",
  1958  								New:  "",
  1959  							},
  1960  							{
  1961  								Type: DiffTypeDeleted,
  1962  								Name: "DelayFunction",
  1963  								Old:  "exponential",
  1964  								New:  "",
  1965  							},
  1966  							{
  1967  								Type: DiffTypeDeleted,
  1968  								Name: "Interval",
  1969  								Old:  "15000000000",
  1970  								New:  "",
  1971  							},
  1972  							{
  1973  								Type: DiffTypeDeleted,
  1974  								Name: "MaxDelay",
  1975  								Old:  "20000000000",
  1976  								New:  "",
  1977  							},
  1978  							{
  1979  								Type: DiffTypeDeleted,
  1980  								Name: "Unlimited",
  1981  								Old:  "false",
  1982  								New:  "",
  1983  							},
  1984  						},
  1985  					},
  1986  				},
  1987  			},
  1988  		},
  1989  		{
  1990  			TestCase: "ReschedulePolicy edited",
  1991  			Old: &TaskGroup{
  1992  				ReschedulePolicy: &ReschedulePolicy{
  1993  					Attempts:      1,
  1994  					Interval:      1 * time.Second,
  1995  					DelayFunction: "exponential",
  1996  					Delay:         20 * time.Second,
  1997  					MaxDelay:      1 * time.Minute,
  1998  					Unlimited:     false,
  1999  				},
  2000  			},
  2001  			New: &TaskGroup{
  2002  				ReschedulePolicy: &ReschedulePolicy{
  2003  					Attempts:      2,
  2004  					Interval:      2 * time.Second,
  2005  					DelayFunction: "constant",
  2006  					Delay:         30 * time.Second,
  2007  					MaxDelay:      1 * time.Minute,
  2008  					Unlimited:     true,
  2009  				},
  2010  			},
  2011  			Expected: &TaskGroupDiff{
  2012  				Type: DiffTypeEdited,
  2013  				Objects: []*ObjectDiff{
  2014  					{
  2015  						Type: DiffTypeEdited,
  2016  						Name: "ReschedulePolicy",
  2017  						Fields: []*FieldDiff{
  2018  							{
  2019  								Type: DiffTypeEdited,
  2020  								Name: "Attempts",
  2021  								Old:  "1",
  2022  								New:  "2",
  2023  							},
  2024  							{
  2025  								Type: DiffTypeEdited,
  2026  								Name: "Delay",
  2027  								Old:  "20000000000",
  2028  								New:  "30000000000",
  2029  							},
  2030  							{
  2031  								Type: DiffTypeEdited,
  2032  								Name: "DelayFunction",
  2033  								Old:  "exponential",
  2034  								New:  "constant",
  2035  							},
  2036  							{
  2037  								Type: DiffTypeEdited,
  2038  								Name: "Interval",
  2039  								Old:  "1000000000",
  2040  								New:  "2000000000",
  2041  							},
  2042  							{
  2043  								Type: DiffTypeEdited,
  2044  								Name: "Unlimited",
  2045  								Old:  "false",
  2046  								New:  "true",
  2047  							},
  2048  						},
  2049  					},
  2050  				},
  2051  			},
  2052  		},
  2053  		{
  2054  			TestCase:   "ReschedulePolicy edited with context",
  2055  			Contextual: true,
  2056  			Old: &TaskGroup{
  2057  				ReschedulePolicy: &ReschedulePolicy{
  2058  					Attempts: 1,
  2059  					Interval: 1 * time.Second,
  2060  				},
  2061  			},
  2062  			New: &TaskGroup{
  2063  				ReschedulePolicy: &ReschedulePolicy{
  2064  					Attempts: 1,
  2065  					Interval: 2 * time.Second,
  2066  				},
  2067  			},
  2068  			Expected: &TaskGroupDiff{
  2069  				Type: DiffTypeEdited,
  2070  				Objects: []*ObjectDiff{
  2071  					{
  2072  						Type: DiffTypeEdited,
  2073  						Name: "ReschedulePolicy",
  2074  						Fields: []*FieldDiff{
  2075  							{
  2076  								Type: DiffTypeNone,
  2077  								Name: "Attempts",
  2078  								Old:  "1",
  2079  								New:  "1",
  2080  							},
  2081  							{
  2082  								Type: DiffTypeNone,
  2083  								Name: "Delay",
  2084  								Old:  "0",
  2085  								New:  "0",
  2086  							},
  2087  							{
  2088  								Type: DiffTypeNone,
  2089  								Name: "DelayFunction",
  2090  								Old:  "",
  2091  								New:  "",
  2092  							},
  2093  							{
  2094  								Type: DiffTypeEdited,
  2095  								Name: "Interval",
  2096  								Old:  "1000000000",
  2097  								New:  "2000000000",
  2098  							},
  2099  							{
  2100  								Type: DiffTypeNone,
  2101  								Name: "MaxDelay",
  2102  								Old:  "0",
  2103  								New:  "0",
  2104  							},
  2105  							{
  2106  								Type: DiffTypeNone,
  2107  								Name: "Unlimited",
  2108  								Old:  "false",
  2109  								New:  "false",
  2110  							},
  2111  						},
  2112  					},
  2113  				},
  2114  			},
  2115  		},
  2116  		{
  2117  			TestCase: "Update strategy deleted",
  2118  			Old: &TaskGroup{
  2119  				Update: &UpdateStrategy{
  2120  					AutoRevert: true,
  2121  				},
  2122  			},
  2123  			New: &TaskGroup{},
  2124  			Expected: &TaskGroupDiff{
  2125  				Type: DiffTypeEdited,
  2126  				Objects: []*ObjectDiff{
  2127  					{
  2128  						Type: DiffTypeDeleted,
  2129  						Name: "Update",
  2130  						Fields: []*FieldDiff{
  2131  							{
  2132  								Type: DiffTypeDeleted,
  2133  								Name: "AutoPromote",
  2134  								Old:  "false",
  2135  								New:  "",
  2136  							},
  2137  							{
  2138  								Type: DiffTypeDeleted,
  2139  								Name: "AutoRevert",
  2140  								Old:  "true",
  2141  								New:  "",
  2142  							},
  2143  							{
  2144  								Type: DiffTypeDeleted,
  2145  								Name: "Canary",
  2146  								Old:  "0",
  2147  								New:  "",
  2148  							},
  2149  							{
  2150  								Type: DiffTypeDeleted,
  2151  								Name: "HealthyDeadline",
  2152  								Old:  "0",
  2153  								New:  "",
  2154  							},
  2155  							{
  2156  								Type: DiffTypeDeleted,
  2157  								Name: "MaxParallel",
  2158  								Old:  "0",
  2159  								New:  "",
  2160  							},
  2161  							{
  2162  								Type: DiffTypeDeleted,
  2163  								Name: "MinHealthyTime",
  2164  								Old:  "0",
  2165  								New:  "",
  2166  							},
  2167  							{
  2168  								Type: DiffTypeDeleted,
  2169  								Name: "ProgressDeadline",
  2170  								Old:  "0",
  2171  								New:  "",
  2172  							},
  2173  						},
  2174  					},
  2175  				},
  2176  			},
  2177  		},
  2178  		{
  2179  			TestCase: "Update strategy added",
  2180  			Old:      &TaskGroup{},
  2181  			New: &TaskGroup{
  2182  				Update: &UpdateStrategy{
  2183  					AutoRevert: true,
  2184  				},
  2185  			},
  2186  			Expected: &TaskGroupDiff{
  2187  				Type: DiffTypeEdited,
  2188  				Objects: []*ObjectDiff{
  2189  					{
  2190  						Type: DiffTypeAdded,
  2191  						Name: "Update",
  2192  						Fields: []*FieldDiff{
  2193  							{
  2194  								Type: DiffTypeAdded,
  2195  								Name: "AutoPromote",
  2196  								Old:  "",
  2197  								New:  "false",
  2198  							},
  2199  							{
  2200  								Type: DiffTypeAdded,
  2201  								Name: "AutoRevert",
  2202  								Old:  "",
  2203  								New:  "true",
  2204  							},
  2205  							{
  2206  								Type: DiffTypeAdded,
  2207  								Name: "Canary",
  2208  								Old:  "",
  2209  								New:  "0",
  2210  							},
  2211  							{
  2212  								Type: DiffTypeAdded,
  2213  								Name: "HealthyDeadline",
  2214  								Old:  "",
  2215  								New:  "0",
  2216  							},
  2217  							{
  2218  								Type: DiffTypeAdded,
  2219  								Name: "MaxParallel",
  2220  								Old:  "",
  2221  								New:  "0",
  2222  							},
  2223  							{
  2224  								Type: DiffTypeAdded,
  2225  								Name: "MinHealthyTime",
  2226  								Old:  "",
  2227  								New:  "0",
  2228  							},
  2229  							{
  2230  								Type: DiffTypeAdded,
  2231  								Name: "ProgressDeadline",
  2232  								Old:  "",
  2233  								New:  "0",
  2234  							},
  2235  						},
  2236  					},
  2237  				},
  2238  			},
  2239  		},
  2240  		{
  2241  			TestCase: "Update strategy edited",
  2242  			Old: &TaskGroup{
  2243  				Update: &UpdateStrategy{
  2244  					MaxParallel:      5,
  2245  					HealthCheck:      "foo",
  2246  					MinHealthyTime:   1 * time.Second,
  2247  					HealthyDeadline:  30 * time.Second,
  2248  					ProgressDeadline: 29 * time.Second,
  2249  					AutoRevert:       true,
  2250  					AutoPromote:      true,
  2251  					Canary:           2,
  2252  				},
  2253  			},
  2254  			New: &TaskGroup{
  2255  				Update: &UpdateStrategy{
  2256  					MaxParallel:      7,
  2257  					HealthCheck:      "bar",
  2258  					MinHealthyTime:   2 * time.Second,
  2259  					HealthyDeadline:  31 * time.Second,
  2260  					ProgressDeadline: 32 * time.Second,
  2261  					AutoRevert:       false,
  2262  					AutoPromote:      false,
  2263  					Canary:           1,
  2264  				},
  2265  			},
  2266  			Expected: &TaskGroupDiff{
  2267  				Type: DiffTypeEdited,
  2268  				Objects: []*ObjectDiff{
  2269  					{
  2270  						Type: DiffTypeEdited,
  2271  						Name: "Update",
  2272  						Fields: []*FieldDiff{
  2273  							{
  2274  								Type: DiffTypeEdited,
  2275  								Name: "AutoPromote",
  2276  								Old:  "true",
  2277  								New:  "false",
  2278  							},
  2279  							{
  2280  								Type: DiffTypeEdited,
  2281  								Name: "AutoRevert",
  2282  								Old:  "true",
  2283  								New:  "false",
  2284  							},
  2285  							{
  2286  								Type: DiffTypeEdited,
  2287  								Name: "Canary",
  2288  								Old:  "2",
  2289  								New:  "1",
  2290  							},
  2291  							{
  2292  								Type: DiffTypeEdited,
  2293  								Name: "HealthCheck",
  2294  								Old:  "foo",
  2295  								New:  "bar",
  2296  							},
  2297  							{
  2298  								Type: DiffTypeEdited,
  2299  								Name: "HealthyDeadline",
  2300  								Old:  "30000000000",
  2301  								New:  "31000000000",
  2302  							},
  2303  							{
  2304  								Type: DiffTypeEdited,
  2305  								Name: "MaxParallel",
  2306  								Old:  "5",
  2307  								New:  "7",
  2308  							},
  2309  							{
  2310  								Type: DiffTypeEdited,
  2311  								Name: "MinHealthyTime",
  2312  								Old:  "1000000000",
  2313  								New:  "2000000000",
  2314  							},
  2315  							{
  2316  								Type: DiffTypeEdited,
  2317  								Name: "ProgressDeadline",
  2318  								Old:  "29000000000",
  2319  								New:  "32000000000",
  2320  							},
  2321  						},
  2322  					},
  2323  				},
  2324  			},
  2325  		},
  2326  		{
  2327  			TestCase:   "Update strategy edited with context",
  2328  			Contextual: true,
  2329  			Old: &TaskGroup{
  2330  				Update: &UpdateStrategy{
  2331  					MaxParallel:      5,
  2332  					HealthCheck:      "foo",
  2333  					MinHealthyTime:   1 * time.Second,
  2334  					HealthyDeadline:  30 * time.Second,
  2335  					ProgressDeadline: 30 * time.Second,
  2336  					AutoRevert:       true,
  2337  					AutoPromote:      true,
  2338  					Canary:           2,
  2339  				},
  2340  			},
  2341  			New: &TaskGroup{
  2342  				Update: &UpdateStrategy{
  2343  					MaxParallel:      7,
  2344  					HealthCheck:      "foo",
  2345  					MinHealthyTime:   1 * time.Second,
  2346  					HealthyDeadline:  30 * time.Second,
  2347  					ProgressDeadline: 30 * time.Second,
  2348  					AutoRevert:       true,
  2349  					AutoPromote:      true,
  2350  					Canary:           2,
  2351  				},
  2352  			},
  2353  			Expected: &TaskGroupDiff{
  2354  				Type: DiffTypeEdited,
  2355  				Objects: []*ObjectDiff{
  2356  					{
  2357  						Type: DiffTypeEdited,
  2358  						Name: "Update",
  2359  						Fields: []*FieldDiff{
  2360  							{
  2361  								Type: DiffTypeNone,
  2362  								Name: "AutoPromote",
  2363  								Old:  "true",
  2364  								New:  "true",
  2365  							},
  2366  							{
  2367  								Type: DiffTypeNone,
  2368  								Name: "AutoRevert",
  2369  								Old:  "true",
  2370  								New:  "true",
  2371  							},
  2372  							{
  2373  								Type: DiffTypeNone,
  2374  								Name: "Canary",
  2375  								Old:  "2",
  2376  								New:  "2",
  2377  							},
  2378  							{
  2379  								Type: DiffTypeNone,
  2380  								Name: "HealthCheck",
  2381  								Old:  "foo",
  2382  								New:  "foo",
  2383  							},
  2384  							{
  2385  								Type: DiffTypeNone,
  2386  								Name: "HealthyDeadline",
  2387  								Old:  "30000000000",
  2388  								New:  "30000000000",
  2389  							},
  2390  							{
  2391  								Type: DiffTypeEdited,
  2392  								Name: "MaxParallel",
  2393  								Old:  "5",
  2394  								New:  "7",
  2395  							},
  2396  							{
  2397  								Type: DiffTypeNone,
  2398  								Name: "MinHealthyTime",
  2399  								Old:  "1000000000",
  2400  								New:  "1000000000",
  2401  							},
  2402  							{
  2403  								Type: DiffTypeNone,
  2404  								Name: "ProgressDeadline",
  2405  								Old:  "30000000000",
  2406  								New:  "30000000000",
  2407  							},
  2408  						},
  2409  					},
  2410  				},
  2411  			},
  2412  		},
  2413  		{
  2414  			TestCase: "EphemeralDisk added",
  2415  			Old:      &TaskGroup{},
  2416  			New: &TaskGroup{
  2417  				EphemeralDisk: &EphemeralDisk{
  2418  					Migrate: true,
  2419  					Sticky:  true,
  2420  					SizeMB:  100,
  2421  				},
  2422  			},
  2423  			Expected: &TaskGroupDiff{
  2424  				Type: DiffTypeEdited,
  2425  				Objects: []*ObjectDiff{
  2426  					{
  2427  						Type: DiffTypeAdded,
  2428  						Name: "EphemeralDisk",
  2429  						Fields: []*FieldDiff{
  2430  							{
  2431  								Type: DiffTypeAdded,
  2432  								Name: "Migrate",
  2433  								Old:  "",
  2434  								New:  "true",
  2435  							},
  2436  							{
  2437  								Type: DiffTypeAdded,
  2438  								Name: "SizeMB",
  2439  								Old:  "",
  2440  								New:  "100",
  2441  							},
  2442  							{
  2443  								Type: DiffTypeAdded,
  2444  								Name: "Sticky",
  2445  								Old:  "",
  2446  								New:  "true",
  2447  							},
  2448  						},
  2449  					},
  2450  				},
  2451  			},
  2452  		},
  2453  		{
  2454  			TestCase: "EphemeralDisk deleted",
  2455  			Old: &TaskGroup{
  2456  				EphemeralDisk: &EphemeralDisk{
  2457  					Migrate: true,
  2458  					Sticky:  true,
  2459  					SizeMB:  100,
  2460  				},
  2461  			},
  2462  			New: &TaskGroup{},
  2463  			Expected: &TaskGroupDiff{
  2464  				Type: DiffTypeEdited,
  2465  				Objects: []*ObjectDiff{
  2466  					{
  2467  						Type: DiffTypeDeleted,
  2468  						Name: "EphemeralDisk",
  2469  						Fields: []*FieldDiff{
  2470  							{
  2471  								Type: DiffTypeDeleted,
  2472  								Name: "Migrate",
  2473  								Old:  "true",
  2474  								New:  "",
  2475  							},
  2476  							{
  2477  								Type: DiffTypeDeleted,
  2478  								Name: "SizeMB",
  2479  								Old:  "100",
  2480  								New:  "",
  2481  							},
  2482  							{
  2483  								Type: DiffTypeDeleted,
  2484  								Name: "Sticky",
  2485  								Old:  "true",
  2486  								New:  "",
  2487  							},
  2488  						},
  2489  					},
  2490  				},
  2491  			},
  2492  		},
  2493  		{
  2494  			TestCase: "EphemeralDisk edited",
  2495  			Old: &TaskGroup{
  2496  				EphemeralDisk: &EphemeralDisk{
  2497  					Migrate: true,
  2498  					Sticky:  true,
  2499  					SizeMB:  150,
  2500  				},
  2501  			},
  2502  			New: &TaskGroup{
  2503  				EphemeralDisk: &EphemeralDisk{
  2504  					Migrate: false,
  2505  					Sticky:  false,
  2506  					SizeMB:  90,
  2507  				},
  2508  			},
  2509  			Expected: &TaskGroupDiff{
  2510  				Type: DiffTypeEdited,
  2511  				Objects: []*ObjectDiff{
  2512  					{
  2513  						Type: DiffTypeEdited,
  2514  						Name: "EphemeralDisk",
  2515  						Fields: []*FieldDiff{
  2516  							{
  2517  								Type: DiffTypeEdited,
  2518  								Name: "Migrate",
  2519  								Old:  "true",
  2520  								New:  "false",
  2521  							},
  2522  							{
  2523  								Type: DiffTypeEdited,
  2524  								Name: "SizeMB",
  2525  								Old:  "150",
  2526  								New:  "90",
  2527  							},
  2528  
  2529  							{
  2530  								Type: DiffTypeEdited,
  2531  								Name: "Sticky",
  2532  								Old:  "true",
  2533  								New:  "false",
  2534  							},
  2535  						},
  2536  					},
  2537  				},
  2538  			},
  2539  		},
  2540  		{
  2541  			TestCase:   "EphemeralDisk edited with context",
  2542  			Contextual: true,
  2543  			Old: &TaskGroup{
  2544  				EphemeralDisk: &EphemeralDisk{
  2545  					Migrate: false,
  2546  					Sticky:  false,
  2547  					SizeMB:  100,
  2548  				},
  2549  			},
  2550  			New: &TaskGroup{
  2551  				EphemeralDisk: &EphemeralDisk{
  2552  					Migrate: true,
  2553  					Sticky:  true,
  2554  					SizeMB:  90,
  2555  				},
  2556  			},
  2557  			Expected: &TaskGroupDiff{
  2558  				Type: DiffTypeEdited,
  2559  				Objects: []*ObjectDiff{
  2560  					{
  2561  						Type: DiffTypeEdited,
  2562  						Name: "EphemeralDisk",
  2563  						Fields: []*FieldDiff{
  2564  							{
  2565  								Type: DiffTypeEdited,
  2566  								Name: "Migrate",
  2567  								Old:  "false",
  2568  								New:  "true",
  2569  							},
  2570  							{
  2571  								Type: DiffTypeEdited,
  2572  								Name: "SizeMB",
  2573  								Old:  "100",
  2574  								New:  "90",
  2575  							},
  2576  							{
  2577  								Type: DiffTypeEdited,
  2578  								Name: "Sticky",
  2579  								Old:  "false",
  2580  								New:  "true",
  2581  							},
  2582  						},
  2583  					},
  2584  				},
  2585  			},
  2586  		},
  2587  		{
  2588  			TestCase:   "TaskGroup Services edited",
  2589  			Contextual: true,
  2590  			Old: &TaskGroup{
  2591  				Services: []*Service{
  2592  					{
  2593  						Name:              "foo",
  2594  						TaskName:          "task1",
  2595  						EnableTagOverride: false,
  2596  						Checks: []*ServiceCheck{
  2597  							{
  2598  								Name:                   "foo",
  2599  								Type:                   "http",
  2600  								Command:                "foo",
  2601  								Args:                   []string{"foo"},
  2602  								Path:                   "foo",
  2603  								Protocol:               "http",
  2604  								Expose:                 true,
  2605  								Interval:               1 * time.Second,
  2606  								Timeout:                1 * time.Second,
  2607  								SuccessBeforePassing:   3,
  2608  								FailuresBeforeCritical: 4,
  2609  							},
  2610  						},
  2611  						Connect: &ConsulConnect{
  2612  							Native: false,
  2613  							SidecarTask: &SidecarTask{
  2614  								Name:   "sidecar",
  2615  								Driver: "docker",
  2616  								Env: map[string]string{
  2617  									"FOO": "BAR",
  2618  								},
  2619  								Config: map[string]interface{}{
  2620  									"foo": "baz",
  2621  								},
  2622  							},
  2623  							Gateway: &ConsulGateway{
  2624  								Proxy: &ConsulGatewayProxy{
  2625  									ConnectTimeout:                  helper.TimeToPtr(1 * time.Second),
  2626  									EnvoyGatewayBindTaggedAddresses: false,
  2627  									EnvoyGatewayBindAddresses: map[string]*ConsulGatewayBindAddress{
  2628  										"service1": {
  2629  											Address: "10.0.0.1",
  2630  											Port:    2001,
  2631  										},
  2632  									},
  2633  									EnvoyDNSDiscoveryType:     "STRICT_DNS",
  2634  									EnvoyGatewayNoDefaultBind: false,
  2635  									Config: map[string]interface{}{
  2636  										"foo": 1,
  2637  									},
  2638  								},
  2639  								Ingress: &ConsulIngressConfigEntry{
  2640  									TLS: &ConsulGatewayTLSConfig{
  2641  										Enabled: false,
  2642  									},
  2643  									Listeners: []*ConsulIngressListener{{
  2644  										Port:     3001,
  2645  										Protocol: "tcp",
  2646  										Services: []*ConsulIngressService{{
  2647  											Name: "listener1",
  2648  										}},
  2649  									}},
  2650  								},
  2651  								Terminating: &ConsulTerminatingConfigEntry{
  2652  									Services: []*ConsulLinkedService{{
  2653  										Name:     "linked1",
  2654  										CAFile:   "ca1.pem",
  2655  										CertFile: "cert1.pem",
  2656  										KeyFile:  "key1.pem",
  2657  										SNI:      "linked1.consul",
  2658  									}},
  2659  								},
  2660  							},
  2661  						},
  2662  					},
  2663  				},
  2664  			},
  2665  
  2666  			New: &TaskGroup{
  2667  				Services: []*Service{
  2668  					{
  2669  						Name:              "foo",
  2670  						TaskName:          "task2",
  2671  						EnableTagOverride: true,
  2672  						Checks: []*ServiceCheck{
  2673  							{
  2674  								Name:     "foo",
  2675  								Type:     "tcp",
  2676  								Command:  "bar",
  2677  								Path:     "bar",
  2678  								Protocol: "tcp",
  2679  								Expose:   false,
  2680  								Interval: 2 * time.Second,
  2681  								Timeout:  2 * time.Second,
  2682  								Header: map[string][]string{
  2683  									"Foo": {"baz"},
  2684  								},
  2685  								SuccessBeforePassing:   5,
  2686  								FailuresBeforeCritical: 6,
  2687  							},
  2688  						},
  2689  						Connect: &ConsulConnect{
  2690  							Native: true,
  2691  							SidecarService: &ConsulSidecarService{
  2692  								Port: "http",
  2693  								Proxy: &ConsulProxy{
  2694  									LocalServiceAddress: "127.0.0.1",
  2695  									LocalServicePort:    8080,
  2696  									Upstreams: []ConsulUpstream{
  2697  										{
  2698  											DestinationName:  "foo",
  2699  											LocalBindPort:    8000,
  2700  											Datacenter:       "dc2",
  2701  											LocalBindAddress: "127.0.0.2",
  2702  										},
  2703  									},
  2704  									Config: map[string]interface{}{
  2705  										"foo": "qux",
  2706  									},
  2707  								},
  2708  							},
  2709  							Gateway: &ConsulGateway{
  2710  								Proxy: &ConsulGatewayProxy{
  2711  									ConnectTimeout:                  helper.TimeToPtr(2 * time.Second),
  2712  									EnvoyGatewayBindTaggedAddresses: true,
  2713  									EnvoyGatewayBindAddresses: map[string]*ConsulGatewayBindAddress{
  2714  										"service1": {
  2715  											Address: "10.0.0.2",
  2716  											Port:    2002,
  2717  										},
  2718  									},
  2719  									EnvoyDNSDiscoveryType:     "LOGICAL_DNS",
  2720  									EnvoyGatewayNoDefaultBind: true,
  2721  									Config: map[string]interface{}{
  2722  										"foo": 2,
  2723  									},
  2724  								},
  2725  								Ingress: &ConsulIngressConfigEntry{
  2726  									TLS: &ConsulGatewayTLSConfig{
  2727  										Enabled: true,
  2728  									},
  2729  									Listeners: []*ConsulIngressListener{{
  2730  										Port:     3002,
  2731  										Protocol: "http",
  2732  										Services: []*ConsulIngressService{{
  2733  											Name:  "listener2",
  2734  											Hosts: []string{"127.0.0.1", "127.0.0.1:3002"},
  2735  										}},
  2736  									}},
  2737  								},
  2738  								Terminating: &ConsulTerminatingConfigEntry{
  2739  									Services: []*ConsulLinkedService{{
  2740  										Name:     "linked2",
  2741  										CAFile:   "ca2.pem",
  2742  										CertFile: "cert2.pem",
  2743  										KeyFile:  "key2.pem",
  2744  										SNI:      "linked2.consul",
  2745  									}},
  2746  								},
  2747  							},
  2748  						},
  2749  					},
  2750  				},
  2751  			},
  2752  
  2753  			Expected: &TaskGroupDiff{
  2754  				Type: DiffTypeEdited,
  2755  				Objects: []*ObjectDiff{
  2756  					{
  2757  						Type: DiffTypeEdited,
  2758  						Name: "Service",
  2759  						Fields: []*FieldDiff{
  2760  							{
  2761  								Type: DiffTypeNone,
  2762  								Name: "AddressMode",
  2763  								Old:  "",
  2764  								New:  "",
  2765  							},
  2766  							{
  2767  								Type: DiffTypeEdited,
  2768  								Name: "EnableTagOverride",
  2769  								Old:  "false",
  2770  								New:  "true",
  2771  							},
  2772  							{
  2773  								Type: DiffTypeNone,
  2774  								Name: "Name",
  2775  								Old:  "foo",
  2776  								New:  "foo",
  2777  							},
  2778  							{
  2779  								Type: DiffTypeNone,
  2780  								Name: "OnUpdate",
  2781  								Old:  "",
  2782  								New:  "",
  2783  							},
  2784  							{
  2785  								Type: DiffTypeNone,
  2786  								Name: "PortLabel",
  2787  								Old:  "",
  2788  								New:  "",
  2789  							},
  2790  							{
  2791  								Type: DiffTypeEdited,
  2792  								Name: "TaskName",
  2793  								Old:  "task1",
  2794  								New:  "task2",
  2795  							},
  2796  						},
  2797  						Objects: []*ObjectDiff{
  2798  							{
  2799  								Type: DiffTypeEdited,
  2800  								Name: "Check",
  2801  								Fields: []*FieldDiff{
  2802  									{
  2803  										Type: DiffTypeNone,
  2804  										Name: "AddressMode",
  2805  										Old:  "",
  2806  										New:  "",
  2807  									},
  2808  									{
  2809  										Type: DiffTypeEdited,
  2810  										Name: "Command",
  2811  										Old:  "foo",
  2812  										New:  "bar",
  2813  									},
  2814  									{
  2815  										Type: DiffTypeEdited,
  2816  										Name: "Expose",
  2817  										Old:  "true",
  2818  										New:  "false",
  2819  									},
  2820  									{
  2821  										Type: DiffTypeEdited,
  2822  										Name: "FailuresBeforeCritical",
  2823  										Old:  "4",
  2824  										New:  "6",
  2825  									},
  2826  									{
  2827  										Type: DiffTypeNone,
  2828  										Name: "GRPCService",
  2829  										Old:  "",
  2830  										New:  "",
  2831  									},
  2832  									{
  2833  										Type: DiffTypeNone,
  2834  										Name: "GRPCUseTLS",
  2835  										Old:  "false",
  2836  										New:  "false",
  2837  									},
  2838  									{
  2839  										Type: DiffTypeNone,
  2840  										Name: "InitialStatus",
  2841  										Old:  "",
  2842  										New:  "",
  2843  									},
  2844  									{
  2845  										Type: DiffTypeEdited,
  2846  										Name: "Interval",
  2847  										Old:  "1000000000",
  2848  										New:  "2000000000",
  2849  									},
  2850  									{
  2851  										Type: DiffTypeNone,
  2852  										Name: "Method",
  2853  										Old:  "",
  2854  										New:  "",
  2855  									},
  2856  									{
  2857  										Type: DiffTypeNone,
  2858  										Name: "Name",
  2859  										Old:  "foo",
  2860  										New:  "foo",
  2861  									},
  2862  									{
  2863  										Type: DiffTypeNone,
  2864  										Name: "OnUpdate",
  2865  										Old:  "",
  2866  										New:  "",
  2867  									},
  2868  									{
  2869  										Type: DiffTypeEdited,
  2870  										Name: "Path",
  2871  										Old:  "foo",
  2872  										New:  "bar",
  2873  									},
  2874  									{
  2875  										Type: DiffTypeNone,
  2876  										Name: "PortLabel",
  2877  										Old:  "",
  2878  										New:  "",
  2879  									},
  2880  									{
  2881  										Type: DiffTypeEdited,
  2882  										Name: "Protocol",
  2883  										Old:  "http",
  2884  										New:  "tcp",
  2885  									},
  2886  									{
  2887  										Type: DiffTypeEdited,
  2888  										Name: "SuccessBeforePassing",
  2889  										Old:  "3",
  2890  										New:  "5",
  2891  									},
  2892  									{
  2893  										Type: DiffTypeNone,
  2894  										Name: "TLSSkipVerify",
  2895  										Old:  "false",
  2896  										New:  "false",
  2897  									},
  2898  									{
  2899  										Type: DiffTypeNone,
  2900  										Name: "TaskName",
  2901  										Old:  "",
  2902  										New:  "",
  2903  									},
  2904  									{
  2905  										Type: DiffTypeEdited,
  2906  										Name: "Timeout",
  2907  										Old:  "1000000000",
  2908  										New:  "2000000000",
  2909  									},
  2910  									{
  2911  										Type: DiffTypeEdited,
  2912  										Name: "Type",
  2913  										Old:  "http",
  2914  										New:  "tcp",
  2915  									},
  2916  								},
  2917  								Objects: []*ObjectDiff{
  2918  									{
  2919  										Type: DiffTypeAdded,
  2920  										Name: "Header",
  2921  										Fields: []*FieldDiff{
  2922  											{
  2923  												Type: DiffTypeAdded,
  2924  												Name: "Foo[0]",
  2925  												Old:  "",
  2926  												New:  "baz",
  2927  											},
  2928  										},
  2929  									},
  2930  								},
  2931  							},
  2932  							{
  2933  								Type: DiffTypeEdited,
  2934  								Name: "ConsulConnect",
  2935  								Fields: []*FieldDiff{
  2936  									{
  2937  										Type: DiffTypeEdited,
  2938  										Name: "Native",
  2939  										Old:  "false",
  2940  										New:  "true",
  2941  									},
  2942  								},
  2943  								Objects: []*ObjectDiff{
  2944  
  2945  									{
  2946  										Type: DiffTypeAdded,
  2947  										Name: "SidecarService",
  2948  										Fields: []*FieldDiff{
  2949  											{
  2950  												Type: DiffTypeAdded,
  2951  												Name: "Port",
  2952  												Old:  "",
  2953  												New:  "http",
  2954  											},
  2955  										},
  2956  										Objects: []*ObjectDiff{
  2957  											{
  2958  												Type: DiffTypeAdded,
  2959  												Name: "ConsulProxy",
  2960  												Fields: []*FieldDiff{
  2961  													{
  2962  														Type: DiffTypeAdded,
  2963  														Name: "LocalServiceAddress",
  2964  														Old:  "",
  2965  														New:  "127.0.0.1",
  2966  													}, {
  2967  														Type: DiffTypeAdded,
  2968  														Name: "LocalServicePort",
  2969  														Old:  "",
  2970  														New:  "8080",
  2971  													},
  2972  												},
  2973  												Objects: []*ObjectDiff{
  2974  													{
  2975  														Type: DiffTypeAdded,
  2976  														Name: "ConsulUpstreams",
  2977  														Fields: []*FieldDiff{
  2978  															{
  2979  																Type: DiffTypeAdded,
  2980  																Name: "Datacenter",
  2981  																Old:  "",
  2982  																New:  "dc2",
  2983  															},
  2984  															{
  2985  																Type: DiffTypeAdded,
  2986  																Name: "DestinationName",
  2987  																Old:  "",
  2988  																New:  "foo",
  2989  															},
  2990  															{
  2991  																Type: DiffTypeAdded,
  2992  																Name: "LocalBindAddress",
  2993  																Old:  "",
  2994  																New:  "127.0.0.2",
  2995  															},
  2996  															{
  2997  																Type: DiffTypeAdded,
  2998  																Name: "LocalBindPort",
  2999  																Old:  "",
  3000  																New:  "8000",
  3001  															},
  3002  														},
  3003  													},
  3004  													{
  3005  														Type: DiffTypeAdded,
  3006  														Name: "Config",
  3007  														Fields: []*FieldDiff{
  3008  															{
  3009  																Type: DiffTypeAdded,
  3010  																Name: "foo",
  3011  																Old:  "",
  3012  																New:  "qux",
  3013  															},
  3014  														},
  3015  													},
  3016  												},
  3017  											},
  3018  										},
  3019  									},
  3020  
  3021  									{
  3022  										Type: DiffTypeDeleted,
  3023  										Name: "SidecarTask",
  3024  										Fields: []*FieldDiff{
  3025  											{
  3026  												Type: DiffTypeDeleted,
  3027  												Name: "Driver",
  3028  												Old:  "docker",
  3029  												New:  "",
  3030  											},
  3031  											{
  3032  												Type: DiffTypeDeleted,
  3033  												Name: "Env[FOO]",
  3034  												Old:  "BAR",
  3035  												New:  "",
  3036  											},
  3037  											{
  3038  												Type: DiffTypeDeleted,
  3039  												Name: "Name",
  3040  												Old:  "sidecar",
  3041  												New:  "",
  3042  											},
  3043  										},
  3044  										Objects: []*ObjectDiff{
  3045  											{
  3046  												Type: DiffTypeDeleted,
  3047  												Name: "Config",
  3048  												Fields: []*FieldDiff{
  3049  													{
  3050  														Type: DiffTypeDeleted,
  3051  														Name: "foo",
  3052  														Old:  "baz",
  3053  														New:  "",
  3054  													},
  3055  												},
  3056  											},
  3057  										},
  3058  									},
  3059  									{
  3060  										Type: DiffTypeEdited,
  3061  										Name: "Gateway",
  3062  										Objects: []*ObjectDiff{
  3063  											{
  3064  												Type: DiffTypeEdited,
  3065  												Name: "Proxy",
  3066  												Fields: []*FieldDiff{
  3067  													{
  3068  														Type: DiffTypeEdited,
  3069  														Name: "ConnectTimeout",
  3070  														Old:  "1s",
  3071  														New:  "2s",
  3072  													},
  3073  													{
  3074  														Type: DiffTypeEdited,
  3075  														Name: "EnvoyDNSDiscoveryType",
  3076  														Old:  "STRICT_DNS",
  3077  														New:  "LOGICAL_DNS",
  3078  													},
  3079  													{
  3080  														Type: DiffTypeEdited,
  3081  														Name: "EnvoyGatewayBindTaggedAddresses",
  3082  														Old:  "false",
  3083  														New:  "true",
  3084  													},
  3085  													{
  3086  														Type: DiffTypeEdited,
  3087  														Name: "EnvoyGatewayNoDefaultBind",
  3088  														Old:  "false",
  3089  														New:  "true",
  3090  													},
  3091  												},
  3092  												Objects: []*ObjectDiff{
  3093  													{
  3094  														Type: DiffTypeEdited,
  3095  														Name: "EnvoyGatewayBindAddresses",
  3096  														Fields: []*FieldDiff{
  3097  															{
  3098  																Type: DiffTypeEdited,
  3099  																Name: "service1",
  3100  																Old:  "10.0.0.1:2001",
  3101  																New:  "10.0.0.2:2002",
  3102  															},
  3103  														},
  3104  													},
  3105  													{
  3106  														Type: DiffTypeEdited,
  3107  														Name: "Config",
  3108  														Fields: []*FieldDiff{
  3109  															{
  3110  																Type: DiffTypeEdited,
  3111  																Name: "foo",
  3112  																Old:  "1",
  3113  																New:  "2",
  3114  															},
  3115  														},
  3116  													},
  3117  												},
  3118  											},
  3119  											{
  3120  												Type: DiffTypeEdited,
  3121  												Name: "Ingress",
  3122  												Objects: []*ObjectDiff{
  3123  													{
  3124  														Type: DiffTypeEdited,
  3125  														Name: "TLS",
  3126  														Fields: []*FieldDiff{
  3127  															{
  3128  																Type: DiffTypeEdited,
  3129  																Name: "Enabled",
  3130  																Old:  "false",
  3131  																New:  "true",
  3132  															},
  3133  														},
  3134  													},
  3135  													{
  3136  														Type: DiffTypeAdded,
  3137  														Name: "Listener",
  3138  														Fields: []*FieldDiff{
  3139  															{
  3140  																Type: DiffTypeAdded,
  3141  																Name: "Port",
  3142  																Old:  "",
  3143  																New:  "3002",
  3144  															},
  3145  															{
  3146  																Type: DiffTypeAdded,
  3147  																Name: "Protocol",
  3148  																Old:  "",
  3149  																New:  "http",
  3150  															},
  3151  														},
  3152  														Objects: []*ObjectDiff{
  3153  															{
  3154  																Type: DiffTypeAdded,
  3155  																Name: "ConsulIngressService",
  3156  																Fields: []*FieldDiff{
  3157  																	{
  3158  																		Type: DiffTypeAdded,
  3159  																		Name: "Name",
  3160  																		Old:  "",
  3161  																		New:  "listener2",
  3162  																	},
  3163  																},
  3164  																Objects: []*ObjectDiff{
  3165  																	{
  3166  																		Type: DiffTypeAdded,
  3167  																		Name: "Hosts",
  3168  																		Fields: []*FieldDiff{
  3169  																			{
  3170  																				Type: DiffTypeAdded,
  3171  																				Name: "Hosts",
  3172  																				Old:  "",
  3173  																				New:  "127.0.0.1",
  3174  																			},
  3175  																			{
  3176  																				Type: DiffTypeAdded,
  3177  																				Name: "Hosts",
  3178  																				Old:  "",
  3179  																				New:  "127.0.0.1:3002",
  3180  																			},
  3181  																		},
  3182  																	},
  3183  																},
  3184  															},
  3185  														},
  3186  													},
  3187  													{
  3188  														Type: DiffTypeDeleted,
  3189  														Name: "Listener",
  3190  														Fields: []*FieldDiff{
  3191  															{
  3192  																Type: DiffTypeDeleted,
  3193  																Name: "Port",
  3194  																Old:  "3001",
  3195  																New:  "",
  3196  															},
  3197  															{
  3198  																Type: DiffTypeDeleted,
  3199  																Name: "Protocol",
  3200  																Old:  "tcp",
  3201  																New:  "",
  3202  															},
  3203  														},
  3204  														Objects: []*ObjectDiff{
  3205  															{
  3206  																Type: DiffTypeDeleted,
  3207  																Name: "ConsulIngressService",
  3208  																Fields: []*FieldDiff{
  3209  																	{
  3210  																		Type: DiffTypeDeleted,
  3211  																		Name: "Name",
  3212  																		Old:  "listener1",
  3213  																		New:  "",
  3214  																	},
  3215  																},
  3216  															},
  3217  														},
  3218  													},
  3219  												},
  3220  											},
  3221  											{
  3222  												Type: DiffTypeEdited,
  3223  												Name: "Terminating",
  3224  												Objects: []*ObjectDiff{
  3225  													{
  3226  														Type: DiffTypeAdded,
  3227  														Name: "Service",
  3228  														Fields: []*FieldDiff{
  3229  															{
  3230  																Type: DiffTypeAdded,
  3231  																Name: "CAFile",
  3232  																Old:  "",
  3233  																New:  "ca2.pem",
  3234  															},
  3235  															{
  3236  																Type: DiffTypeAdded,
  3237  																Name: "CertFile",
  3238  																Old:  "",
  3239  																New:  "cert2.pem",
  3240  															},
  3241  															{
  3242  																Type: DiffTypeAdded,
  3243  																Name: "KeyFile",
  3244  																Old:  "",
  3245  																New:  "key2.pem",
  3246  															},
  3247  															{
  3248  																Type: DiffTypeAdded,
  3249  																Name: "Name",
  3250  																Old:  "",
  3251  																New:  "linked2",
  3252  															},
  3253  															{
  3254  																Type: DiffTypeAdded,
  3255  																Name: "SNI",
  3256  																Old:  "",
  3257  																New:  "linked2.consul",
  3258  															},
  3259  														},
  3260  													},
  3261  													{
  3262  														Type: DiffTypeDeleted,
  3263  														Name: "Service",
  3264  														Fields: []*FieldDiff{
  3265  															{
  3266  																Type: DiffTypeDeleted,
  3267  																Name: "CAFile",
  3268  																Old:  "ca1.pem",
  3269  																New:  "",
  3270  															},
  3271  															{
  3272  																Type: DiffTypeDeleted,
  3273  																Name: "CertFile",
  3274  																Old:  "cert1.pem",
  3275  																New:  "",
  3276  															},
  3277  															{
  3278  																Type: DiffTypeDeleted,
  3279  																Name: "KeyFile",
  3280  																Old:  "key1.pem",
  3281  																New:  "",
  3282  															},
  3283  															{
  3284  																Type: DiffTypeDeleted,
  3285  																Name: "Name",
  3286  																Old:  "linked1",
  3287  																New:  "",
  3288  															},
  3289  															{
  3290  																Type: DiffTypeDeleted,
  3291  																Name: "SNI",
  3292  																Old:  "linked1.consul",
  3293  																New:  "",
  3294  															},
  3295  														},
  3296  													},
  3297  												},
  3298  											},
  3299  										},
  3300  									},
  3301  								},
  3302  							},
  3303  						},
  3304  					},
  3305  				},
  3306  			},
  3307  		},
  3308  		{
  3309  			TestCase:   "TaskGroup Networks edited",
  3310  			Contextual: true,
  3311  			Old: &TaskGroup{
  3312  				Networks: Networks{
  3313  					{
  3314  						Device: "foo",
  3315  						CIDR:   "foo",
  3316  						IP:     "foo",
  3317  						MBits:  100,
  3318  						ReservedPorts: []Port{
  3319  							{
  3320  								Label: "foo",
  3321  								Value: 80,
  3322  							},
  3323  						},
  3324  					},
  3325  				},
  3326  			},
  3327  			New: &TaskGroup{
  3328  				Networks: Networks{
  3329  					{
  3330  						Device: "bar",
  3331  						CIDR:   "bar",
  3332  						IP:     "bar",
  3333  						MBits:  200,
  3334  						DynamicPorts: []Port{
  3335  							{
  3336  								Label:       "bar",
  3337  								To:          8081,
  3338  								HostNetwork: "public",
  3339  							},
  3340  						},
  3341  						DNS: &DNSConfig{
  3342  							Servers: []string{"1.1.1.1"},
  3343  						},
  3344  					},
  3345  				},
  3346  			},
  3347  			Expected: &TaskGroupDiff{
  3348  				Type: DiffTypeEdited,
  3349  				Objects: []*ObjectDiff{
  3350  					{
  3351  						Type: DiffTypeAdded,
  3352  						Name: "Network",
  3353  						Fields: []*FieldDiff{
  3354  							{
  3355  								Type: DiffTypeAdded,
  3356  								Name: "MBits",
  3357  								Old:  "",
  3358  								New:  "200",
  3359  							},
  3360  							{
  3361  								Type: DiffTypeNone,
  3362  								Name: "Mode",
  3363  								Old:  "",
  3364  								New:  "",
  3365  							},
  3366  						},
  3367  						Objects: []*ObjectDiff{
  3368  							{
  3369  								Type: DiffTypeAdded,
  3370  								Name: "Dynamic Port",
  3371  								Fields: []*FieldDiff{
  3372  									{
  3373  										Type: DiffTypeAdded,
  3374  										Name: "HostNetwork",
  3375  										Old:  "",
  3376  										New:  "public",
  3377  									},
  3378  									{
  3379  										Type: DiffTypeAdded,
  3380  										Name: "Label",
  3381  										Old:  "",
  3382  										New:  "bar",
  3383  									},
  3384  									{
  3385  										Type: DiffTypeAdded,
  3386  										Name: "To",
  3387  										Old:  "",
  3388  										New:  "8081",
  3389  									},
  3390  								},
  3391  							},
  3392  							{
  3393  								Type: DiffTypeAdded,
  3394  								Name: "DNS",
  3395  								Fields: []*FieldDiff{
  3396  									{
  3397  										Type: DiffTypeAdded,
  3398  										Name: "Servers",
  3399  										Old:  "",
  3400  										New:  "1.1.1.1",
  3401  									},
  3402  								},
  3403  							},
  3404  						},
  3405  					},
  3406  					{
  3407  						Type: DiffTypeDeleted,
  3408  						Name: "Network",
  3409  						Fields: []*FieldDiff{
  3410  							{
  3411  								Type: DiffTypeDeleted,
  3412  								Name: "MBits",
  3413  								Old:  "100",
  3414  								New:  "",
  3415  							},
  3416  							{
  3417  								Type: DiffTypeNone,
  3418  								Name: "Mode",
  3419  								Old:  "",
  3420  								New:  "",
  3421  							},
  3422  						},
  3423  						Objects: []*ObjectDiff{
  3424  							{
  3425  								Type: DiffTypeDeleted,
  3426  								Name: "Static Port",
  3427  								Fields: []*FieldDiff{
  3428  									{
  3429  										Type: DiffTypeNone,
  3430  										Name: "HostNetwork",
  3431  										Old:  "",
  3432  										New:  "",
  3433  									},
  3434  									{
  3435  										Type: DiffTypeDeleted,
  3436  										Name: "Label",
  3437  										Old:  "foo",
  3438  										New:  "",
  3439  									},
  3440  									{
  3441  										Type: DiffTypeDeleted,
  3442  										Name: "To",
  3443  										Old:  "0",
  3444  										New:  "",
  3445  									},
  3446  									{
  3447  										Type: DiffTypeDeleted,
  3448  										Name: "Value",
  3449  										Old:  "80",
  3450  										New:  "",
  3451  									},
  3452  								},
  3453  							},
  3454  						},
  3455  					},
  3456  				},
  3457  			},
  3458  		},
  3459  		{
  3460  			TestCase: "Tasks edited",
  3461  			Old: &TaskGroup{
  3462  				Tasks: []*Task{
  3463  					{
  3464  						Name:   "foo",
  3465  						Driver: "docker",
  3466  					},
  3467  					{
  3468  						Name:   "bar",
  3469  						Driver: "docker",
  3470  					},
  3471  					{
  3472  						Name:          "baz",
  3473  						ShutdownDelay: 1 * time.Second,
  3474  					},
  3475  				},
  3476  			},
  3477  			New: &TaskGroup{
  3478  				Tasks: []*Task{
  3479  					{
  3480  						Name:   "bar",
  3481  						Driver: "docker",
  3482  					},
  3483  					{
  3484  						Name:   "bam",
  3485  						Driver: "docker",
  3486  					},
  3487  					{
  3488  						Name:          "baz",
  3489  						ShutdownDelay: 2 * time.Second,
  3490  					},
  3491  				},
  3492  			},
  3493  			Expected: &TaskGroupDiff{
  3494  				Type: DiffTypeEdited,
  3495  				Tasks: []*TaskDiff{
  3496  					{
  3497  						Type: DiffTypeAdded,
  3498  						Name: "bam",
  3499  						Fields: []*FieldDiff{
  3500  							{
  3501  								Type: DiffTypeAdded,
  3502  								Name: "Driver",
  3503  								Old:  "",
  3504  								New:  "docker",
  3505  							},
  3506  							{
  3507  								Type: DiffTypeAdded,
  3508  								Name: "KillTimeout",
  3509  								Old:  "",
  3510  								New:  "0",
  3511  							},
  3512  							{
  3513  								Type: DiffTypeAdded,
  3514  								Name: "Leader",
  3515  								Old:  "",
  3516  								New:  "false",
  3517  							},
  3518  							{
  3519  								Type: DiffTypeAdded,
  3520  								Name: "ShutdownDelay",
  3521  								Old:  "",
  3522  								New:  "0",
  3523  							},
  3524  						},
  3525  					},
  3526  					{
  3527  						Type: DiffTypeNone,
  3528  						Name: "bar",
  3529  					},
  3530  					{
  3531  						Type: DiffTypeEdited,
  3532  						Name: "baz",
  3533  						Fields: []*FieldDiff{
  3534  							{
  3535  								Type: DiffTypeEdited,
  3536  								Name: "ShutdownDelay",
  3537  								Old:  "1000000000",
  3538  								New:  "2000000000",
  3539  							},
  3540  						},
  3541  					},
  3542  					{
  3543  						Type: DiffTypeDeleted,
  3544  						Name: "foo",
  3545  						Fields: []*FieldDiff{
  3546  							{
  3547  								Type: DiffTypeDeleted,
  3548  								Name: "Driver",
  3549  								Old:  "docker",
  3550  								New:  "",
  3551  							},
  3552  							{
  3553  								Type: DiffTypeDeleted,
  3554  								Name: "KillTimeout",
  3555  								Old:  "0",
  3556  								New:  "",
  3557  							},
  3558  							{
  3559  								Type: DiffTypeDeleted,
  3560  								Name: "Leader",
  3561  								Old:  "false",
  3562  								New:  "",
  3563  							},
  3564  							{
  3565  								Type: DiffTypeDeleted,
  3566  								Name: "ShutdownDelay",
  3567  								Old:  "0",
  3568  								New:  "",
  3569  							},
  3570  						},
  3571  					},
  3572  				},
  3573  			},
  3574  		},
  3575  		{
  3576  			TestCase: "TaskGroup shutdown_delay edited",
  3577  			Old: &TaskGroup{
  3578  				ShutdownDelay: helper.TimeToPtr(30 * time.Second),
  3579  			},
  3580  			New: &TaskGroup{
  3581  				ShutdownDelay: helper.TimeToPtr(5 * time.Second),
  3582  			},
  3583  			Expected: &TaskGroupDiff{
  3584  				Type: DiffTypeEdited,
  3585  				Fields: []*FieldDiff{
  3586  					{
  3587  						Type: DiffTypeEdited,
  3588  						Name: "ShutdownDelay",
  3589  						Old:  "30000000000",
  3590  						New:  "5000000000",
  3591  					},
  3592  				},
  3593  			},
  3594  		},
  3595  		{
  3596  			TestCase: "TaskGroup shutdown_delay removed",
  3597  			Old: &TaskGroup{
  3598  				ShutdownDelay: helper.TimeToPtr(30 * time.Second),
  3599  			},
  3600  			New: &TaskGroup{},
  3601  			Expected: &TaskGroupDiff{
  3602  				Type: DiffTypeEdited,
  3603  				Fields: []*FieldDiff{
  3604  					{
  3605  						Type: DiffTypeDeleted,
  3606  						Name: "ShutdownDelay",
  3607  						Old:  "30000000000",
  3608  						New:  "",
  3609  					},
  3610  				},
  3611  			},
  3612  		},
  3613  		{
  3614  			TestCase: "TaskGroup shutdown_delay added",
  3615  			Old:      &TaskGroup{},
  3616  			New: &TaskGroup{
  3617  				ShutdownDelay: helper.TimeToPtr(30 * time.Second),
  3618  			},
  3619  			Expected: &TaskGroupDiff{
  3620  				Type: DiffTypeEdited,
  3621  				Fields: []*FieldDiff{
  3622  					{
  3623  						Type: DiffTypeAdded,
  3624  						Name: "ShutdownDelay",
  3625  						Old:  "",
  3626  						New:  "30000000000",
  3627  					},
  3628  				},
  3629  			},
  3630  		},
  3631  
  3632  		{
  3633  			TestCase: "TaskGroup volumes added",
  3634  			Old:      &TaskGroup{},
  3635  			New: &TaskGroup{
  3636  				Volumes: map[string]*VolumeRequest{
  3637  					"foo": &VolumeRequest{
  3638  						Name:     "foo",
  3639  						Type:     "host",
  3640  						Source:   "foo-src",
  3641  						ReadOnly: true,
  3642  					},
  3643  				},
  3644  			},
  3645  
  3646  			Expected: &TaskGroupDiff{
  3647  				Type: DiffTypeEdited,
  3648  				Objects: []*ObjectDiff{
  3649  					{
  3650  						Type: DiffTypeAdded,
  3651  						Name: "Volume",
  3652  						Fields: []*FieldDiff{
  3653  							{
  3654  								Type: DiffTypeAdded,
  3655  								Name: "Name",
  3656  								Old:  "",
  3657  								New:  "foo",
  3658  							},
  3659  							{
  3660  								Type: DiffTypeAdded,
  3661  								Name: "ReadOnly",
  3662  								Old:  "",
  3663  								New:  "true",
  3664  							},
  3665  							{
  3666  								Type: DiffTypeAdded,
  3667  								Name: "Source",
  3668  								Old:  "",
  3669  								New:  "foo-src",
  3670  							},
  3671  							{
  3672  								Type: DiffTypeAdded,
  3673  								Name: "Type",
  3674  								Old:  "",
  3675  								New:  "host",
  3676  							},
  3677  						},
  3678  					},
  3679  				},
  3680  			},
  3681  		},
  3682  
  3683  		{
  3684  			TestCase: "TaskGroup volumes edited",
  3685  			Old: &TaskGroup{
  3686  				Volumes: map[string]*VolumeRequest{
  3687  					"foo": &VolumeRequest{
  3688  						Name:     "foo",
  3689  						Type:     "csi",
  3690  						Source:   "foo-src1",
  3691  						ReadOnly: false,
  3692  						MountOptions: &CSIMountOptions{
  3693  							FSType:     "ext4",
  3694  							MountFlags: []string{"relatime", "rw"},
  3695  						},
  3696  					},
  3697  					"bar": &VolumeRequest{
  3698  						Name:     "bar",
  3699  						Type:     "host",
  3700  						Source:   "bar-src",
  3701  						ReadOnly: true,
  3702  					},
  3703  				},
  3704  			},
  3705  			New: &TaskGroup{
  3706  				Volumes: map[string]*VolumeRequest{
  3707  					"foo": &VolumeRequest{
  3708  						Name:     "foo",
  3709  						Type:     "csi",
  3710  						Source:   "foo-src2",
  3711  						ReadOnly: true,
  3712  						MountOptions: &CSIMountOptions{
  3713  							FSType:     "ext4",
  3714  							MountFlags: []string{"relatime", "rw", "nosuid"},
  3715  						},
  3716  					},
  3717  					"bar": &VolumeRequest{ // untouched
  3718  						Name:     "bar",
  3719  						Type:     "host",
  3720  						Source:   "bar-src",
  3721  						ReadOnly: true,
  3722  					},
  3723  				},
  3724  			},
  3725  
  3726  			Expected: &TaskGroupDiff{
  3727  				Type: DiffTypeEdited,
  3728  				Objects: []*ObjectDiff{
  3729  					{
  3730  						Type: DiffTypeEdited,
  3731  						Name: "Volume",
  3732  						Fields: []*FieldDiff{
  3733  							{
  3734  								Type: DiffTypeEdited,
  3735  								Name: "ReadOnly",
  3736  								Old:  "false",
  3737  								New:  "true",
  3738  							},
  3739  							{
  3740  								Type: DiffTypeEdited,
  3741  								Name: "Source",
  3742  								Old:  "foo-src1",
  3743  								New:  "foo-src2",
  3744  							},
  3745  						},
  3746  						Objects: []*ObjectDiff{
  3747  							{
  3748  								Type: DiffTypeEdited,
  3749  								Name: "MountOptions",
  3750  								Objects: []*ObjectDiff{
  3751  									{
  3752  										Type: DiffTypeAdded,
  3753  										Name: "MountFlags",
  3754  										Fields: []*FieldDiff{
  3755  											{
  3756  												Type: DiffTypeAdded,
  3757  												Name: "MountFlags",
  3758  												Old:  "",
  3759  												New:  "nosuid",
  3760  											},
  3761  										},
  3762  									},
  3763  								},
  3764  							},
  3765  						},
  3766  					},
  3767  				},
  3768  			},
  3769  		},
  3770  	}
  3771  
  3772  	for i, c := range cases {
  3773  		require.NotEmpty(t, c.TestCase, "case #%d needs a name", i+1)
  3774  
  3775  		t.Run(c.TestCase, func(t *testing.T) {
  3776  			result, err := c.Old.Diff(c.New, c.Contextual)
  3777  			switch c.ExpErr {
  3778  			case true:
  3779  				require.Error(t, err, "case %q expected error", c.TestCase)
  3780  			case false:
  3781  				require.NoError(t, err, "case %q expected no error", c.TestCase)
  3782  				require.True(t, reflect.DeepEqual(result, c.Expected),
  3783  					"case %q got\n%#v\nwant:\n%#v\n", c.TestCase, result, c.Expected)
  3784  			}
  3785  		})
  3786  	}
  3787  }
  3788  
  3789  func TestTaskDiff(t *testing.T) {
  3790  	cases := []struct {
  3791  		Name       string
  3792  		Old, New   *Task
  3793  		Expected   *TaskDiff
  3794  		Error      bool
  3795  		Contextual bool
  3796  	}{
  3797  		{
  3798  			Name: "Empty",
  3799  			Old:  nil,
  3800  			New:  nil,
  3801  			Expected: &TaskDiff{
  3802  				Type: DiffTypeNone,
  3803  			},
  3804  		},
  3805  		{
  3806  			Name: "Primitive only that has different names",
  3807  			Old: &Task{
  3808  				Name: "foo",
  3809  				Meta: map[string]string{
  3810  					"foo": "bar",
  3811  				},
  3812  			},
  3813  			New: &Task{
  3814  				Name: "bar",
  3815  				Meta: map[string]string{
  3816  					"foo": "bar",
  3817  				},
  3818  			},
  3819  			Error: true,
  3820  		},
  3821  		{
  3822  			Name: "Primitive only that is the same",
  3823  			Old: &Task{
  3824  				Name:   "foo",
  3825  				Driver: "exec",
  3826  				User:   "foo",
  3827  				Env: map[string]string{
  3828  					"FOO": "bar",
  3829  				},
  3830  				Meta: map[string]string{
  3831  					"foo": "bar",
  3832  				},
  3833  				KillTimeout: 1 * time.Second,
  3834  				Leader:      true,
  3835  			},
  3836  			New: &Task{
  3837  				Name:   "foo",
  3838  				Driver: "exec",
  3839  				User:   "foo",
  3840  				Env: map[string]string{
  3841  					"FOO": "bar",
  3842  				},
  3843  				Meta: map[string]string{
  3844  					"foo": "bar",
  3845  				},
  3846  				KillTimeout: 1 * time.Second,
  3847  				Leader:      true,
  3848  			},
  3849  			Expected: &TaskDiff{
  3850  				Type: DiffTypeNone,
  3851  				Name: "foo",
  3852  			},
  3853  		},
  3854  		{
  3855  			Name: "Primitive only that has diffs",
  3856  			Old: &Task{
  3857  				Name:   "foo",
  3858  				Driver: "exec",
  3859  				User:   "foo",
  3860  				Env: map[string]string{
  3861  					"FOO": "bar",
  3862  				},
  3863  				Meta: map[string]string{
  3864  					"foo": "bar",
  3865  				},
  3866  				KillTimeout: 1 * time.Second,
  3867  				Leader:      true,
  3868  			},
  3869  			New: &Task{
  3870  				Name:   "foo",
  3871  				Driver: "docker",
  3872  				User:   "bar",
  3873  				Env: map[string]string{
  3874  					"FOO": "baz",
  3875  				},
  3876  				Meta: map[string]string{
  3877  					"foo": "baz",
  3878  				},
  3879  				KillTimeout: 2 * time.Second,
  3880  				Leader:      false,
  3881  			},
  3882  			Expected: &TaskDiff{
  3883  				Type: DiffTypeEdited,
  3884  				Name: "foo",
  3885  				Fields: []*FieldDiff{
  3886  					{
  3887  						Type: DiffTypeEdited,
  3888  						Name: "Driver",
  3889  						Old:  "exec",
  3890  						New:  "docker",
  3891  					},
  3892  					{
  3893  						Type: DiffTypeEdited,
  3894  						Name: "Env[FOO]",
  3895  						Old:  "bar",
  3896  						New:  "baz",
  3897  					},
  3898  					{
  3899  						Type: DiffTypeEdited,
  3900  						Name: "KillTimeout",
  3901  						Old:  "1000000000",
  3902  						New:  "2000000000",
  3903  					},
  3904  					{
  3905  						Type: DiffTypeEdited,
  3906  						Name: "Leader",
  3907  						Old:  "true",
  3908  						New:  "false",
  3909  					},
  3910  					{
  3911  						Type: DiffTypeEdited,
  3912  						Name: "Meta[foo]",
  3913  						Old:  "bar",
  3914  						New:  "baz",
  3915  					},
  3916  					{
  3917  						Type: DiffTypeEdited,
  3918  						Name: "User",
  3919  						Old:  "foo",
  3920  						New:  "bar",
  3921  					},
  3922  				},
  3923  			},
  3924  		},
  3925  		{
  3926  			Name: "Map diff",
  3927  			Old: &Task{
  3928  				Meta: map[string]string{
  3929  					"foo": "foo",
  3930  					"bar": "bar",
  3931  				},
  3932  				Env: map[string]string{
  3933  					"foo": "foo",
  3934  					"bar": "bar",
  3935  				},
  3936  			},
  3937  			New: &Task{
  3938  				Meta: map[string]string{
  3939  					"bar": "bar",
  3940  					"baz": "baz",
  3941  				},
  3942  				Env: map[string]string{
  3943  					"bar": "bar",
  3944  					"baz": "baz",
  3945  				},
  3946  			},
  3947  			Expected: &TaskDiff{
  3948  				Type: DiffTypeEdited,
  3949  				Fields: []*FieldDiff{
  3950  					{
  3951  						Type: DiffTypeAdded,
  3952  						Name: "Env[baz]",
  3953  						Old:  "",
  3954  						New:  "baz",
  3955  					},
  3956  					{
  3957  						Type: DiffTypeDeleted,
  3958  						Name: "Env[foo]",
  3959  						Old:  "foo",
  3960  						New:  "",
  3961  					},
  3962  					{
  3963  						Type: DiffTypeAdded,
  3964  						Name: "Meta[baz]",
  3965  						Old:  "",
  3966  						New:  "baz",
  3967  					},
  3968  					{
  3969  						Type: DiffTypeDeleted,
  3970  						Name: "Meta[foo]",
  3971  						Old:  "foo",
  3972  						New:  "",
  3973  					},
  3974  				},
  3975  			},
  3976  		},
  3977  		{
  3978  			Name: "Constraints edited",
  3979  			Old: &Task{
  3980  				Constraints: []*Constraint{
  3981  					{
  3982  						LTarget: "foo",
  3983  						RTarget: "foo",
  3984  						Operand: "foo",
  3985  						str:     "foo",
  3986  					},
  3987  					{
  3988  						LTarget: "bar",
  3989  						RTarget: "bar",
  3990  						Operand: "bar",
  3991  						str:     "bar",
  3992  					},
  3993  				},
  3994  			},
  3995  			New: &Task{
  3996  				Constraints: []*Constraint{
  3997  					{
  3998  						LTarget: "foo",
  3999  						RTarget: "foo",
  4000  						Operand: "foo",
  4001  						str:     "foo",
  4002  					},
  4003  					{
  4004  						LTarget: "baz",
  4005  						RTarget: "baz",
  4006  						Operand: "baz",
  4007  						str:     "baz",
  4008  					},
  4009  				},
  4010  			},
  4011  			Expected: &TaskDiff{
  4012  				Type: DiffTypeEdited,
  4013  				Objects: []*ObjectDiff{
  4014  					{
  4015  						Type: DiffTypeAdded,
  4016  						Name: "Constraint",
  4017  						Fields: []*FieldDiff{
  4018  							{
  4019  								Type: DiffTypeAdded,
  4020  								Name: "LTarget",
  4021  								Old:  "",
  4022  								New:  "baz",
  4023  							},
  4024  							{
  4025  								Type: DiffTypeAdded,
  4026  								Name: "Operand",
  4027  								Old:  "",
  4028  								New:  "baz",
  4029  							},
  4030  							{
  4031  								Type: DiffTypeAdded,
  4032  								Name: "RTarget",
  4033  								Old:  "",
  4034  								New:  "baz",
  4035  							},
  4036  						},
  4037  					},
  4038  					{
  4039  						Type: DiffTypeDeleted,
  4040  						Name: "Constraint",
  4041  						Fields: []*FieldDiff{
  4042  							{
  4043  								Type: DiffTypeDeleted,
  4044  								Name: "LTarget",
  4045  								Old:  "bar",
  4046  								New:  "",
  4047  							},
  4048  							{
  4049  								Type: DiffTypeDeleted,
  4050  								Name: "Operand",
  4051  								Old:  "bar",
  4052  								New:  "",
  4053  							},
  4054  							{
  4055  								Type: DiffTypeDeleted,
  4056  								Name: "RTarget",
  4057  								Old:  "bar",
  4058  								New:  "",
  4059  							},
  4060  						},
  4061  					},
  4062  				},
  4063  			},
  4064  		},
  4065  		{
  4066  			Name: "Affinities edited",
  4067  			Old: &Task{
  4068  				Affinities: []*Affinity{
  4069  					{
  4070  						LTarget: "foo",
  4071  						RTarget: "foo",
  4072  						Operand: "foo",
  4073  						Weight:  20,
  4074  						str:     "foo",
  4075  					},
  4076  					{
  4077  						LTarget: "bar",
  4078  						RTarget: "bar",
  4079  						Operand: "bar",
  4080  						Weight:  20,
  4081  						str:     "bar",
  4082  					},
  4083  				},
  4084  			},
  4085  			New: &Task{
  4086  				Affinities: []*Affinity{
  4087  					{
  4088  						LTarget: "foo",
  4089  						RTarget: "foo",
  4090  						Operand: "foo",
  4091  						Weight:  20,
  4092  						str:     "foo",
  4093  					},
  4094  					{
  4095  						LTarget: "baz",
  4096  						RTarget: "baz",
  4097  						Operand: "baz",
  4098  						Weight:  20,
  4099  						str:     "baz",
  4100  					},
  4101  				},
  4102  			},
  4103  			Expected: &TaskDiff{
  4104  				Type: DiffTypeEdited,
  4105  				Objects: []*ObjectDiff{
  4106  					{
  4107  						Type: DiffTypeAdded,
  4108  						Name: "Affinity",
  4109  						Fields: []*FieldDiff{
  4110  							{
  4111  								Type: DiffTypeAdded,
  4112  								Name: "LTarget",
  4113  								Old:  "",
  4114  								New:  "baz",
  4115  							},
  4116  							{
  4117  								Type: DiffTypeAdded,
  4118  								Name: "Operand",
  4119  								Old:  "",
  4120  								New:  "baz",
  4121  							},
  4122  							{
  4123  								Type: DiffTypeAdded,
  4124  								Name: "RTarget",
  4125  								Old:  "",
  4126  								New:  "baz",
  4127  							},
  4128  							{
  4129  								Type: DiffTypeAdded,
  4130  								Name: "Weight",
  4131  								Old:  "",
  4132  								New:  "20",
  4133  							},
  4134  						},
  4135  					},
  4136  					{
  4137  						Type: DiffTypeDeleted,
  4138  						Name: "Affinity",
  4139  						Fields: []*FieldDiff{
  4140  							{
  4141  								Type: DiffTypeDeleted,
  4142  								Name: "LTarget",
  4143  								Old:  "bar",
  4144  								New:  "",
  4145  							},
  4146  							{
  4147  								Type: DiffTypeDeleted,
  4148  								Name: "Operand",
  4149  								Old:  "bar",
  4150  								New:  "",
  4151  							},
  4152  							{
  4153  								Type: DiffTypeDeleted,
  4154  								Name: "RTarget",
  4155  								Old:  "bar",
  4156  								New:  "",
  4157  							},
  4158  							{
  4159  								Type: DiffTypeDeleted,
  4160  								Name: "Weight",
  4161  								Old:  "20",
  4162  								New:  "",
  4163  							},
  4164  						},
  4165  					},
  4166  				},
  4167  			},
  4168  		},
  4169  		{
  4170  			Name: "LogConfig added",
  4171  			Old:  &Task{},
  4172  			New: &Task{
  4173  				LogConfig: &LogConfig{
  4174  					MaxFiles:      1,
  4175  					MaxFileSizeMB: 10,
  4176  				},
  4177  			},
  4178  			Expected: &TaskDiff{
  4179  				Type: DiffTypeEdited,
  4180  				Objects: []*ObjectDiff{
  4181  					{
  4182  						Type: DiffTypeAdded,
  4183  						Name: "LogConfig",
  4184  						Fields: []*FieldDiff{
  4185  							{
  4186  								Type: DiffTypeAdded,
  4187  								Name: "MaxFileSizeMB",
  4188  								Old:  "",
  4189  								New:  "10",
  4190  							},
  4191  							{
  4192  								Type: DiffTypeAdded,
  4193  								Name: "MaxFiles",
  4194  								Old:  "",
  4195  								New:  "1",
  4196  							},
  4197  						},
  4198  					},
  4199  				},
  4200  			},
  4201  		},
  4202  		{
  4203  			Name: "LogConfig deleted",
  4204  			Old: &Task{
  4205  				LogConfig: &LogConfig{
  4206  					MaxFiles:      1,
  4207  					MaxFileSizeMB: 10,
  4208  				},
  4209  			},
  4210  			New: &Task{},
  4211  			Expected: &TaskDiff{
  4212  				Type: DiffTypeEdited,
  4213  				Objects: []*ObjectDiff{
  4214  					{
  4215  						Type: DiffTypeDeleted,
  4216  						Name: "LogConfig",
  4217  						Fields: []*FieldDiff{
  4218  							{
  4219  								Type: DiffTypeDeleted,
  4220  								Name: "MaxFileSizeMB",
  4221  								Old:  "10",
  4222  								New:  "",
  4223  							},
  4224  							{
  4225  								Type: DiffTypeDeleted,
  4226  								Name: "MaxFiles",
  4227  								Old:  "1",
  4228  								New:  "",
  4229  							},
  4230  						},
  4231  					},
  4232  				},
  4233  			},
  4234  		},
  4235  		{
  4236  			Name: "LogConfig edited",
  4237  			Old: &Task{
  4238  				LogConfig: &LogConfig{
  4239  					MaxFiles:      1,
  4240  					MaxFileSizeMB: 10,
  4241  				},
  4242  			},
  4243  			New: &Task{
  4244  				LogConfig: &LogConfig{
  4245  					MaxFiles:      2,
  4246  					MaxFileSizeMB: 20,
  4247  				},
  4248  			},
  4249  			Expected: &TaskDiff{
  4250  				Type: DiffTypeEdited,
  4251  				Objects: []*ObjectDiff{
  4252  					{
  4253  						Type: DiffTypeEdited,
  4254  						Name: "LogConfig",
  4255  						Fields: []*FieldDiff{
  4256  							{
  4257  								Type: DiffTypeEdited,
  4258  								Name: "MaxFileSizeMB",
  4259  								Old:  "10",
  4260  								New:  "20",
  4261  							},
  4262  							{
  4263  								Type: DiffTypeEdited,
  4264  								Name: "MaxFiles",
  4265  								Old:  "1",
  4266  								New:  "2",
  4267  							},
  4268  						},
  4269  					},
  4270  				},
  4271  			},
  4272  		},
  4273  		{
  4274  			Name:       "LogConfig edited with context",
  4275  			Contextual: true,
  4276  			Old: &Task{
  4277  				LogConfig: &LogConfig{
  4278  					MaxFiles:      1,
  4279  					MaxFileSizeMB: 10,
  4280  				},
  4281  			},
  4282  			New: &Task{
  4283  				LogConfig: &LogConfig{
  4284  					MaxFiles:      1,
  4285  					MaxFileSizeMB: 20,
  4286  				},
  4287  			},
  4288  			Expected: &TaskDiff{
  4289  				Type: DiffTypeEdited,
  4290  				Objects: []*ObjectDiff{
  4291  					{
  4292  						Type: DiffTypeEdited,
  4293  						Name: "LogConfig",
  4294  						Fields: []*FieldDiff{
  4295  							{
  4296  								Type: DiffTypeEdited,
  4297  								Name: "MaxFileSizeMB",
  4298  								Old:  "10",
  4299  								New:  "20",
  4300  							},
  4301  							{
  4302  								Type: DiffTypeNone,
  4303  								Name: "MaxFiles",
  4304  								Old:  "1",
  4305  								New:  "1",
  4306  							},
  4307  						},
  4308  					},
  4309  				},
  4310  			},
  4311  		},
  4312  		{
  4313  			Name: "Artifacts edited",
  4314  			Old: &Task{
  4315  				Artifacts: []*TaskArtifact{
  4316  					{
  4317  						GetterSource: "foo",
  4318  						GetterOptions: map[string]string{
  4319  							"foo": "bar",
  4320  						},
  4321  						RelativeDest: "foo",
  4322  					},
  4323  					{
  4324  						GetterSource: "bar",
  4325  						GetterOptions: map[string]string{
  4326  							"bar": "baz",
  4327  						},
  4328  						GetterHeaders: map[string]string{
  4329  							"User": "user1",
  4330  						},
  4331  						GetterMode:   "dir",
  4332  						RelativeDest: "bar",
  4333  					},
  4334  				},
  4335  			},
  4336  			New: &Task{
  4337  				Artifacts: []*TaskArtifact{
  4338  					{
  4339  						GetterSource: "foo",
  4340  						GetterOptions: map[string]string{
  4341  							"foo": "bar",
  4342  						},
  4343  						RelativeDest: "foo",
  4344  					},
  4345  					{
  4346  						GetterSource: "bam",
  4347  						GetterOptions: map[string]string{
  4348  							"bam": "baz",
  4349  						},
  4350  						GetterHeaders: map[string]string{
  4351  							"User":       "user2",
  4352  							"User-Agent": "nomad",
  4353  						},
  4354  						GetterMode:   "file",
  4355  						RelativeDest: "bam",
  4356  					},
  4357  				},
  4358  			},
  4359  			Expected: &TaskDiff{
  4360  				Type: DiffTypeEdited,
  4361  				Objects: []*ObjectDiff{
  4362  					{
  4363  						Type: DiffTypeAdded,
  4364  						Name: "Artifact",
  4365  						Fields: []*FieldDiff{
  4366  							{
  4367  								Type: DiffTypeAdded,
  4368  								Name: "GetterHeaders[User-Agent]",
  4369  								Old:  "",
  4370  								New:  "nomad",
  4371  							},
  4372  							{
  4373  								Type: DiffTypeAdded,
  4374  								Name: "GetterHeaders[User]",
  4375  								Old:  "",
  4376  								New:  "user2",
  4377  							},
  4378  							{
  4379  								Type: DiffTypeAdded,
  4380  								Name: "GetterMode",
  4381  								Old:  "",
  4382  								New:  "file",
  4383  							},
  4384  							{
  4385  								Type: DiffTypeAdded,
  4386  								Name: "GetterOptions[bam]",
  4387  								Old:  "",
  4388  								New:  "baz",
  4389  							},
  4390  							{
  4391  								Type: DiffTypeAdded,
  4392  								Name: "GetterSource",
  4393  								Old:  "",
  4394  								New:  "bam",
  4395  							},
  4396  							{
  4397  								Type: DiffTypeAdded,
  4398  								Name: "RelativeDest",
  4399  								Old:  "",
  4400  								New:  "bam",
  4401  							},
  4402  						},
  4403  					},
  4404  					{
  4405  						Type: DiffTypeDeleted,
  4406  						Name: "Artifact",
  4407  						Fields: []*FieldDiff{
  4408  							{
  4409  								Type: DiffTypeDeleted,
  4410  								Name: "GetterHeaders[User]",
  4411  								Old:  "user1",
  4412  								New:  "",
  4413  							},
  4414  							{
  4415  								Type: DiffTypeDeleted,
  4416  								Name: "GetterMode",
  4417  								Old:  "dir",
  4418  								New:  "",
  4419  							},
  4420  							{
  4421  								Type: DiffTypeDeleted,
  4422  								Name: "GetterOptions[bar]",
  4423  								Old:  "baz",
  4424  								New:  "",
  4425  							},
  4426  							{
  4427  								Type: DiffTypeDeleted,
  4428  								Name: "GetterSource",
  4429  								Old:  "bar",
  4430  								New:  "",
  4431  							},
  4432  							{
  4433  								Type: DiffTypeDeleted,
  4434  								Name: "RelativeDest",
  4435  								Old:  "bar",
  4436  								New:  "",
  4437  							},
  4438  						},
  4439  					},
  4440  				},
  4441  			},
  4442  		},
  4443  		{
  4444  			Name: "Resources edited (no networks)",
  4445  			Old: &Task{
  4446  				Resources: &Resources{
  4447  					CPU:      100,
  4448  					MemoryMB: 100,
  4449  					DiskMB:   100,
  4450  				},
  4451  			},
  4452  			New: &Task{
  4453  				Resources: &Resources{
  4454  					CPU:      200,
  4455  					MemoryMB: 200,
  4456  					DiskMB:   200,
  4457  				},
  4458  			},
  4459  			Expected: &TaskDiff{
  4460  				Type: DiffTypeEdited,
  4461  				Objects: []*ObjectDiff{
  4462  					{
  4463  						Type: DiffTypeEdited,
  4464  						Name: "Resources",
  4465  						Fields: []*FieldDiff{
  4466  							{
  4467  								Type: DiffTypeEdited,
  4468  								Name: "CPU",
  4469  								Old:  "100",
  4470  								New:  "200",
  4471  							},
  4472  							{
  4473  								Type: DiffTypeEdited,
  4474  								Name: "DiskMB",
  4475  								Old:  "100",
  4476  								New:  "200",
  4477  							},
  4478  							{
  4479  								Type: DiffTypeEdited,
  4480  								Name: "MemoryMB",
  4481  								Old:  "100",
  4482  								New:  "200",
  4483  							},
  4484  						},
  4485  					},
  4486  				},
  4487  			},
  4488  		},
  4489  		{
  4490  			Name:       "Resources edited (no networks) with context",
  4491  			Contextual: true,
  4492  			Old: &Task{
  4493  				Resources: &Resources{
  4494  					CPU:      100,
  4495  					MemoryMB: 100,
  4496  					DiskMB:   100,
  4497  				},
  4498  			},
  4499  			New: &Task{
  4500  				Resources: &Resources{
  4501  					CPU:      200,
  4502  					MemoryMB: 100,
  4503  					DiskMB:   200,
  4504  				},
  4505  			},
  4506  			Expected: &TaskDiff{
  4507  				Type: DiffTypeEdited,
  4508  				Objects: []*ObjectDiff{
  4509  					{
  4510  						Type: DiffTypeEdited,
  4511  						Name: "Resources",
  4512  						Fields: []*FieldDiff{
  4513  							{
  4514  								Type: DiffTypeEdited,
  4515  								Name: "CPU",
  4516  								Old:  "100",
  4517  								New:  "200",
  4518  							},
  4519  							{
  4520  								Type: DiffTypeEdited,
  4521  								Name: "DiskMB",
  4522  								Old:  "100",
  4523  								New:  "200",
  4524  							},
  4525  							{
  4526  								Type: DiffTypeNone,
  4527  								Name: "IOPS",
  4528  								Old:  "0",
  4529  								New:  "0",
  4530  							},
  4531  							{
  4532  								Type: DiffTypeNone,
  4533  								Name: "MemoryMB",
  4534  								Old:  "100",
  4535  								New:  "100",
  4536  							},
  4537  						},
  4538  					},
  4539  				},
  4540  			},
  4541  		},
  4542  		{
  4543  			Name: "Network Resources edited",
  4544  			Old: &Task{
  4545  				Resources: &Resources{
  4546  					Networks: []*NetworkResource{
  4547  						{
  4548  							Device: "foo",
  4549  							CIDR:   "foo",
  4550  							IP:     "foo",
  4551  							MBits:  100,
  4552  							ReservedPorts: []Port{
  4553  								{
  4554  									Label: "foo",
  4555  									Value: 80,
  4556  								},
  4557  							},
  4558  							DynamicPorts: []Port{
  4559  								{
  4560  									Label: "bar",
  4561  									To:    8080,
  4562  								},
  4563  							},
  4564  						},
  4565  					},
  4566  				},
  4567  			},
  4568  			New: &Task{
  4569  				Resources: &Resources{
  4570  					Networks: []*NetworkResource{
  4571  						{
  4572  							Device: "bar",
  4573  							CIDR:   "bar",
  4574  							IP:     "bar",
  4575  							MBits:  200,
  4576  							ReservedPorts: []Port{
  4577  								{
  4578  									Label: "foo",
  4579  									Value: 81,
  4580  								},
  4581  							},
  4582  							DynamicPorts: []Port{
  4583  								{
  4584  									Label: "baz",
  4585  									To:    8081,
  4586  								},
  4587  							},
  4588  						},
  4589  					},
  4590  				},
  4591  			},
  4592  			Expected: &TaskDiff{
  4593  				Type: DiffTypeEdited,
  4594  				Objects: []*ObjectDiff{
  4595  					{
  4596  						Type: DiffTypeEdited,
  4597  						Name: "Resources",
  4598  						Objects: []*ObjectDiff{
  4599  							{
  4600  								Type: DiffTypeAdded,
  4601  								Name: "Network",
  4602  								Fields: []*FieldDiff{
  4603  									{
  4604  										Type: DiffTypeAdded,
  4605  										Name: "MBits",
  4606  										Old:  "",
  4607  										New:  "200",
  4608  									},
  4609  								},
  4610  								Objects: []*ObjectDiff{
  4611  									{
  4612  										Type: DiffTypeAdded,
  4613  										Name: "Static Port",
  4614  										Fields: []*FieldDiff{
  4615  											{
  4616  												Type: DiffTypeAdded,
  4617  												Name: "Label",
  4618  												Old:  "",
  4619  												New:  "foo",
  4620  											},
  4621  											{
  4622  												Type: DiffTypeAdded,
  4623  												Name: "To",
  4624  												Old:  "",
  4625  												New:  "0",
  4626  											},
  4627  											{
  4628  												Type: DiffTypeAdded,
  4629  												Name: "Value",
  4630  												Old:  "",
  4631  												New:  "81",
  4632  											},
  4633  										},
  4634  									},
  4635  									{
  4636  										Type: DiffTypeAdded,
  4637  										Name: "Dynamic Port",
  4638  										Fields: []*FieldDiff{
  4639  											{
  4640  												Type: DiffTypeAdded,
  4641  												Name: "Label",
  4642  												Old:  "",
  4643  												New:  "baz",
  4644  											},
  4645  											{
  4646  												Type: DiffTypeAdded,
  4647  												Name: "To",
  4648  												Old:  "",
  4649  												New:  "8081",
  4650  											},
  4651  										},
  4652  									},
  4653  								},
  4654  							},
  4655  							{
  4656  								Type: DiffTypeDeleted,
  4657  								Name: "Network",
  4658  								Fields: []*FieldDiff{
  4659  									{
  4660  										Type: DiffTypeDeleted,
  4661  										Name: "MBits",
  4662  										Old:  "100",
  4663  										New:  "",
  4664  									},
  4665  								},
  4666  								Objects: []*ObjectDiff{
  4667  									{
  4668  										Type: DiffTypeDeleted,
  4669  										Name: "Static Port",
  4670  										Fields: []*FieldDiff{
  4671  											{
  4672  												Type: DiffTypeDeleted,
  4673  												Name: "Label",
  4674  												Old:  "foo",
  4675  												New:  "",
  4676  											},
  4677  											{
  4678  												Type: DiffTypeDeleted,
  4679  												Name: "To",
  4680  												Old:  "0",
  4681  												New:  "",
  4682  											},
  4683  											{
  4684  												Type: DiffTypeDeleted,
  4685  												Name: "Value",
  4686  												Old:  "80",
  4687  												New:  "",
  4688  											},
  4689  										},
  4690  									},
  4691  									{
  4692  										Type: DiffTypeDeleted,
  4693  										Name: "Dynamic Port",
  4694  										Fields: []*FieldDiff{
  4695  											{
  4696  												Type: DiffTypeDeleted,
  4697  												Name: "Label",
  4698  												Old:  "bar",
  4699  												New:  "",
  4700  											},
  4701  											{
  4702  												Type: DiffTypeDeleted,
  4703  												Name: "To",
  4704  												Old:  "8080",
  4705  												New:  "",
  4706  											},
  4707  										},
  4708  									},
  4709  								},
  4710  							},
  4711  						},
  4712  					},
  4713  				},
  4714  			},
  4715  		},
  4716  		{
  4717  			Name: "Device Resources edited",
  4718  			Old: &Task{
  4719  				Resources: &Resources{
  4720  					Devices: []*RequestedDevice{
  4721  						{
  4722  							Name:  "foo",
  4723  							Count: 2,
  4724  						},
  4725  						{
  4726  							Name:  "bar",
  4727  							Count: 2,
  4728  						},
  4729  						{
  4730  							Name:  "baz",
  4731  							Count: 2,
  4732  						},
  4733  					},
  4734  				},
  4735  			},
  4736  			New: &Task{
  4737  				Resources: &Resources{
  4738  					Devices: []*RequestedDevice{
  4739  						{
  4740  							Name:  "foo",
  4741  							Count: 2,
  4742  						},
  4743  						{
  4744  							Name:  "bar",
  4745  							Count: 3,
  4746  						},
  4747  						{
  4748  							Name:  "bam",
  4749  							Count: 2,
  4750  						},
  4751  					},
  4752  				},
  4753  			},
  4754  			Expected: &TaskDiff{
  4755  				Type: DiffTypeEdited,
  4756  				Objects: []*ObjectDiff{
  4757  					{
  4758  						Type: DiffTypeEdited,
  4759  						Name: "Resources",
  4760  						Objects: []*ObjectDiff{
  4761  							{
  4762  								Type: DiffTypeEdited,
  4763  								Name: "Device",
  4764  								Fields: []*FieldDiff{
  4765  									{
  4766  										Type: DiffTypeEdited,
  4767  										Name: "Count",
  4768  										Old:  "2",
  4769  										New:  "3",
  4770  									},
  4771  								},
  4772  							},
  4773  							{
  4774  								Type: DiffTypeAdded,
  4775  								Name: "Device",
  4776  								Fields: []*FieldDiff{
  4777  									{
  4778  										Type: DiffTypeAdded,
  4779  										Name: "Count",
  4780  										Old:  "",
  4781  										New:  "2",
  4782  									},
  4783  									{
  4784  										Type: DiffTypeAdded,
  4785  										Name: "Name",
  4786  										Old:  "",
  4787  										New:  "bam",
  4788  									},
  4789  								},
  4790  							},
  4791  							{
  4792  								Type: DiffTypeDeleted,
  4793  								Name: "Device",
  4794  								Fields: []*FieldDiff{
  4795  									{
  4796  										Type: DiffTypeDeleted,
  4797  										Name: "Count",
  4798  										Old:  "2",
  4799  										New:  "",
  4800  									},
  4801  									{
  4802  										Type: DiffTypeDeleted,
  4803  										Name: "Name",
  4804  										Old:  "baz",
  4805  										New:  "",
  4806  									},
  4807  								},
  4808  							},
  4809  						},
  4810  					},
  4811  				},
  4812  			},
  4813  		},
  4814  		{
  4815  			Name:       "Device Resources edited with context",
  4816  			Contextual: true,
  4817  			Old: &Task{
  4818  				Resources: &Resources{
  4819  					CPU:      100,
  4820  					MemoryMB: 100,
  4821  					DiskMB:   100,
  4822  					Devices: []*RequestedDevice{
  4823  						{
  4824  							Name:  "foo",
  4825  							Count: 2,
  4826  						},
  4827  						{
  4828  							Name:  "bar",
  4829  							Count: 2,
  4830  						},
  4831  						{
  4832  							Name:  "baz",
  4833  							Count: 2,
  4834  						},
  4835  					},
  4836  				},
  4837  			},
  4838  			New: &Task{
  4839  				Resources: &Resources{
  4840  					CPU:      100,
  4841  					MemoryMB: 100,
  4842  					DiskMB:   100,
  4843  					Devices: []*RequestedDevice{
  4844  						{
  4845  							Name:  "foo",
  4846  							Count: 2,
  4847  						},
  4848  						{
  4849  							Name:  "bar",
  4850  							Count: 3,
  4851  						},
  4852  						{
  4853  							Name:  "bam",
  4854  							Count: 2,
  4855  						},
  4856  					},
  4857  				},
  4858  			},
  4859  			Expected: &TaskDiff{
  4860  				Type: DiffTypeEdited,
  4861  				Objects: []*ObjectDiff{
  4862  					{
  4863  						Type: DiffTypeEdited,
  4864  						Name: "Resources",
  4865  						Fields: []*FieldDiff{
  4866  							{
  4867  								Type: DiffTypeNone,
  4868  								Name: "CPU",
  4869  								Old:  "100",
  4870  								New:  "100",
  4871  							},
  4872  							{
  4873  								Type: DiffTypeNone,
  4874  								Name: "DiskMB",
  4875  								Old:  "100",
  4876  								New:  "100",
  4877  							},
  4878  							{
  4879  								Type: DiffTypeNone,
  4880  								Name: "IOPS",
  4881  								Old:  "0",
  4882  								New:  "0",
  4883  							},
  4884  							{
  4885  								Type: DiffTypeNone,
  4886  								Name: "MemoryMB",
  4887  								Old:  "100",
  4888  								New:  "100",
  4889  							},
  4890  						},
  4891  						Objects: []*ObjectDiff{
  4892  							{
  4893  								Type: DiffTypeEdited,
  4894  								Name: "Device",
  4895  								Fields: []*FieldDiff{
  4896  									{
  4897  										Type: DiffTypeEdited,
  4898  										Name: "Count",
  4899  										Old:  "2",
  4900  										New:  "3",
  4901  									},
  4902  									{
  4903  										Type: DiffTypeNone,
  4904  										Name: "Name",
  4905  										Old:  "bar",
  4906  										New:  "bar",
  4907  									},
  4908  								},
  4909  							},
  4910  							{
  4911  								Type: DiffTypeAdded,
  4912  								Name: "Device",
  4913  								Fields: []*FieldDiff{
  4914  									{
  4915  										Type: DiffTypeAdded,
  4916  										Name: "Count",
  4917  										Old:  "",
  4918  										New:  "2",
  4919  									},
  4920  									{
  4921  										Type: DiffTypeAdded,
  4922  										Name: "Name",
  4923  										Old:  "",
  4924  										New:  "bam",
  4925  									},
  4926  								},
  4927  							},
  4928  							{
  4929  								Type: DiffTypeDeleted,
  4930  								Name: "Device",
  4931  								Fields: []*FieldDiff{
  4932  									{
  4933  										Type: DiffTypeDeleted,
  4934  										Name: "Count",
  4935  										Old:  "2",
  4936  										New:  "",
  4937  									},
  4938  									{
  4939  										Type: DiffTypeDeleted,
  4940  										Name: "Name",
  4941  										Old:  "baz",
  4942  										New:  "",
  4943  									},
  4944  								},
  4945  							},
  4946  						},
  4947  					},
  4948  				},
  4949  			},
  4950  		},
  4951  		{
  4952  			Name: "Config same",
  4953  			Old: &Task{
  4954  				Config: map[string]interface{}{
  4955  					"foo": 1,
  4956  					"bar": "bar",
  4957  					"bam": []string{"a", "b"},
  4958  					"baz": map[string]int{
  4959  						"a": 1,
  4960  						"b": 2,
  4961  					},
  4962  					"boom": &Port{
  4963  						Label: "boom_port",
  4964  					},
  4965  				},
  4966  			},
  4967  			New: &Task{
  4968  				Config: map[string]interface{}{
  4969  					"foo": 1,
  4970  					"bar": "bar",
  4971  					"bam": []string{"a", "b"},
  4972  					"baz": map[string]int{
  4973  						"a": 1,
  4974  						"b": 2,
  4975  					},
  4976  					"boom": &Port{
  4977  						Label: "boom_port",
  4978  					},
  4979  				},
  4980  			},
  4981  			Expected: &TaskDiff{
  4982  				Type: DiffTypeNone,
  4983  			},
  4984  		},
  4985  		{
  4986  			Name: "Config edited",
  4987  			Old: &Task{
  4988  				Config: map[string]interface{}{
  4989  					"foo": 1,
  4990  					"bar": "baz",
  4991  					"bam": []string{"a", "b"},
  4992  					"baz": map[string]int{
  4993  						"a": 1,
  4994  						"b": 2,
  4995  					},
  4996  					"boom": &Port{
  4997  						Label: "boom_port",
  4998  					},
  4999  				},
  5000  			},
  5001  			New: &Task{
  5002  				Config: map[string]interface{}{
  5003  					"foo": 2,
  5004  					"bar": "baz",
  5005  					"bam": []string{"a", "c", "d"},
  5006  					"baz": map[string]int{
  5007  						"b": 3,
  5008  						"c": 4,
  5009  					},
  5010  					"boom": &Port{
  5011  						Label: "boom_port2",
  5012  					},
  5013  				},
  5014  			},
  5015  			Expected: &TaskDiff{
  5016  				Type: DiffTypeEdited,
  5017  				Objects: []*ObjectDiff{
  5018  					{
  5019  						Type: DiffTypeEdited,
  5020  						Name: "Config",
  5021  						Fields: []*FieldDiff{
  5022  							{
  5023  								Type: DiffTypeEdited,
  5024  								Name: "bam[1]",
  5025  								Old:  "b",
  5026  								New:  "c",
  5027  							},
  5028  							{
  5029  								Type: DiffTypeAdded,
  5030  								Name: "bam[2]",
  5031  								Old:  "",
  5032  								New:  "d",
  5033  							},
  5034  							{
  5035  								Type: DiffTypeDeleted,
  5036  								Name: "baz[a]",
  5037  								Old:  "1",
  5038  								New:  "",
  5039  							},
  5040  							{
  5041  								Type: DiffTypeEdited,
  5042  								Name: "baz[b]",
  5043  								Old:  "2",
  5044  								New:  "3",
  5045  							},
  5046  							{
  5047  								Type: DiffTypeAdded,
  5048  								Name: "baz[c]",
  5049  								Old:  "",
  5050  								New:  "4",
  5051  							},
  5052  							{
  5053  								Type: DiffTypeEdited,
  5054  								Name: "boom.Label",
  5055  								Old:  "boom_port",
  5056  								New:  "boom_port2",
  5057  							},
  5058  							{
  5059  								Type: DiffTypeEdited,
  5060  								Name: "foo",
  5061  								Old:  "1",
  5062  								New:  "2",
  5063  							},
  5064  						},
  5065  					},
  5066  				},
  5067  			},
  5068  		},
  5069  		{
  5070  			Name:       "Config edited with context",
  5071  			Contextual: true,
  5072  			Old: &Task{
  5073  				Config: map[string]interface{}{
  5074  					"foo": 1,
  5075  					"bar": "baz",
  5076  					"bam": []string{"a", "b"},
  5077  					"baz": map[string]int{
  5078  						"a": 1,
  5079  						"b": 2,
  5080  					},
  5081  					"boom": &Port{
  5082  						Label: "boom_port",
  5083  					},
  5084  				},
  5085  			},
  5086  			New: &Task{
  5087  				Config: map[string]interface{}{
  5088  					"foo": 2,
  5089  					"bar": "baz",
  5090  					"bam": []string{"a", "c", "d"},
  5091  					"baz": map[string]int{
  5092  						"a": 1,
  5093  						"b": 2,
  5094  					},
  5095  					"boom": &Port{
  5096  						Label: "boom_port",
  5097  					},
  5098  				},
  5099  			},
  5100  			Expected: &TaskDiff{
  5101  				Type: DiffTypeEdited,
  5102  				Objects: []*ObjectDiff{
  5103  					{
  5104  						Type: DiffTypeEdited,
  5105  						Name: "Config",
  5106  						Fields: []*FieldDiff{
  5107  							{
  5108  								Type: DiffTypeNone,
  5109  								Name: "bam[0]",
  5110  								Old:  "a",
  5111  								New:  "a",
  5112  							},
  5113  							{
  5114  								Type: DiffTypeEdited,
  5115  								Name: "bam[1]",
  5116  								Old:  "b",
  5117  								New:  "c",
  5118  							},
  5119  							{
  5120  								Type: DiffTypeAdded,
  5121  								Name: "bam[2]",
  5122  								Old:  "",
  5123  								New:  "d",
  5124  							},
  5125  							{
  5126  								Type: DiffTypeNone,
  5127  								Name: "bar",
  5128  								Old:  "baz",
  5129  								New:  "baz",
  5130  							},
  5131  							{
  5132  								Type: DiffTypeNone,
  5133  								Name: "baz[a]",
  5134  								Old:  "1",
  5135  								New:  "1",
  5136  							},
  5137  							{
  5138  								Type: DiffTypeNone,
  5139  								Name: "baz[b]",
  5140  								Old:  "2",
  5141  								New:  "2",
  5142  							},
  5143  							{
  5144  								Type: DiffTypeNone,
  5145  								Name: "boom.HostNetwork",
  5146  								Old:  "",
  5147  								New:  "",
  5148  							},
  5149  							{
  5150  								Type: DiffTypeNone,
  5151  								Name: "boom.Label",
  5152  								Old:  "boom_port",
  5153  								New:  "boom_port",
  5154  							},
  5155  							{
  5156  								Type: DiffTypeNone,
  5157  								Name: "boom.To",
  5158  								Old:  "0",
  5159  								New:  "0",
  5160  							},
  5161  							{
  5162  								Type: DiffTypeNone,
  5163  								Name: "boom.Value",
  5164  								Old:  "0",
  5165  								New:  "0",
  5166  							},
  5167  							{
  5168  								Type: DiffTypeEdited,
  5169  								Name: "foo",
  5170  								Old:  "1",
  5171  								New:  "2",
  5172  							},
  5173  						},
  5174  					},
  5175  				},
  5176  			},
  5177  		},
  5178  		{
  5179  			Name: "Services edited (no checks)",
  5180  			Old: &Task{
  5181  				Services: []*Service{
  5182  					{
  5183  						Name:      "foo",
  5184  						PortLabel: "foo",
  5185  					},
  5186  					{
  5187  						Name:      "bar",
  5188  						PortLabel: "bar",
  5189  					},
  5190  					{
  5191  						Name:      "baz",
  5192  						PortLabel: "baz",
  5193  					},
  5194  				},
  5195  			},
  5196  			New: &Task{
  5197  				Services: []*Service{
  5198  					{
  5199  						Name:      "bar",
  5200  						PortLabel: "bar",
  5201  					},
  5202  					{
  5203  						Name:      "baz",
  5204  						PortLabel: "baz2",
  5205  					},
  5206  					{
  5207  						Name:      "bam",
  5208  						PortLabel: "bam",
  5209  					},
  5210  				},
  5211  			},
  5212  			Expected: &TaskDiff{
  5213  				Type: DiffTypeEdited,
  5214  				Objects: []*ObjectDiff{
  5215  					{
  5216  						Type: DiffTypeEdited,
  5217  						Name: "Service",
  5218  						Fields: []*FieldDiff{
  5219  							{
  5220  								Type: DiffTypeEdited,
  5221  								Name: "PortLabel",
  5222  								Old:  "baz",
  5223  								New:  "baz2",
  5224  							},
  5225  						},
  5226  					},
  5227  					{
  5228  						Type: DiffTypeAdded,
  5229  						Name: "Service",
  5230  						Fields: []*FieldDiff{
  5231  							{
  5232  								Type: DiffTypeAdded,
  5233  								Name: "EnableTagOverride",
  5234  								Old:  "",
  5235  								New:  "false",
  5236  							},
  5237  							{
  5238  								Type: DiffTypeAdded,
  5239  								Name: "Name",
  5240  								Old:  "",
  5241  								New:  "bam",
  5242  							},
  5243  							{
  5244  								Type: DiffTypeAdded,
  5245  								Name: "PortLabel",
  5246  								Old:  "",
  5247  								New:  "bam",
  5248  							},
  5249  						},
  5250  					},
  5251  					{
  5252  						Type: DiffTypeDeleted,
  5253  						Name: "Service",
  5254  						Fields: []*FieldDiff{
  5255  							{
  5256  								Type: DiffTypeDeleted,
  5257  								Name: "EnableTagOverride",
  5258  								Old:  "false",
  5259  								New:  "",
  5260  							},
  5261  							{
  5262  								Type: DiffTypeDeleted,
  5263  								Name: "Name",
  5264  								Old:  "foo",
  5265  								New:  "",
  5266  							},
  5267  							{
  5268  								Type: DiffTypeDeleted,
  5269  								Name: "PortLabel",
  5270  								Old:  "foo",
  5271  								New:  "",
  5272  							},
  5273  						},
  5274  					},
  5275  				},
  5276  			},
  5277  		},
  5278  		{
  5279  			Name:       "Services edited (no checks) with context",
  5280  			Contextual: true,
  5281  			Old: &Task{
  5282  				Services: []*Service{
  5283  					{
  5284  						Name:      "foo",
  5285  						PortLabel: "foo",
  5286  					},
  5287  				},
  5288  			},
  5289  			New: &Task{
  5290  				Services: []*Service{
  5291  					{
  5292  						Name:        "foo",
  5293  						PortLabel:   "bar",
  5294  						AddressMode: "driver",
  5295  						TaskName:    "task1",
  5296  					},
  5297  				},
  5298  			},
  5299  			Expected: &TaskDiff{
  5300  				Type: DiffTypeEdited,
  5301  				Objects: []*ObjectDiff{
  5302  					{
  5303  						Type: DiffTypeEdited,
  5304  						Name: "Service",
  5305  						Fields: []*FieldDiff{
  5306  							{
  5307  								Type: DiffTypeAdded,
  5308  								Name: "AddressMode",
  5309  								Old:  "",
  5310  								New:  "driver",
  5311  							},
  5312  							{
  5313  								Type: DiffTypeNone,
  5314  								Name: "EnableTagOverride",
  5315  								Old:  "false",
  5316  								New:  "false",
  5317  							},
  5318  							{
  5319  								Type: DiffTypeNone,
  5320  								Name: "Name",
  5321  								Old:  "foo",
  5322  								New:  "foo",
  5323  							},
  5324  							{
  5325  								Type: DiffTypeNone,
  5326  								Name: "OnUpdate",
  5327  								Old:  "",
  5328  								New:  "",
  5329  							},
  5330  							{
  5331  								Type: DiffTypeEdited,
  5332  								Name: "PortLabel",
  5333  								Old:  "foo",
  5334  								New:  "bar",
  5335  							},
  5336  							{
  5337  								Type: DiffTypeAdded,
  5338  								Name: "TaskName",
  5339  								Old:  "",
  5340  								New:  "task1",
  5341  							},
  5342  						},
  5343  					},
  5344  				},
  5345  			},
  5346  		},
  5347  		{
  5348  			Name:       "Service EnableTagOverride edited no context",
  5349  			Contextual: false,
  5350  			Old: &Task{
  5351  				Services: []*Service{{
  5352  					EnableTagOverride: false,
  5353  				}},
  5354  			},
  5355  			New: &Task{
  5356  				Services: []*Service{{
  5357  					EnableTagOverride: true,
  5358  				}},
  5359  			},
  5360  			Expected: &TaskDiff{
  5361  				Type: DiffTypeEdited,
  5362  				Objects: []*ObjectDiff{
  5363  					{
  5364  						Type: DiffTypeEdited,
  5365  						Name: "Service",
  5366  						Fields: []*FieldDiff{
  5367  							{
  5368  								Type: DiffTypeEdited,
  5369  								Name: "EnableTagOverride",
  5370  								Old:  "false",
  5371  								New:  "true",
  5372  							},
  5373  						},
  5374  					},
  5375  				},
  5376  			},
  5377  		},
  5378  		{
  5379  			Name:       "Services tags edited (no checks) with context",
  5380  			Contextual: true,
  5381  			Old: &Task{
  5382  				Services: []*Service{
  5383  					{
  5384  						Tags:       []string{"foo", "bar"},
  5385  						CanaryTags: []string{"foo", "bar"},
  5386  					},
  5387  				},
  5388  			},
  5389  			New: &Task{
  5390  				Services: []*Service{
  5391  					{
  5392  						Tags:       []string{"bar", "bam"},
  5393  						CanaryTags: []string{"bar", "bam"},
  5394  					},
  5395  				},
  5396  			},
  5397  			Expected: &TaskDiff{
  5398  				Type: DiffTypeEdited,
  5399  				Objects: []*ObjectDiff{
  5400  					{
  5401  						Type: DiffTypeEdited,
  5402  						Name: "Service",
  5403  						Objects: []*ObjectDiff{
  5404  							{
  5405  								Type: DiffTypeEdited,
  5406  								Name: "CanaryTags",
  5407  								Fields: []*FieldDiff{
  5408  									{
  5409  										Type: DiffTypeAdded,
  5410  										Name: "CanaryTags",
  5411  										Old:  "",
  5412  										New:  "bam",
  5413  									},
  5414  									{
  5415  										Type: DiffTypeNone,
  5416  										Name: "CanaryTags",
  5417  										Old:  "bar",
  5418  										New:  "bar",
  5419  									},
  5420  									{
  5421  										Type: DiffTypeDeleted,
  5422  										Name: "CanaryTags",
  5423  										Old:  "foo",
  5424  										New:  "",
  5425  									},
  5426  								},
  5427  							},
  5428  							{
  5429  								Type: DiffTypeEdited,
  5430  								Name: "Tags",
  5431  								Fields: []*FieldDiff{
  5432  									{
  5433  										Type: DiffTypeAdded,
  5434  										Name: "Tags",
  5435  										Old:  "",
  5436  										New:  "bam",
  5437  									},
  5438  									{
  5439  										Type: DiffTypeNone,
  5440  										Name: "Tags",
  5441  										Old:  "bar",
  5442  										New:  "bar",
  5443  									},
  5444  									{
  5445  										Type: DiffTypeDeleted,
  5446  										Name: "Tags",
  5447  										Old:  "foo",
  5448  										New:  "",
  5449  									},
  5450  								},
  5451  							},
  5452  						},
  5453  						Fields: []*FieldDiff{
  5454  							{
  5455  								Type: DiffTypeNone,
  5456  								Name: "AddressMode",
  5457  							},
  5458  							{
  5459  								Type: DiffTypeNone,
  5460  								Name: "EnableTagOverride",
  5461  								Old:  "false",
  5462  								New:  "false",
  5463  							},
  5464  							{
  5465  								Type: DiffTypeNone,
  5466  								Name: "Name",
  5467  							},
  5468  							{
  5469  								Type: DiffTypeNone,
  5470  								Name: "OnUpdate",
  5471  							},
  5472  							{
  5473  								Type: DiffTypeNone,
  5474  								Name: "PortLabel",
  5475  							},
  5476  							{
  5477  								Type: DiffTypeNone,
  5478  								Name: "TaskName",
  5479  							},
  5480  						},
  5481  					},
  5482  				},
  5483  			},
  5484  		},
  5485  
  5486  		{
  5487  			Name: "Service with Connect",
  5488  			Old: &Task{
  5489  				Services: []*Service{
  5490  					{
  5491  						Name: "foo",
  5492  					},
  5493  				},
  5494  			},
  5495  			New: &Task{
  5496  				Services: []*Service{
  5497  					{
  5498  						Name: "foo",
  5499  						Connect: &ConsulConnect{
  5500  							SidecarService: &ConsulSidecarService{},
  5501  						},
  5502  					},
  5503  				},
  5504  			},
  5505  			Expected: &TaskDiff{
  5506  				Type: DiffTypeEdited,
  5507  				Objects: []*ObjectDiff{
  5508  					{
  5509  						Type: DiffTypeEdited,
  5510  						Name: "Service",
  5511  						Objects: []*ObjectDiff{
  5512  							{
  5513  								Type: DiffTypeAdded,
  5514  								Name: "ConsulConnect",
  5515  								Fields: []*FieldDiff{
  5516  									{
  5517  										Type: DiffTypeAdded,
  5518  										Name: "Native",
  5519  										Old:  "",
  5520  										New:  "false",
  5521  									},
  5522  								},
  5523  								Objects: []*ObjectDiff{
  5524  									{
  5525  										Type: DiffTypeAdded,
  5526  										Name: "SidecarService",
  5527  									},
  5528  								},
  5529  							},
  5530  						},
  5531  					},
  5532  				},
  5533  			},
  5534  		},
  5535  
  5536  		{
  5537  			Name: "Service with Connect Native",
  5538  			Old: &Task{
  5539  				Services: []*Service{
  5540  					{
  5541  						Name: "foo",
  5542  					},
  5543  				},
  5544  			},
  5545  			New: &Task{
  5546  				Services: []*Service{
  5547  					{
  5548  						Name:     "foo",
  5549  						TaskName: "task1",
  5550  						Connect: &ConsulConnect{
  5551  							Native: true,
  5552  						},
  5553  					},
  5554  				},
  5555  			},
  5556  			Expected: &TaskDiff{
  5557  				Type: DiffTypeEdited,
  5558  				Objects: []*ObjectDiff{
  5559  					{
  5560  						Type: DiffTypeEdited,
  5561  						Name: "Service",
  5562  						Fields: []*FieldDiff{
  5563  							{
  5564  								Type: DiffTypeAdded,
  5565  								Name: "TaskName",
  5566  								Old:  "",
  5567  								New:  "task1",
  5568  							},
  5569  						},
  5570  						Objects: []*ObjectDiff{
  5571  							{
  5572  								Type: DiffTypeAdded,
  5573  								Name: "ConsulConnect",
  5574  								Fields: []*FieldDiff{
  5575  									{
  5576  										Type: DiffTypeAdded,
  5577  										Name: "Native",
  5578  										Old:  "",
  5579  										New:  "true",
  5580  									},
  5581  								},
  5582  							},
  5583  						},
  5584  					},
  5585  				},
  5586  			},
  5587  		},
  5588  
  5589  		{
  5590  			Name: "Service Checks edited",
  5591  			Old: &Task{
  5592  				Services: []*Service{
  5593  					{
  5594  						Name: "foo",
  5595  						Checks: []*ServiceCheck{
  5596  							{
  5597  								Name:     "foo",
  5598  								Type:     "http",
  5599  								Command:  "foo",
  5600  								Args:     []string{"foo"},
  5601  								Path:     "foo",
  5602  								Protocol: "http",
  5603  								Interval: 1 * time.Second,
  5604  								Timeout:  1 * time.Second,
  5605  								Header: map[string][]string{
  5606  									"Foo": {"bar"},
  5607  								},
  5608  								SuccessBeforePassing:   1,
  5609  								FailuresBeforeCritical: 1,
  5610  							},
  5611  							{
  5612  								Name:                   "bar",
  5613  								Type:                   "http",
  5614  								Command:                "foo",
  5615  								Args:                   []string{"foo"},
  5616  								Path:                   "foo",
  5617  								Protocol:               "http",
  5618  								Interval:               1 * time.Second,
  5619  								Timeout:                1 * time.Second,
  5620  								SuccessBeforePassing:   7,
  5621  								FailuresBeforeCritical: 7,
  5622  							},
  5623  							{
  5624  								Name:     "baz",
  5625  								Type:     "http",
  5626  								Command:  "foo",
  5627  								Args:     []string{"foo"},
  5628  								Path:     "foo",
  5629  								Protocol: "http",
  5630  								Interval: 1 * time.Second,
  5631  								Timeout:  1 * time.Second,
  5632  							},
  5633  						},
  5634  					},
  5635  				},
  5636  			},
  5637  			New: &Task{
  5638  				Services: []*Service{
  5639  					{
  5640  						Name: "foo",
  5641  						Checks: []*ServiceCheck{
  5642  							{
  5643  								Name:                   "bar",
  5644  								Type:                   "http",
  5645  								Command:                "foo",
  5646  								Args:                   []string{"foo"},
  5647  								Path:                   "foo",
  5648  								Protocol:               "http",
  5649  								Interval:               1 * time.Second,
  5650  								Timeout:                1 * time.Second,
  5651  								SuccessBeforePassing:   7,
  5652  								FailuresBeforeCritical: 7,
  5653  							},
  5654  							{
  5655  								Name:     "baz",
  5656  								Type:     "tcp",
  5657  								Command:  "foo",
  5658  								Args:     []string{"foo"},
  5659  								Path:     "foo",
  5660  								Protocol: "http",
  5661  								Interval: 1 * time.Second,
  5662  								Timeout:  1 * time.Second,
  5663  								Header: map[string][]string{
  5664  									"Eggs": {"spam"},
  5665  								},
  5666  							},
  5667  							{
  5668  								Name:                   "bam",
  5669  								Type:                   "http",
  5670  								Command:                "foo",
  5671  								Args:                   []string{"foo"},
  5672  								Path:                   "foo",
  5673  								Protocol:               "http",
  5674  								Interval:               1 * time.Second,
  5675  								Timeout:                1 * time.Second,
  5676  								SuccessBeforePassing:   2,
  5677  								FailuresBeforeCritical: 2,
  5678  							},
  5679  						},
  5680  					},
  5681  				},
  5682  			},
  5683  			Expected: &TaskDiff{
  5684  				Type: DiffTypeEdited,
  5685  				Objects: []*ObjectDiff{
  5686  					{
  5687  						Type: DiffTypeEdited,
  5688  						Name: "Service",
  5689  						Objects: []*ObjectDiff{
  5690  							{
  5691  								Type: DiffTypeEdited,
  5692  								Name: "Check",
  5693  								Fields: []*FieldDiff{
  5694  									{
  5695  										Type: DiffTypeEdited,
  5696  										Name: "Type",
  5697  										Old:  "http",
  5698  										New:  "tcp",
  5699  									},
  5700  								},
  5701  								Objects: []*ObjectDiff{
  5702  									{
  5703  										Type: DiffTypeAdded,
  5704  										Name: "Header",
  5705  										Fields: []*FieldDiff{
  5706  											{
  5707  												Type: DiffTypeAdded,
  5708  												Name: "Eggs[0]",
  5709  												Old:  "",
  5710  												New:  "spam",
  5711  											},
  5712  										},
  5713  									},
  5714  								},
  5715  							},
  5716  							{
  5717  								Type: DiffTypeAdded,
  5718  								Name: "Check",
  5719  								Fields: []*FieldDiff{
  5720  									{
  5721  										Type: DiffTypeAdded,
  5722  										Name: "Command",
  5723  										Old:  "",
  5724  										New:  "foo",
  5725  									},
  5726  									{
  5727  										Type: DiffTypeAdded,
  5728  										Name: "Expose",
  5729  										Old:  "",
  5730  										New:  "false",
  5731  									},
  5732  									{
  5733  										Type: DiffTypeAdded,
  5734  										Name: "FailuresBeforeCritical",
  5735  										Old:  "",
  5736  										New:  "2",
  5737  									},
  5738  									{
  5739  										Type: DiffTypeAdded,
  5740  										Name: "GRPCUseTLS",
  5741  										Old:  "",
  5742  										New:  "false",
  5743  									},
  5744  									{
  5745  										Type: DiffTypeAdded,
  5746  										Name: "Interval",
  5747  										Old:  "",
  5748  										New:  "1000000000",
  5749  									},
  5750  									{
  5751  										Type: DiffTypeAdded,
  5752  										Name: "Name",
  5753  										Old:  "",
  5754  										New:  "bam",
  5755  									},
  5756  									{
  5757  										Type: DiffTypeAdded,
  5758  										Name: "Path",
  5759  										Old:  "",
  5760  										New:  "foo",
  5761  									},
  5762  									{
  5763  										Type: DiffTypeAdded,
  5764  										Name: "Protocol",
  5765  										Old:  "",
  5766  										New:  "http",
  5767  									},
  5768  									{
  5769  										Type: DiffTypeAdded,
  5770  										Name: "SuccessBeforePassing",
  5771  										Old:  "",
  5772  										New:  "2",
  5773  									},
  5774  									{
  5775  										Type: DiffTypeAdded,
  5776  										Name: "TLSSkipVerify",
  5777  										Old:  "",
  5778  										New:  "false",
  5779  									},
  5780  									{
  5781  										Type: DiffTypeAdded,
  5782  										Name: "Timeout",
  5783  										Old:  "",
  5784  										New:  "1000000000",
  5785  									},
  5786  									{
  5787  										Type: DiffTypeAdded,
  5788  										Name: "Type",
  5789  										Old:  "",
  5790  										New:  "http",
  5791  									},
  5792  								},
  5793  							},
  5794  							{
  5795  								Type: DiffTypeDeleted,
  5796  								Name: "Check",
  5797  								Fields: []*FieldDiff{
  5798  									{
  5799  										Type: DiffTypeDeleted,
  5800  										Name: "Command",
  5801  										Old:  "foo",
  5802  										New:  "",
  5803  									},
  5804  									{
  5805  										Type: DiffTypeDeleted,
  5806  										Name: "Expose",
  5807  										Old:  "false",
  5808  										New:  "",
  5809  									},
  5810  									{
  5811  										Type: DiffTypeDeleted,
  5812  										Name: "FailuresBeforeCritical",
  5813  										Old:  "1",
  5814  										New:  "",
  5815  									},
  5816  									{
  5817  										Type: DiffTypeDeleted,
  5818  										Name: "GRPCUseTLS",
  5819  										Old:  "false",
  5820  										New:  "",
  5821  									},
  5822  									{
  5823  										Type: DiffTypeDeleted,
  5824  										Name: "Interval",
  5825  										Old:  "1000000000",
  5826  										New:  "",
  5827  									},
  5828  									{
  5829  										Type: DiffTypeDeleted,
  5830  										Name: "Name",
  5831  										Old:  "foo",
  5832  										New:  "",
  5833  									},
  5834  									{
  5835  										Type: DiffTypeDeleted,
  5836  										Name: "Path",
  5837  										Old:  "foo",
  5838  										New:  "",
  5839  									},
  5840  									{
  5841  										Type: DiffTypeDeleted,
  5842  										Name: "Protocol",
  5843  										Old:  "http",
  5844  										New:  "",
  5845  									},
  5846  									{
  5847  										Type: DiffTypeDeleted,
  5848  										Name: "SuccessBeforePassing",
  5849  										Old:  "1",
  5850  										New:  "",
  5851  									},
  5852  									{
  5853  										Type: DiffTypeDeleted,
  5854  										Name: "TLSSkipVerify",
  5855  										Old:  "false",
  5856  										New:  "",
  5857  									},
  5858  									{
  5859  										Type: DiffTypeDeleted,
  5860  										Name: "Timeout",
  5861  										Old:  "1000000000",
  5862  										New:  "",
  5863  									},
  5864  									{
  5865  										Type: DiffTypeDeleted,
  5866  										Name: "Type",
  5867  										Old:  "http",
  5868  										New:  "",
  5869  									},
  5870  								},
  5871  								Objects: []*ObjectDiff{
  5872  									{
  5873  										Type: DiffTypeDeleted,
  5874  										Name: "Header",
  5875  										Fields: []*FieldDiff{
  5876  											{
  5877  												Type: DiffTypeDeleted,
  5878  												Name: "Foo[0]",
  5879  												Old:  "bar",
  5880  											},
  5881  										},
  5882  									},
  5883  								},
  5884  							},
  5885  						},
  5886  					},
  5887  				},
  5888  			},
  5889  		},
  5890  		{
  5891  			Name:       "Service Checks edited with context",
  5892  			Contextual: true,
  5893  			Old: &Task{
  5894  				Services: []*Service{
  5895  					{
  5896  						Name: "foo",
  5897  						Checks: []*ServiceCheck{
  5898  							{
  5899  								Name:          "foo",
  5900  								Type:          "http",
  5901  								Command:       "foo",
  5902  								Args:          []string{"foo"},
  5903  								Path:          "foo",
  5904  								Protocol:      "http",
  5905  								Interval:      1 * time.Second,
  5906  								Timeout:       1 * time.Second,
  5907  								InitialStatus: "critical",
  5908  								Header: map[string][]string{
  5909  									"Foo": {"bar"},
  5910  								},
  5911  								SuccessBeforePassing:   4,
  5912  								FailuresBeforeCritical: 5,
  5913  								OnUpdate:               "require_healthy",
  5914  							},
  5915  						},
  5916  					},
  5917  				},
  5918  			},
  5919  			New: &Task{
  5920  				Services: []*Service{
  5921  					{
  5922  						Name: "foo",
  5923  						Checks: []*ServiceCheck{
  5924  							{
  5925  								Name:          "foo",
  5926  								Type:          "tcp",
  5927  								Command:       "foo",
  5928  								Args:          []string{"foo"},
  5929  								Path:          "foo",
  5930  								Protocol:      "http",
  5931  								Interval:      1 * time.Second,
  5932  								Timeout:       1 * time.Second,
  5933  								InitialStatus: "passing",
  5934  								Method:        "POST",
  5935  								Header: map[string][]string{
  5936  									"Foo":  {"bar", "baz"},
  5937  									"Eggs": {"spam"},
  5938  								},
  5939  								SuccessBeforePassing: 4,
  5940  								OnUpdate:             "ignore_warnings",
  5941  							},
  5942  						},
  5943  					},
  5944  				},
  5945  			},
  5946  			Expected: &TaskDiff{
  5947  				Type: DiffTypeEdited,
  5948  				Objects: []*ObjectDiff{
  5949  					{
  5950  						Type: DiffTypeEdited,
  5951  						Name: "Service",
  5952  						Fields: []*FieldDiff{
  5953  							{
  5954  								Type: DiffTypeNone,
  5955  								Name: "AddressMode",
  5956  								Old:  "",
  5957  								New:  "",
  5958  							},
  5959  							{
  5960  								Type: DiffTypeNone,
  5961  								Name: "EnableTagOverride",
  5962  								Old:  "false",
  5963  								New:  "false",
  5964  							},
  5965  							{
  5966  								Type: DiffTypeNone,
  5967  								Name: "Name",
  5968  								Old:  "foo",
  5969  								New:  "foo",
  5970  							},
  5971  							{
  5972  								Type: DiffTypeNone,
  5973  								Name: "OnUpdate",
  5974  							},
  5975  							{
  5976  								Type: DiffTypeNone,
  5977  								Name: "PortLabel",
  5978  								Old:  "",
  5979  								New:  "",
  5980  							},
  5981  							{
  5982  								Type: DiffTypeNone,
  5983  								Name: "TaskName",
  5984  								Old:  "",
  5985  								New:  "",
  5986  							},
  5987  						},
  5988  						Objects: []*ObjectDiff{
  5989  							{
  5990  								Type: DiffTypeEdited,
  5991  								Name: "Check",
  5992  								Fields: []*FieldDiff{
  5993  									{
  5994  										Type: DiffTypeNone,
  5995  										Name: "AddressMode",
  5996  										Old:  "",
  5997  										New:  "",
  5998  									},
  5999  									{
  6000  										Type: DiffTypeNone,
  6001  										Name: "Command",
  6002  										Old:  "foo",
  6003  										New:  "foo",
  6004  									},
  6005  									{
  6006  										Type: DiffTypeNone,
  6007  										Name: "Expose",
  6008  										Old:  "false",
  6009  										New:  "false",
  6010  									},
  6011  									{
  6012  										Type: DiffTypeEdited,
  6013  										Name: "FailuresBeforeCritical",
  6014  										Old:  "5",
  6015  										New:  "0",
  6016  									},
  6017  									{
  6018  										Type: DiffTypeNone,
  6019  										Name: "GRPCService",
  6020  										Old:  "",
  6021  										New:  "",
  6022  									},
  6023  									{
  6024  										Type: DiffTypeNone,
  6025  										Name: "GRPCUseTLS",
  6026  										Old:  "false",
  6027  										New:  "false",
  6028  									},
  6029  									{
  6030  										Type: DiffTypeEdited,
  6031  										Name: "InitialStatus",
  6032  										Old:  "critical",
  6033  										New:  "passing",
  6034  									},
  6035  									{
  6036  										Type: DiffTypeNone,
  6037  										Name: "Interval",
  6038  										Old:  "1000000000",
  6039  										New:  "1000000000",
  6040  									},
  6041  									{
  6042  										Type: DiffTypeAdded,
  6043  										Name: "Method",
  6044  										Old:  "",
  6045  										New:  "POST",
  6046  									},
  6047  									{
  6048  										Type: DiffTypeNone,
  6049  										Name: "Name",
  6050  										Old:  "foo",
  6051  										New:  "foo",
  6052  									},
  6053  									{
  6054  										Type: DiffTypeEdited,
  6055  										Name: "OnUpdate",
  6056  										Old:  "require_healthy",
  6057  										New:  "ignore_warnings",
  6058  									},
  6059  									{
  6060  										Type: DiffTypeNone,
  6061  										Name: "Path",
  6062  										Old:  "foo",
  6063  										New:  "foo",
  6064  									},
  6065  									{
  6066  										Type: DiffTypeNone,
  6067  										Name: "PortLabel",
  6068  										Old:  "",
  6069  										New:  "",
  6070  									},
  6071  									{
  6072  										Type: DiffTypeNone,
  6073  										Name: "Protocol",
  6074  										Old:  "http",
  6075  										New:  "http",
  6076  									},
  6077  									{
  6078  										Type: DiffTypeNone,
  6079  										Name: "SuccessBeforePassing",
  6080  										Old:  "4",
  6081  										New:  "4",
  6082  									},
  6083  									{
  6084  										Type: DiffTypeNone,
  6085  										Name: "TLSSkipVerify",
  6086  										Old:  "false",
  6087  										New:  "false",
  6088  									},
  6089  									{
  6090  										Type: DiffTypeNone,
  6091  										Name: "TaskName",
  6092  										Old:  "",
  6093  										New:  "",
  6094  									},
  6095  									{
  6096  										Type: DiffTypeNone,
  6097  										Name: "Timeout",
  6098  										Old:  "1000000000",
  6099  										New:  "1000000000",
  6100  									},
  6101  									{
  6102  										Type: DiffTypeEdited,
  6103  										Name: "Type",
  6104  										Old:  "http",
  6105  										New:  "tcp",
  6106  									},
  6107  								},
  6108  								Objects: []*ObjectDiff{
  6109  									{
  6110  										Type: DiffTypeEdited,
  6111  										Name: "Header",
  6112  										Fields: []*FieldDiff{
  6113  											{
  6114  												Type: DiffTypeAdded,
  6115  												Name: "Eggs[0]",
  6116  												Old:  "",
  6117  												New:  "spam",
  6118  											},
  6119  											{
  6120  												Type: DiffTypeNone,
  6121  												Name: "Foo[0]",
  6122  												Old:  "bar",
  6123  												New:  "bar",
  6124  											},
  6125  											{
  6126  												Type: DiffTypeAdded,
  6127  												Name: "Foo[1]",
  6128  												Old:  "",
  6129  												New:  "baz",
  6130  											},
  6131  										},
  6132  									},
  6133  								},
  6134  							},
  6135  						},
  6136  					},
  6137  				},
  6138  			},
  6139  		},
  6140  		{
  6141  			Name: "CheckRestart edited",
  6142  			Old: &Task{
  6143  				Services: []*Service{
  6144  					{
  6145  						Name: "foo",
  6146  						Checks: []*ServiceCheck{
  6147  							{
  6148  								Name:     "foo",
  6149  								Type:     "http",
  6150  								Command:  "foo",
  6151  								Args:     []string{"foo"},
  6152  								Path:     "foo",
  6153  								Protocol: "http",
  6154  								Interval: 1 * time.Second,
  6155  								Timeout:  1 * time.Second,
  6156  							},
  6157  							{
  6158  								Name:     "bar",
  6159  								Type:     "http",
  6160  								Command:  "foo",
  6161  								Args:     []string{"foo"},
  6162  								Path:     "foo",
  6163  								Protocol: "http",
  6164  								Interval: 1 * time.Second,
  6165  								Timeout:  1 * time.Second,
  6166  								CheckRestart: &CheckRestart{
  6167  									Limit: 2,
  6168  									Grace: 2 * time.Second,
  6169  								},
  6170  							},
  6171  							{
  6172  								Name:     "baz",
  6173  								Type:     "http",
  6174  								Command:  "foo",
  6175  								Args:     []string{"foo"},
  6176  								Path:     "foo",
  6177  								Protocol: "http",
  6178  								Interval: 1 * time.Second,
  6179  								Timeout:  1 * time.Second,
  6180  								CheckRestart: &CheckRestart{
  6181  									Limit: 3,
  6182  									Grace: 3 * time.Second,
  6183  								},
  6184  							},
  6185  						},
  6186  					},
  6187  				},
  6188  			},
  6189  			New: &Task{
  6190  				Services: []*Service{
  6191  					{
  6192  						Name: "foo",
  6193  						Checks: []*ServiceCheck{
  6194  							{
  6195  								Name:     "foo",
  6196  								Type:     "http",
  6197  								Command:  "foo",
  6198  								Args:     []string{"foo"},
  6199  								Path:     "foo",
  6200  								Protocol: "http",
  6201  								Interval: 1 * time.Second,
  6202  								Timeout:  1 * time.Second,
  6203  								CheckRestart: &CheckRestart{
  6204  									Limit: 1,
  6205  									Grace: 1 * time.Second,
  6206  								},
  6207  							},
  6208  							{
  6209  								Name:     "bar",
  6210  								Type:     "http",
  6211  								Command:  "foo",
  6212  								Args:     []string{"foo"},
  6213  								Path:     "foo",
  6214  								Protocol: "http",
  6215  								Interval: 1 * time.Second,
  6216  								Timeout:  1 * time.Second,
  6217  							},
  6218  							{
  6219  								Name:     "baz",
  6220  								Type:     "http",
  6221  								Command:  "foo",
  6222  								Args:     []string{"foo"},
  6223  								Path:     "foo",
  6224  								Protocol: "http",
  6225  								Interval: 1 * time.Second,
  6226  								Timeout:  1 * time.Second,
  6227  								CheckRestart: &CheckRestart{
  6228  									Limit: 4,
  6229  									Grace: 4 * time.Second,
  6230  								},
  6231  							},
  6232  						},
  6233  					},
  6234  				},
  6235  			},
  6236  			Expected: &TaskDiff{
  6237  				Type: DiffTypeEdited,
  6238  				Objects: []*ObjectDiff{
  6239  					{
  6240  						Type: DiffTypeEdited,
  6241  						Name: "Service",
  6242  						Objects: []*ObjectDiff{
  6243  							{
  6244  								Type: DiffTypeEdited,
  6245  								Name: "Check",
  6246  								Objects: []*ObjectDiff{
  6247  									{
  6248  										Type: DiffTypeEdited,
  6249  										Name: "CheckRestart",
  6250  										Fields: []*FieldDiff{
  6251  											{
  6252  												Type: DiffTypeEdited,
  6253  												Name: "Grace",
  6254  												Old:  "3000000000",
  6255  												New:  "4000000000",
  6256  											},
  6257  											{
  6258  												Type: DiffTypeEdited,
  6259  												Name: "Limit",
  6260  												Old:  "3",
  6261  												New:  "4",
  6262  											},
  6263  										},
  6264  									},
  6265  								},
  6266  							},
  6267  							{
  6268  								Type: DiffTypeEdited,
  6269  								Name: "Check",
  6270  								Objects: []*ObjectDiff{
  6271  									{
  6272  										Type: DiffTypeAdded,
  6273  										Name: "CheckRestart",
  6274  										Fields: []*FieldDiff{
  6275  											{
  6276  												Type: DiffTypeAdded,
  6277  												Name: "Grace",
  6278  												New:  "1000000000",
  6279  											},
  6280  											{
  6281  												Type: DiffTypeAdded,
  6282  												Name: "IgnoreWarnings",
  6283  												New:  "false",
  6284  											},
  6285  											{
  6286  												Type: DiffTypeAdded,
  6287  												Name: "Limit",
  6288  												New:  "1",
  6289  											},
  6290  										},
  6291  									},
  6292  								},
  6293  							},
  6294  							{
  6295  								Type: DiffTypeEdited,
  6296  								Name: "Check",
  6297  								Objects: []*ObjectDiff{
  6298  									{
  6299  										Type: DiffTypeDeleted,
  6300  										Name: "CheckRestart",
  6301  										Fields: []*FieldDiff{
  6302  											{
  6303  												Type: DiffTypeDeleted,
  6304  												Name: "Grace",
  6305  												Old:  "2000000000",
  6306  											},
  6307  											{
  6308  												Type: DiffTypeDeleted,
  6309  												Name: "IgnoreWarnings",
  6310  												Old:  "false",
  6311  											},
  6312  											{
  6313  												Type: DiffTypeDeleted,
  6314  												Name: "Limit",
  6315  												Old:  "2",
  6316  											},
  6317  										},
  6318  									},
  6319  								},
  6320  							},
  6321  						},
  6322  					},
  6323  				},
  6324  			},
  6325  		},
  6326  		{
  6327  			Name: "Vault added",
  6328  			Old:  &Task{},
  6329  			New: &Task{
  6330  				Vault: &Vault{
  6331  					Policies:     []string{"foo", "bar"},
  6332  					Env:          true,
  6333  					ChangeMode:   "signal",
  6334  					ChangeSignal: "SIGUSR1",
  6335  				},
  6336  			},
  6337  			Expected: &TaskDiff{
  6338  				Type: DiffTypeEdited,
  6339  				Objects: []*ObjectDiff{
  6340  					{
  6341  						Type: DiffTypeAdded,
  6342  						Name: "Vault",
  6343  						Fields: []*FieldDiff{
  6344  							{
  6345  								Type: DiffTypeAdded,
  6346  								Name: "ChangeMode",
  6347  								Old:  "",
  6348  								New:  "signal",
  6349  							},
  6350  							{
  6351  								Type: DiffTypeAdded,
  6352  								Name: "ChangeSignal",
  6353  								Old:  "",
  6354  								New:  "SIGUSR1",
  6355  							},
  6356  							{
  6357  								Type: DiffTypeAdded,
  6358  								Name: "Env",
  6359  								Old:  "",
  6360  								New:  "true",
  6361  							},
  6362  						},
  6363  						Objects: []*ObjectDiff{
  6364  							{
  6365  								Type: DiffTypeAdded,
  6366  								Name: "Policies",
  6367  								Fields: []*FieldDiff{
  6368  									{
  6369  										Type: DiffTypeAdded,
  6370  										Name: "Policies",
  6371  										Old:  "",
  6372  										New:  "bar",
  6373  									},
  6374  									{
  6375  										Type: DiffTypeAdded,
  6376  										Name: "Policies",
  6377  										Old:  "",
  6378  										New:  "foo",
  6379  									},
  6380  								},
  6381  							},
  6382  						},
  6383  					},
  6384  				},
  6385  			},
  6386  		},
  6387  		{
  6388  			Name: "Vault deleted",
  6389  			Old: &Task{
  6390  				Vault: &Vault{
  6391  					Policies:     []string{"foo", "bar"},
  6392  					Env:          true,
  6393  					ChangeMode:   "signal",
  6394  					ChangeSignal: "SIGUSR1",
  6395  				},
  6396  			},
  6397  			New: &Task{},
  6398  			Expected: &TaskDiff{
  6399  				Type: DiffTypeEdited,
  6400  				Objects: []*ObjectDiff{
  6401  					{
  6402  						Type: DiffTypeDeleted,
  6403  						Name: "Vault",
  6404  						Fields: []*FieldDiff{
  6405  							{
  6406  								Type: DiffTypeDeleted,
  6407  								Name: "ChangeMode",
  6408  								Old:  "signal",
  6409  								New:  "",
  6410  							},
  6411  							{
  6412  								Type: DiffTypeDeleted,
  6413  								Name: "ChangeSignal",
  6414  								Old:  "SIGUSR1",
  6415  								New:  "",
  6416  							},
  6417  							{
  6418  								Type: DiffTypeDeleted,
  6419  								Name: "Env",
  6420  								Old:  "true",
  6421  								New:  "",
  6422  							},
  6423  						},
  6424  						Objects: []*ObjectDiff{
  6425  							{
  6426  								Type: DiffTypeDeleted,
  6427  								Name: "Policies",
  6428  								Fields: []*FieldDiff{
  6429  									{
  6430  										Type: DiffTypeDeleted,
  6431  										Name: "Policies",
  6432  										Old:  "bar",
  6433  										New:  "",
  6434  									},
  6435  									{
  6436  										Type: DiffTypeDeleted,
  6437  										Name: "Policies",
  6438  										Old:  "foo",
  6439  										New:  "",
  6440  									},
  6441  								},
  6442  							},
  6443  						},
  6444  					},
  6445  				},
  6446  			},
  6447  		},
  6448  		{
  6449  			Name: "Vault edited",
  6450  			Old: &Task{
  6451  				Vault: &Vault{
  6452  					Namespace:    "ns1",
  6453  					Policies:     []string{"foo", "bar"},
  6454  					Env:          true,
  6455  					ChangeMode:   "signal",
  6456  					ChangeSignal: "SIGUSR1",
  6457  				},
  6458  			},
  6459  			New: &Task{
  6460  				Vault: &Vault{
  6461  					Namespace:    "ns2",
  6462  					Policies:     []string{"bar", "baz"},
  6463  					Env:          false,
  6464  					ChangeMode:   "restart",
  6465  					ChangeSignal: "foo",
  6466  				},
  6467  			},
  6468  			Expected: &TaskDiff{
  6469  				Type: DiffTypeEdited,
  6470  				Objects: []*ObjectDiff{
  6471  					{
  6472  						Type: DiffTypeEdited,
  6473  						Name: "Vault",
  6474  						Fields: []*FieldDiff{
  6475  							{
  6476  								Type: DiffTypeEdited,
  6477  								Name: "ChangeMode",
  6478  								Old:  "signal",
  6479  								New:  "restart",
  6480  							},
  6481  							{
  6482  								Type: DiffTypeEdited,
  6483  								Name: "ChangeSignal",
  6484  								Old:  "SIGUSR1",
  6485  								New:  "foo",
  6486  							},
  6487  							{
  6488  								Type: DiffTypeEdited,
  6489  								Name: "Env",
  6490  								Old:  "true",
  6491  								New:  "false",
  6492  							},
  6493  							{
  6494  								Type: DiffTypeEdited,
  6495  								Name: "Namespace",
  6496  								Old:  "ns1",
  6497  								New:  "ns2",
  6498  							},
  6499  						},
  6500  						Objects: []*ObjectDiff{
  6501  							{
  6502  								Type: DiffTypeEdited,
  6503  								Name: "Policies",
  6504  								Fields: []*FieldDiff{
  6505  									{
  6506  										Type: DiffTypeAdded,
  6507  										Name: "Policies",
  6508  										Old:  "",
  6509  										New:  "baz",
  6510  									},
  6511  									{
  6512  										Type: DiffTypeDeleted,
  6513  										Name: "Policies",
  6514  										Old:  "foo",
  6515  										New:  "",
  6516  									},
  6517  								},
  6518  							},
  6519  						},
  6520  					},
  6521  				},
  6522  			},
  6523  		},
  6524  		{
  6525  			Name:       "Vault edited with context",
  6526  			Contextual: true,
  6527  			Old: &Task{
  6528  				Vault: &Vault{
  6529  					Namespace:    "ns1",
  6530  					Policies:     []string{"foo", "bar"},
  6531  					Env:          true,
  6532  					ChangeMode:   "signal",
  6533  					ChangeSignal: "SIGUSR1",
  6534  				},
  6535  			},
  6536  			New: &Task{
  6537  				Vault: &Vault{
  6538  					Namespace:    "ns1",
  6539  					Policies:     []string{"bar", "baz"},
  6540  					Env:          true,
  6541  					ChangeMode:   "signal",
  6542  					ChangeSignal: "SIGUSR1",
  6543  				},
  6544  			},
  6545  			Expected: &TaskDiff{
  6546  				Type: DiffTypeEdited,
  6547  				Objects: []*ObjectDiff{
  6548  					{
  6549  						Type: DiffTypeEdited,
  6550  						Name: "Vault",
  6551  						Fields: []*FieldDiff{
  6552  							{
  6553  								Type: DiffTypeNone,
  6554  								Name: "ChangeMode",
  6555  								Old:  "signal",
  6556  								New:  "signal",
  6557  							},
  6558  							{
  6559  								Type: DiffTypeNone,
  6560  								Name: "ChangeSignal",
  6561  								Old:  "SIGUSR1",
  6562  								New:  "SIGUSR1",
  6563  							},
  6564  							{
  6565  								Type: DiffTypeNone,
  6566  								Name: "Env",
  6567  								Old:  "true",
  6568  								New:  "true",
  6569  							},
  6570  							{
  6571  								Type: DiffTypeNone,
  6572  								Name: "Namespace",
  6573  								Old:  "ns1",
  6574  								New:  "ns1",
  6575  							},
  6576  						},
  6577  						Objects: []*ObjectDiff{
  6578  							{
  6579  								Type: DiffTypeEdited,
  6580  								Name: "Policies",
  6581  								Fields: []*FieldDiff{
  6582  									{
  6583  										Type: DiffTypeAdded,
  6584  										Name: "Policies",
  6585  										Old:  "",
  6586  										New:  "baz",
  6587  									},
  6588  									{
  6589  										Type: DiffTypeNone,
  6590  										Name: "Policies",
  6591  										Old:  "bar",
  6592  										New:  "bar",
  6593  									},
  6594  									{
  6595  										Type: DiffTypeDeleted,
  6596  										Name: "Policies",
  6597  										Old:  "foo",
  6598  										New:  "",
  6599  									},
  6600  								},
  6601  							},
  6602  						},
  6603  					},
  6604  				},
  6605  			},
  6606  		},
  6607  		{
  6608  			Name: "Template edited",
  6609  			Old: &Task{
  6610  				Templates: []*Template{
  6611  					{
  6612  						SourcePath:   "foo",
  6613  						DestPath:     "bar",
  6614  						EmbeddedTmpl: "baz",
  6615  						ChangeMode:   "bam",
  6616  						ChangeSignal: "SIGHUP",
  6617  						Splay:        1,
  6618  						Perms:        "0644",
  6619  					},
  6620  					{
  6621  						SourcePath:   "foo2",
  6622  						DestPath:     "bar2",
  6623  						EmbeddedTmpl: "baz2",
  6624  						ChangeMode:   "bam2",
  6625  						ChangeSignal: "SIGHUP2",
  6626  						Splay:        2,
  6627  						Perms:        "0666",
  6628  						Envvars:      true,
  6629  					},
  6630  				},
  6631  			},
  6632  			New: &Task{
  6633  				Templates: []*Template{
  6634  					{
  6635  						SourcePath:   "foo",
  6636  						DestPath:     "bar",
  6637  						EmbeddedTmpl: "baz",
  6638  						ChangeMode:   "bam",
  6639  						ChangeSignal: "SIGHUP",
  6640  						Splay:        1,
  6641  						Perms:        "0644",
  6642  					},
  6643  					{
  6644  						SourcePath:   "foo3",
  6645  						DestPath:     "bar3",
  6646  						EmbeddedTmpl: "baz3",
  6647  						ChangeMode:   "bam3",
  6648  						ChangeSignal: "SIGHUP3",
  6649  						Splay:        3,
  6650  						Perms:        "0776",
  6651  					},
  6652  				},
  6653  			},
  6654  			Expected: &TaskDiff{
  6655  				Type: DiffTypeEdited,
  6656  				Objects: []*ObjectDiff{
  6657  					{
  6658  						Type: DiffTypeAdded,
  6659  						Name: "Template",
  6660  						Fields: []*FieldDiff{
  6661  							{
  6662  								Type: DiffTypeAdded,
  6663  								Name: "ChangeMode",
  6664  								Old:  "",
  6665  								New:  "bam3",
  6666  							},
  6667  							{
  6668  								Type: DiffTypeAdded,
  6669  								Name: "ChangeSignal",
  6670  								Old:  "",
  6671  								New:  "SIGHUP3",
  6672  							},
  6673  							{
  6674  								Type: DiffTypeAdded,
  6675  								Name: "DestPath",
  6676  								Old:  "",
  6677  								New:  "bar3",
  6678  							},
  6679  							{
  6680  								Type: DiffTypeAdded,
  6681  								Name: "EmbeddedTmpl",
  6682  								Old:  "",
  6683  								New:  "baz3",
  6684  							},
  6685  							{
  6686  								Type: DiffTypeAdded,
  6687  								Name: "Envvars",
  6688  								Old:  "",
  6689  								New:  "false",
  6690  							},
  6691  							{
  6692  								Type: DiffTypeAdded,
  6693  								Name: "Perms",
  6694  								Old:  "",
  6695  								New:  "0776",
  6696  							},
  6697  							{
  6698  								Type: DiffTypeAdded,
  6699  								Name: "SourcePath",
  6700  								Old:  "",
  6701  								New:  "foo3",
  6702  							},
  6703  							{
  6704  								Type: DiffTypeAdded,
  6705  								Name: "Splay",
  6706  								Old:  "",
  6707  								New:  "3",
  6708  							},
  6709  							{
  6710  								Type: DiffTypeAdded,
  6711  								Name: "VaultGrace",
  6712  								Old:  "",
  6713  								New:  "0",
  6714  							},
  6715  						},
  6716  					},
  6717  					{
  6718  						Type: DiffTypeDeleted,
  6719  						Name: "Template",
  6720  						Fields: []*FieldDiff{
  6721  							{
  6722  								Type: DiffTypeDeleted,
  6723  								Name: "ChangeMode",
  6724  								Old:  "bam2",
  6725  								New:  "",
  6726  							},
  6727  							{
  6728  								Type: DiffTypeDeleted,
  6729  								Name: "ChangeSignal",
  6730  								Old:  "SIGHUP2",
  6731  								New:  "",
  6732  							},
  6733  							{
  6734  								Type: DiffTypeDeleted,
  6735  								Name: "DestPath",
  6736  								Old:  "bar2",
  6737  								New:  "",
  6738  							},
  6739  							{
  6740  								Type: DiffTypeDeleted,
  6741  								Name: "EmbeddedTmpl",
  6742  								Old:  "baz2",
  6743  								New:  "",
  6744  							},
  6745  							{
  6746  								Type: DiffTypeDeleted,
  6747  								Name: "Envvars",
  6748  								Old:  "true",
  6749  								New:  "",
  6750  							},
  6751  							{
  6752  								Type: DiffTypeDeleted,
  6753  								Name: "Perms",
  6754  								Old:  "0666",
  6755  								New:  "",
  6756  							},
  6757  							{
  6758  								Type: DiffTypeDeleted,
  6759  								Name: "SourcePath",
  6760  								Old:  "foo2",
  6761  								New:  "",
  6762  							},
  6763  							{
  6764  								Type: DiffTypeDeleted,
  6765  								Name: "Splay",
  6766  								Old:  "2",
  6767  								New:  "",
  6768  							},
  6769  							{
  6770  								Type: DiffTypeDeleted,
  6771  								Name: "VaultGrace",
  6772  								Old:  "0",
  6773  								New:  "",
  6774  							},
  6775  						},
  6776  					},
  6777  				},
  6778  			},
  6779  		},
  6780  		{
  6781  			Name: "DispatchPayload added",
  6782  			Old:  &Task{},
  6783  			New: &Task{
  6784  				DispatchPayload: &DispatchPayloadConfig{
  6785  					File: "foo",
  6786  				},
  6787  			},
  6788  			Expected: &TaskDiff{
  6789  				Type: DiffTypeEdited,
  6790  				Objects: []*ObjectDiff{
  6791  					{
  6792  						Type: DiffTypeAdded,
  6793  						Name: "DispatchPayload",
  6794  						Fields: []*FieldDiff{
  6795  							{
  6796  								Type: DiffTypeAdded,
  6797  								Name: "File",
  6798  								Old:  "",
  6799  								New:  "foo",
  6800  							},
  6801  						},
  6802  					},
  6803  				},
  6804  			},
  6805  		},
  6806  		{
  6807  			Name: "DispatchPayload deleted",
  6808  			Old: &Task{
  6809  				DispatchPayload: &DispatchPayloadConfig{
  6810  					File: "foo",
  6811  				},
  6812  			},
  6813  			New: &Task{},
  6814  			Expected: &TaskDiff{
  6815  				Type: DiffTypeEdited,
  6816  				Objects: []*ObjectDiff{
  6817  					{
  6818  						Type: DiffTypeDeleted,
  6819  						Name: "DispatchPayload",
  6820  						Fields: []*FieldDiff{
  6821  							{
  6822  								Type: DiffTypeDeleted,
  6823  								Name: "File",
  6824  								Old:  "foo",
  6825  								New:  "",
  6826  							},
  6827  						},
  6828  					},
  6829  				},
  6830  			},
  6831  		},
  6832  		{
  6833  			Name: "Dispatch payload edited",
  6834  			Old: &Task{
  6835  				DispatchPayload: &DispatchPayloadConfig{
  6836  					File: "foo",
  6837  				},
  6838  			},
  6839  			New: &Task{
  6840  				DispatchPayload: &DispatchPayloadConfig{
  6841  					File: "bar",
  6842  				},
  6843  			},
  6844  			Expected: &TaskDiff{
  6845  				Type: DiffTypeEdited,
  6846  				Objects: []*ObjectDiff{
  6847  					{
  6848  						Type: DiffTypeEdited,
  6849  						Name: "DispatchPayload",
  6850  						Fields: []*FieldDiff{
  6851  							{
  6852  								Type: DiffTypeEdited,
  6853  								Name: "File",
  6854  								Old:  "foo",
  6855  								New:  "bar",
  6856  							},
  6857  						},
  6858  					},
  6859  				},
  6860  			},
  6861  		},
  6862  		{
  6863  			// Place holder for if more fields are added
  6864  			Name:       "DispatchPayload edited with context",
  6865  			Contextual: true,
  6866  			Old: &Task{
  6867  				DispatchPayload: &DispatchPayloadConfig{
  6868  					File: "foo",
  6869  				},
  6870  			},
  6871  			New: &Task{
  6872  				DispatchPayload: &DispatchPayloadConfig{
  6873  					File: "bar",
  6874  				},
  6875  			},
  6876  			Expected: &TaskDiff{
  6877  				Type: DiffTypeEdited,
  6878  				Objects: []*ObjectDiff{
  6879  					{
  6880  						Type: DiffTypeEdited,
  6881  						Name: "DispatchPayload",
  6882  						Fields: []*FieldDiff{
  6883  							{
  6884  								Type: DiffTypeEdited,
  6885  								Name: "File",
  6886  								Old:  "foo",
  6887  								New:  "bar",
  6888  							},
  6889  						},
  6890  					},
  6891  				},
  6892  			},
  6893  		},
  6894  	}
  6895  
  6896  	for i, c := range cases {
  6897  		t.Run(c.Name, func(t *testing.T) {
  6898  			actual, err := c.Old.Diff(c.New, c.Contextual)
  6899  			if c.Error && err == nil {
  6900  				t.Fatalf("case %d: expected errored", i+1)
  6901  			} else if err != nil {
  6902  				if !c.Error {
  6903  					t.Fatalf("case %d: errored %#v", i+1, err)
  6904  				} else {
  6905  					return
  6906  				}
  6907  			}
  6908  
  6909  			if !reflect.DeepEqual(actual, c.Expected) {
  6910  				t.Errorf("case %d: got:\n%#v\n want:\n%#v\n",
  6911  					i+1, actual, c.Expected)
  6912  			}
  6913  		})
  6914  	}
  6915  }