github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/structs/diff_test.go (about)

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