github.com/nir0s/nomad@v0.8.7-rc1/command/agent/consul/unit_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  	"sync/atomic"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/hashicorp/consul/api"
    13  	cstructs "github.com/hashicorp/nomad/client/structs"
    14  
    15  	"github.com/hashicorp/nomad/helper/testlog"
    16  	"github.com/hashicorp/nomad/helper/uuid"
    17  	"github.com/hashicorp/nomad/nomad/structs"
    18  	"github.com/hashicorp/nomad/testutil"
    19  	"github.com/kr/pretty"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  const (
    25  	// Ports used in testTask
    26  	xPort = 1234
    27  	yPort = 1235
    28  )
    29  
    30  func testTask() *TaskServices {
    31  	return &TaskServices{
    32  		AllocID:   uuid.Generate(),
    33  		Name:      "taskname",
    34  		Restarter: &restartRecorder{},
    35  		Services: []*structs.Service{
    36  			{
    37  				Name:      "taskname-service",
    38  				PortLabel: "x",
    39  				Tags:      []string{"tag1", "tag2"},
    40  			},
    41  		},
    42  		Networks: []*structs.NetworkResource{
    43  			{
    44  				DynamicPorts: []structs.Port{
    45  					{Label: "x", Value: xPort},
    46  					{Label: "y", Value: yPort},
    47  				},
    48  			},
    49  		},
    50  		DriverExec: newMockExec(),
    51  	}
    52  }
    53  
    54  // mockExec implements the ScriptExecutor interface and will use an alternate
    55  // implementation t.ExecFunc if non-nil.
    56  type mockExec struct {
    57  	// Ticked whenever a script is called
    58  	execs chan int
    59  
    60  	// If non-nil will be called by script checks
    61  	ExecFunc func(ctx context.Context, cmd string, args []string) ([]byte, int, error)
    62  }
    63  
    64  func newMockExec() *mockExec {
    65  	return &mockExec{
    66  		execs: make(chan int, 100),
    67  	}
    68  }
    69  
    70  func (m *mockExec) Exec(ctx context.Context, cmd string, args []string) ([]byte, int, error) {
    71  	select {
    72  	case m.execs <- 1:
    73  	default:
    74  	}
    75  	if m.ExecFunc == nil {
    76  		// Default impl is just "ok"
    77  		return []byte("ok"), 0, nil
    78  	}
    79  	return m.ExecFunc(ctx, cmd, args)
    80  }
    81  
    82  // restartRecorder is a minimal TaskRestarter implementation that simply
    83  // counts how many restarts were triggered.
    84  type restartRecorder struct {
    85  	restarts int64
    86  }
    87  
    88  func (r *restartRecorder) Restart(source, reason string, failure bool) {
    89  	atomic.AddInt64(&r.restarts, 1)
    90  }
    91  
    92  // testFakeCtx contains a fake Consul AgentAPI
    93  type testFakeCtx struct {
    94  	ServiceClient *ServiceClient
    95  	FakeConsul    *MockAgent
    96  	Task          *TaskServices
    97  	MockExec      *mockExec
    98  }
    99  
   100  var errNoOps = fmt.Errorf("testing error: no pending operations")
   101  
   102  // syncOps simulates one iteration of the ServiceClient.Run loop and returns
   103  // any errors returned by sync() or errNoOps if no pending operations.
   104  func (t *testFakeCtx) syncOnce() error {
   105  	select {
   106  	case ops := <-t.ServiceClient.opCh:
   107  		t.ServiceClient.merge(ops)
   108  		return t.ServiceClient.sync()
   109  	default:
   110  		return errNoOps
   111  	}
   112  }
   113  
   114  // setupFake creates a testFakeCtx with a ServiceClient backed by a fakeConsul.
   115  // A test Task is also provided.
   116  func setupFake(t *testing.T) *testFakeCtx {
   117  	fc := NewMockAgent()
   118  	tt := testTask()
   119  	return &testFakeCtx{
   120  		ServiceClient: NewServiceClient(fc, testlog.Logger(t), true),
   121  		FakeConsul:    fc,
   122  		Task:          tt,
   123  		MockExec:      tt.DriverExec.(*mockExec),
   124  	}
   125  }
   126  
   127  func TestConsul_ChangeTags(t *testing.T) {
   128  	ctx := setupFake(t)
   129  
   130  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
   131  		t.Fatalf("unexpected error registering task: %v", err)
   132  	}
   133  
   134  	if err := ctx.syncOnce(); err != nil {
   135  		t.Fatalf("unexpected error syncing task: %v", err)
   136  	}
   137  
   138  	if n := len(ctx.FakeConsul.services); n != 1 {
   139  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   140  	}
   141  
   142  	// Query the allocs registrations and then again when we update. The IDs
   143  	// should change
   144  	reg1, err := ctx.ServiceClient.AllocRegistrations(ctx.Task.AllocID)
   145  	if err != nil {
   146  		t.Fatalf("Looking up alloc registration failed: %v", err)
   147  	}
   148  	if reg1 == nil {
   149  		t.Fatalf("Nil alloc registrations: %v", err)
   150  	}
   151  	if num := reg1.NumServices(); num != 1 {
   152  		t.Fatalf("Wrong number of services: got %d; want 1", num)
   153  	}
   154  	if num := reg1.NumChecks(); num != 0 {
   155  		t.Fatalf("Wrong number of checks: got %d; want 0", num)
   156  	}
   157  
   158  	origKey := ""
   159  	for k, v := range ctx.FakeConsul.services {
   160  		origKey = k
   161  		if v.Name != ctx.Task.Services[0].Name {
   162  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   163  		}
   164  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   165  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   166  		}
   167  	}
   168  
   169  	origTask := ctx.Task.Copy()
   170  	ctx.Task.Services[0].Tags[0] = "newtag"
   171  	if err := ctx.ServiceClient.UpdateTask(origTask, ctx.Task); err != nil {
   172  		t.Fatalf("unexpected error registering task: %v", err)
   173  	}
   174  	if err := ctx.syncOnce(); err != nil {
   175  		t.Fatalf("unexpected error syncing task: %v", err)
   176  	}
   177  
   178  	if n := len(ctx.FakeConsul.services); n != 1 {
   179  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   180  	}
   181  
   182  	for k, v := range ctx.FakeConsul.services {
   183  		if k == origKey {
   184  			t.Errorf("expected key to change but found %q", k)
   185  		}
   186  		if v.Name != ctx.Task.Services[0].Name {
   187  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   188  		}
   189  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   190  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   191  		}
   192  	}
   193  
   194  	// Check again and ensure the IDs changed
   195  	reg2, err := ctx.ServiceClient.AllocRegistrations(ctx.Task.AllocID)
   196  	if err != nil {
   197  		t.Fatalf("Looking up alloc registration failed: %v", err)
   198  	}
   199  	if reg2 == nil {
   200  		t.Fatalf("Nil alloc registrations: %v", err)
   201  	}
   202  	if num := reg2.NumServices(); num != 1 {
   203  		t.Fatalf("Wrong number of services: got %d; want 1", num)
   204  	}
   205  	if num := reg2.NumChecks(); num != 0 {
   206  		t.Fatalf("Wrong number of checks: got %d; want 0", num)
   207  	}
   208  
   209  	for task, treg := range reg1.Tasks {
   210  		otherTaskReg, ok := reg2.Tasks[task]
   211  		if !ok {
   212  			t.Fatalf("Task %q not in second reg", task)
   213  		}
   214  
   215  		for sID := range treg.Services {
   216  			if _, ok := otherTaskReg.Services[sID]; ok {
   217  				t.Fatalf("service ID didn't change")
   218  			}
   219  		}
   220  	}
   221  }
   222  
   223  // TestConsul_ChangePorts asserts that changing the ports on a service updates
   224  // it in Consul. Pre-0.7.1 ports were not part of the service ID and this was a
   225  // slightly different code path than changing tags.
   226  func TestConsul_ChangePorts(t *testing.T) {
   227  	ctx := setupFake(t)
   228  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   229  		{
   230  			Name:      "c1",
   231  			Type:      "tcp",
   232  			Interval:  time.Second,
   233  			Timeout:   time.Second,
   234  			PortLabel: "x",
   235  		},
   236  		{
   237  			Name:     "c2",
   238  			Type:     "script",
   239  			Interval: 9000 * time.Hour,
   240  			Timeout:  time.Second,
   241  		},
   242  		{
   243  			Name:      "c3",
   244  			Type:      "http",
   245  			Protocol:  "http",
   246  			Path:      "/",
   247  			Interval:  time.Second,
   248  			Timeout:   time.Second,
   249  			PortLabel: "y",
   250  		},
   251  	}
   252  
   253  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
   254  		t.Fatalf("unexpected error registering task: %v", err)
   255  	}
   256  
   257  	if err := ctx.syncOnce(); err != nil {
   258  		t.Fatalf("unexpected error syncing task: %v", err)
   259  	}
   260  
   261  	if n := len(ctx.FakeConsul.services); n != 1 {
   262  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   263  	}
   264  
   265  	origServiceKey := ""
   266  	for k, v := range ctx.FakeConsul.services {
   267  		origServiceKey = k
   268  		if v.Name != ctx.Task.Services[0].Name {
   269  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   270  		}
   271  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   272  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   273  		}
   274  		if v.Port != xPort {
   275  			t.Errorf("expected Port x=%v but found: %v", xPort, v.Port)
   276  		}
   277  	}
   278  
   279  	if n := len(ctx.FakeConsul.checks); n != 3 {
   280  		t.Fatalf("expected 3 checks but found %d:\n%#v", n, ctx.FakeConsul.checks)
   281  	}
   282  
   283  	origTCPKey := ""
   284  	origScriptKey := ""
   285  	origHTTPKey := ""
   286  	for k, v := range ctx.FakeConsul.checks {
   287  		switch v.Name {
   288  		case "c1":
   289  			origTCPKey = k
   290  			if expected := fmt.Sprintf(":%d", xPort); v.TCP != expected {
   291  				t.Errorf("expected Port x=%v but found: %v", expected, v.TCP)
   292  			}
   293  		case "c2":
   294  			origScriptKey = k
   295  			select {
   296  			case <-ctx.MockExec.execs:
   297  				if n := len(ctx.MockExec.execs); n > 0 {
   298  					t.Errorf("expected 1 exec but found: %d", n+1)
   299  				}
   300  			case <-time.After(3 * time.Second):
   301  				t.Errorf("script not called in time")
   302  			}
   303  		case "c3":
   304  			origHTTPKey = k
   305  			if expected := fmt.Sprintf("http://:%d/", yPort); v.HTTP != expected {
   306  				t.Errorf("expected Port y=%v but found: %v", expected, v.HTTP)
   307  			}
   308  		default:
   309  			t.Fatalf("unexpected check: %q", v.Name)
   310  		}
   311  	}
   312  
   313  	// Now update the PortLabel on the Service and Check c3
   314  	origTask := ctx.Task.Copy()
   315  	ctx.Task.Services[0].PortLabel = "y"
   316  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   317  		{
   318  			Name:      "c1",
   319  			Type:      "tcp",
   320  			Interval:  time.Second,
   321  			Timeout:   time.Second,
   322  			PortLabel: "x",
   323  		},
   324  		{
   325  			Name:     "c2",
   326  			Type:     "script",
   327  			Interval: 9000 * time.Hour,
   328  			Timeout:  time.Second,
   329  		},
   330  		{
   331  			Name:     "c3",
   332  			Type:     "http",
   333  			Protocol: "http",
   334  			Path:     "/",
   335  			Interval: time.Second,
   336  			Timeout:  time.Second,
   337  			// Removed PortLabel; should default to service's (y)
   338  		},
   339  	}
   340  	if err := ctx.ServiceClient.UpdateTask(origTask, ctx.Task); err != nil {
   341  		t.Fatalf("unexpected error registering task: %v", err)
   342  	}
   343  	if err := ctx.syncOnce(); err != nil {
   344  		t.Fatalf("unexpected error syncing task: %v", err)
   345  	}
   346  
   347  	if n := len(ctx.FakeConsul.services); n != 1 {
   348  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   349  	}
   350  
   351  	for k, v := range ctx.FakeConsul.services {
   352  		if k == origServiceKey {
   353  			t.Errorf("expected key change; still: %q", k)
   354  		}
   355  		if v.Name != ctx.Task.Services[0].Name {
   356  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   357  		}
   358  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   359  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   360  		}
   361  		if v.Port != yPort {
   362  			t.Errorf("expected Port y=%v but found: %v", yPort, v.Port)
   363  		}
   364  	}
   365  
   366  	if n := len(ctx.FakeConsul.checks); n != 3 {
   367  		t.Fatalf("expected 3 check but found %d:\n%#v", n, ctx.FakeConsul.checks)
   368  	}
   369  
   370  	for k, v := range ctx.FakeConsul.checks {
   371  		switch v.Name {
   372  		case "c1":
   373  			if k == origTCPKey {
   374  				t.Errorf("expected key change for %s from %q", v.Name, origTCPKey)
   375  			}
   376  			if expected := fmt.Sprintf(":%d", xPort); v.TCP != expected {
   377  				t.Errorf("expected Port x=%v but found: %v", expected, v.TCP)
   378  			}
   379  		case "c2":
   380  			if k == origScriptKey {
   381  				t.Errorf("expected key change for %s from %q", v.Name, origScriptKey)
   382  			}
   383  			select {
   384  			case <-ctx.MockExec.execs:
   385  				if n := len(ctx.MockExec.execs); n > 0 {
   386  					t.Errorf("expected 1 exec but found: %d", n+1)
   387  				}
   388  			case <-time.After(3 * time.Second):
   389  				t.Errorf("script not called in time")
   390  			}
   391  		case "c3":
   392  			if k == origHTTPKey {
   393  				t.Errorf("expected %s key to change from %q", v.Name, k)
   394  			}
   395  			if expected := fmt.Sprintf("http://:%d/", yPort); v.HTTP != expected {
   396  				t.Errorf("expected Port y=%v but found: %v", expected, v.HTTP)
   397  			}
   398  		default:
   399  			t.Errorf("Unknown check: %q", k)
   400  		}
   401  	}
   402  }
   403  
   404  // TestConsul_ChangeChecks asserts that updating only the checks on a service
   405  // properly syncs with Consul.
   406  func TestConsul_ChangeChecks(t *testing.T) {
   407  	ctx := setupFake(t)
   408  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   409  		{
   410  			Name:      "c1",
   411  			Type:      "tcp",
   412  			Interval:  time.Second,
   413  			Timeout:   time.Second,
   414  			PortLabel: "x",
   415  			CheckRestart: &structs.CheckRestart{
   416  				Limit: 3,
   417  			},
   418  		},
   419  	}
   420  
   421  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
   422  		t.Fatalf("unexpected error registering task: %v", err)
   423  	}
   424  
   425  	if err := ctx.syncOnce(); err != nil {
   426  		t.Fatalf("unexpected error syncing task: %v", err)
   427  	}
   428  
   429  	if n := len(ctx.FakeConsul.services); n != 1 {
   430  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   431  	}
   432  
   433  	// Assert a check restart watch update was enqueued and clear it
   434  	if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 {
   435  		t.Fatalf("expected 1 check restart update but found %d", n)
   436  	}
   437  	upd := <-ctx.ServiceClient.checkWatcher.checkUpdateCh
   438  	c1ID := upd.checkID
   439  
   440  	// Query the allocs registrations and then again when we update. The IDs
   441  	// should change
   442  	reg1, err := ctx.ServiceClient.AllocRegistrations(ctx.Task.AllocID)
   443  	if err != nil {
   444  		t.Fatalf("Looking up alloc registration failed: %v", err)
   445  	}
   446  	if reg1 == nil {
   447  		t.Fatalf("Nil alloc registrations: %v", err)
   448  	}
   449  	if num := reg1.NumServices(); num != 1 {
   450  		t.Fatalf("Wrong number of services: got %d; want 1", num)
   451  	}
   452  	if num := reg1.NumChecks(); num != 1 {
   453  		t.Fatalf("Wrong number of checks: got %d; want 1", num)
   454  	}
   455  
   456  	origServiceKey := ""
   457  	for k, v := range ctx.FakeConsul.services {
   458  		origServiceKey = k
   459  		if v.Name != ctx.Task.Services[0].Name {
   460  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   461  		}
   462  		if v.Port != xPort {
   463  			t.Errorf("expected Port x=%v but found: %v", xPort, v.Port)
   464  		}
   465  	}
   466  
   467  	if n := len(ctx.FakeConsul.checks); n != 1 {
   468  		t.Fatalf("expected 1 check but found %d:\n%#v", n, ctx.FakeConsul.checks)
   469  	}
   470  	for _, v := range ctx.FakeConsul.checks {
   471  		if v.Name != "c1" {
   472  			t.Fatalf("expected check c1 but found %q", v.Name)
   473  		}
   474  	}
   475  
   476  	// Now add a check and modify the original
   477  	origTask := ctx.Task.Copy()
   478  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   479  		{
   480  			Name:      "c1",
   481  			Type:      "tcp",
   482  			Interval:  2 * time.Second,
   483  			Timeout:   time.Second,
   484  			PortLabel: "x",
   485  			CheckRestart: &structs.CheckRestart{
   486  				Limit: 3,
   487  			},
   488  		},
   489  		{
   490  			Name:      "c2",
   491  			Type:      "http",
   492  			Path:      "/",
   493  			Interval:  time.Second,
   494  			Timeout:   time.Second,
   495  			PortLabel: "x",
   496  		},
   497  	}
   498  	if err := ctx.ServiceClient.UpdateTask(origTask, ctx.Task); err != nil {
   499  		t.Fatalf("unexpected error registering task: %v", err)
   500  	}
   501  
   502  	// Assert 2 check restart watch updates was enqueued
   503  	if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 2 {
   504  		t.Fatalf("expected 2 check restart updates but found %d", n)
   505  	}
   506  
   507  	// First the new watch
   508  	upd = <-ctx.ServiceClient.checkWatcher.checkUpdateCh
   509  	if upd.checkID == c1ID || upd.remove {
   510  		t.Fatalf("expected check watch update to be an add of checkID=%q but found remove=%t checkID=%q",
   511  			c1ID, upd.remove, upd.checkID)
   512  	}
   513  
   514  	// Then remove the old watch
   515  	upd = <-ctx.ServiceClient.checkWatcher.checkUpdateCh
   516  	if upd.checkID != c1ID || !upd.remove {
   517  		t.Fatalf("expected check watch update to be a removal of checkID=%q but found remove=%t checkID=%q",
   518  			c1ID, upd.remove, upd.checkID)
   519  	}
   520  
   521  	if err := ctx.syncOnce(); err != nil {
   522  		t.Fatalf("unexpected error syncing task: %v", err)
   523  	}
   524  
   525  	if n := len(ctx.FakeConsul.services); n != 1 {
   526  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   527  	}
   528  
   529  	if _, ok := ctx.FakeConsul.services[origServiceKey]; !ok {
   530  		t.Errorf("unexpected key change; was: %q -- but found %#v", origServiceKey, ctx.FakeConsul.services)
   531  	}
   532  
   533  	if n := len(ctx.FakeConsul.checks); n != 2 {
   534  		t.Fatalf("expected 2 check but found %d:\n%#v", n, ctx.FakeConsul.checks)
   535  	}
   536  
   537  	for k, v := range ctx.FakeConsul.checks {
   538  		switch v.Name {
   539  		case "c1":
   540  			if expected := fmt.Sprintf(":%d", xPort); v.TCP != expected {
   541  				t.Errorf("expected Port x=%v but found: %v", expected, v.TCP)
   542  			}
   543  
   544  			// update id
   545  			c1ID = k
   546  		case "c2":
   547  			if expected := fmt.Sprintf("http://:%d/", xPort); v.HTTP != expected {
   548  				t.Errorf("expected Port x=%v but found: %v", expected, v.HTTP)
   549  			}
   550  		default:
   551  			t.Errorf("Unknown check: %q", k)
   552  		}
   553  	}
   554  
   555  	// Check again and ensure the IDs changed
   556  	reg2, err := ctx.ServiceClient.AllocRegistrations(ctx.Task.AllocID)
   557  	if err != nil {
   558  		t.Fatalf("Looking up alloc registration failed: %v", err)
   559  	}
   560  	if reg2 == nil {
   561  		t.Fatalf("Nil alloc registrations: %v", err)
   562  	}
   563  	if num := reg2.NumServices(); num != 1 {
   564  		t.Fatalf("Wrong number of services: got %d; want 1", num)
   565  	}
   566  	if num := reg2.NumChecks(); num != 2 {
   567  		t.Fatalf("Wrong number of checks: got %d; want 2", num)
   568  	}
   569  
   570  	for task, treg := range reg1.Tasks {
   571  		otherTaskReg, ok := reg2.Tasks[task]
   572  		if !ok {
   573  			t.Fatalf("Task %q not in second reg", task)
   574  		}
   575  
   576  		for sID, sreg := range treg.Services {
   577  			otherServiceReg, ok := otherTaskReg.Services[sID]
   578  			if !ok {
   579  				t.Fatalf("service ID changed")
   580  			}
   581  
   582  			for newID := range sreg.checkIDs {
   583  				if _, ok := otherServiceReg.checkIDs[newID]; ok {
   584  					t.Fatalf("check IDs should change")
   585  				}
   586  			}
   587  		}
   588  	}
   589  
   590  	// Alter a CheckRestart and make sure the watcher is updated but nothing else
   591  	origTask = ctx.Task.Copy()
   592  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   593  		{
   594  			Name:      "c1",
   595  			Type:      "tcp",
   596  			Interval:  2 * time.Second,
   597  			Timeout:   time.Second,
   598  			PortLabel: "x",
   599  			CheckRestart: &structs.CheckRestart{
   600  				Limit: 11,
   601  			},
   602  		},
   603  		{
   604  			Name:      "c2",
   605  			Type:      "http",
   606  			Path:      "/",
   607  			Interval:  time.Second,
   608  			Timeout:   time.Second,
   609  			PortLabel: "x",
   610  		},
   611  	}
   612  	if err := ctx.ServiceClient.UpdateTask(origTask, ctx.Task); err != nil {
   613  		t.Fatalf("unexpected error registering task: %v", err)
   614  	}
   615  	if err := ctx.syncOnce(); err != nil {
   616  		t.Fatalf("unexpected error syncing task: %v", err)
   617  	}
   618  
   619  	if n := len(ctx.FakeConsul.checks); n != 2 {
   620  		t.Fatalf("expected 2 check but found %d:\n%#v", n, ctx.FakeConsul.checks)
   621  	}
   622  
   623  	for k, v := range ctx.FakeConsul.checks {
   624  		if v.Name == "c1" {
   625  			if k != c1ID {
   626  				t.Errorf("expected c1 to still have id %q but found %q", c1ID, k)
   627  			}
   628  			break
   629  		}
   630  	}
   631  
   632  	// Assert a check restart watch update was enqueued for a removal and an add
   633  	if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 {
   634  		t.Fatalf("expected 1 check restart update but found %d", n)
   635  	}
   636  	<-ctx.ServiceClient.checkWatcher.checkUpdateCh
   637  }
   638  
   639  // TestConsul_RegServices tests basic service registration.
   640  func TestConsul_RegServices(t *testing.T) {
   641  	ctx := setupFake(t)
   642  
   643  	// Add a check w/restarting
   644  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   645  		{
   646  			Name:     "testcheck",
   647  			Type:     "tcp",
   648  			Interval: 100 * time.Millisecond,
   649  			CheckRestart: &structs.CheckRestart{
   650  				Limit: 3,
   651  			},
   652  		},
   653  	}
   654  
   655  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
   656  		t.Fatalf("unexpected error registering task: %v", err)
   657  	}
   658  
   659  	if err := ctx.syncOnce(); err != nil {
   660  		t.Fatalf("unexpected error syncing task: %v", err)
   661  	}
   662  
   663  	if n := len(ctx.FakeConsul.services); n != 1 {
   664  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   665  	}
   666  
   667  	for _, v := range ctx.FakeConsul.services {
   668  		if v.Name != ctx.Task.Services[0].Name {
   669  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   670  		}
   671  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   672  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   673  		}
   674  		if v.Port != xPort {
   675  			t.Errorf("expected Port=%d != %d", xPort, v.Port)
   676  		}
   677  	}
   678  
   679  	// Assert the check update is pending
   680  	if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 {
   681  		t.Fatalf("expected 1 check restart update but found %d", n)
   682  	}
   683  
   684  	// Assert the check update is properly formed
   685  	checkUpd := <-ctx.ServiceClient.checkWatcher.checkUpdateCh
   686  	if checkUpd.checkRestart.allocID != ctx.Task.AllocID {
   687  		t.Fatalf("expected check's allocid to be %q but found %q", "allocid", checkUpd.checkRestart.allocID)
   688  	}
   689  	if expected := 200 * time.Millisecond; checkUpd.checkRestart.timeLimit != expected {
   690  		t.Fatalf("expected check's time limit to be %v but found %v", expected, checkUpd.checkRestart.timeLimit)
   691  	}
   692  
   693  	// Make a change which will register a new service
   694  	ctx.Task.Services[0].Name = "taskname-service2"
   695  	ctx.Task.Services[0].Tags[0] = "tag3"
   696  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
   697  		t.Fatalf("unexpected error registering task: %v", err)
   698  	}
   699  
   700  	// Assert check update is pending
   701  	if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 {
   702  		t.Fatalf("expected 1 check restart update but found %d", n)
   703  	}
   704  
   705  	// Assert the check update's id has changed
   706  	checkUpd2 := <-ctx.ServiceClient.checkWatcher.checkUpdateCh
   707  	if checkUpd.checkID == checkUpd2.checkID {
   708  		t.Fatalf("expected new check update to have a new ID both both have: %q", checkUpd.checkID)
   709  	}
   710  
   711  	// Make sure changes don't take affect until sync() is called (since
   712  	// Run() isn't running)
   713  	if n := len(ctx.FakeConsul.services); n != 1 {
   714  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   715  	}
   716  	for _, v := range ctx.FakeConsul.services {
   717  		if reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   718  			t.Errorf("expected Tags to differ, changes applied before sync()")
   719  		}
   720  	}
   721  
   722  	// Now sync() and re-check for the applied updates
   723  	if err := ctx.syncOnce(); err != nil {
   724  		t.Fatalf("unexpected error syncing task: %v", err)
   725  	}
   726  	if n := len(ctx.FakeConsul.services); n != 2 {
   727  		t.Fatalf("expected 2 services but found %d:\n%#v", n, ctx.FakeConsul.services)
   728  	}
   729  	found := false
   730  	for _, v := range ctx.FakeConsul.services {
   731  		if v.Name == ctx.Task.Services[0].Name {
   732  			if found {
   733  				t.Fatalf("found new service name %q twice", v.Name)
   734  			}
   735  			found = true
   736  			if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   737  				t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   738  			}
   739  		}
   740  	}
   741  	if !found {
   742  		t.Fatalf("did not find new service %q", ctx.Task.Services[0].Name)
   743  	}
   744  
   745  	// Remove the new task
   746  	ctx.ServiceClient.RemoveTask(ctx.Task)
   747  	if err := ctx.syncOnce(); err != nil {
   748  		t.Fatalf("unexpected error syncing task: %v", err)
   749  	}
   750  	if n := len(ctx.FakeConsul.services); n != 1 {
   751  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   752  	}
   753  	for _, v := range ctx.FakeConsul.services {
   754  		if v.Name != "taskname-service" {
   755  			t.Errorf("expected original task to survive not %q", v.Name)
   756  		}
   757  	}
   758  
   759  	// Assert check update is pending
   760  	if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 {
   761  		t.Fatalf("expected 1 check restart update but found %d", n)
   762  	}
   763  
   764  	// Assert the check update's id is correct and that it's a removal
   765  	checkUpd3 := <-ctx.ServiceClient.checkWatcher.checkUpdateCh
   766  	if checkUpd2.checkID != checkUpd3.checkID {
   767  		t.Fatalf("expected checkid %q but found %q", checkUpd2.checkID, checkUpd3.checkID)
   768  	}
   769  	if !checkUpd3.remove {
   770  		t.Fatalf("expected check watch removal update but found: %#v", checkUpd3)
   771  	}
   772  }
   773  
   774  // TestConsul_ShutdownOK tests the ok path for the shutdown logic in
   775  // ServiceClient.
   776  func TestConsul_ShutdownOK(t *testing.T) {
   777  	require := require.New(t)
   778  	ctx := setupFake(t)
   779  
   780  	// Add a script check to make sure its TTL gets updated
   781  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   782  		{
   783  			Name:    "scriptcheck",
   784  			Type:    "script",
   785  			Command: "true",
   786  			// Make check block until shutdown
   787  			Interval:      9000 * time.Hour,
   788  			Timeout:       10 * time.Second,
   789  			InitialStatus: "warning",
   790  		},
   791  	}
   792  
   793  	go ctx.ServiceClient.Run()
   794  
   795  	// Register a task and agent
   796  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
   797  		t.Fatalf("unexpected error registering task: %v", err)
   798  	}
   799  
   800  	agentServices := []*structs.Service{
   801  		{
   802  			Name:      "http",
   803  			Tags:      []string{"nomad"},
   804  			PortLabel: "localhost:2345",
   805  		},
   806  	}
   807  	if err := ctx.ServiceClient.RegisterAgent("client", agentServices); err != nil {
   808  		t.Fatalf("unexpected error registering agent: %v", err)
   809  	}
   810  
   811  	testutil.WaitForResult(func() (bool, error) {
   812  		return ctx.ServiceClient.hasSeen(), fmt.Errorf("error contacting Consul")
   813  	}, func(err error) {
   814  		t.Fatalf("err: %v", err)
   815  	})
   816  
   817  	// Shutdown should block until scripts finish
   818  	if err := ctx.ServiceClient.Shutdown(); err != nil {
   819  		t.Errorf("unexpected error shutting down client: %v", err)
   820  	}
   821  
   822  	// UpdateTTL should have been called once for the script check and once
   823  	// for shutdown
   824  	if n := len(ctx.FakeConsul.checkTTLs); n != 1 {
   825  		t.Fatalf("expected 1 checkTTL entry but found: %d", n)
   826  	}
   827  	for _, v := range ctx.FakeConsul.checkTTLs {
   828  		require.Equalf(2, v, "expected 2 updates but foud %d", v)
   829  	}
   830  	for _, v := range ctx.FakeConsul.checks {
   831  		if v.Status != "passing" {
   832  			t.Fatalf("expected check to be passing but found %q", v.Status)
   833  		}
   834  	}
   835  }
   836  
   837  // TestConsul_ShutdownSlow tests the slow but ok path for the shutdown logic in
   838  // ServiceClient.
   839  func TestConsul_ShutdownSlow(t *testing.T) {
   840  	t.Parallel()
   841  	ctx := setupFake(t)
   842  
   843  	// Add a script check to make sure its TTL gets updated
   844  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   845  		{
   846  			Name:    "scriptcheck",
   847  			Type:    "script",
   848  			Command: "true",
   849  			// Make check block until shutdown
   850  			Interval:      9000 * time.Hour,
   851  			Timeout:       5 * time.Second,
   852  			InitialStatus: "warning",
   853  		},
   854  	}
   855  
   856  	// Make Exec slow, but not too slow
   857  	waiter := make(chan struct{})
   858  	ctx.MockExec.ExecFunc = func(ctx context.Context, cmd string, args []string) ([]byte, int, error) {
   859  		select {
   860  		case <-waiter:
   861  		default:
   862  			close(waiter)
   863  		}
   864  		time.Sleep(time.Second)
   865  		return []byte{}, 0, nil
   866  	}
   867  
   868  	// Make shutdown wait time just a bit longer than ctx.Exec takes
   869  	ctx.ServiceClient.shutdownWait = 3 * time.Second
   870  
   871  	go ctx.ServiceClient.Run()
   872  
   873  	// Register a task and agent
   874  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
   875  		t.Fatalf("unexpected error registering task: %v", err)
   876  	}
   877  
   878  	// wait for Exec to get called before shutting down
   879  	<-waiter
   880  
   881  	// Shutdown should block until all enqueued operations finish.
   882  	preShutdown := time.Now()
   883  	if err := ctx.ServiceClient.Shutdown(); err != nil {
   884  		t.Errorf("unexpected error shutting down client: %v", err)
   885  	}
   886  
   887  	// Shutdown time should have taken: 1s <= shutdown <= 3s
   888  	shutdownTime := time.Now().Sub(preShutdown)
   889  	if shutdownTime < time.Second || shutdownTime > ctx.ServiceClient.shutdownWait {
   890  		t.Errorf("expected shutdown to take >1s and <%s but took: %s", ctx.ServiceClient.shutdownWait, shutdownTime)
   891  	}
   892  
   893  	// UpdateTTL should have been called once for the script check
   894  	if n := len(ctx.FakeConsul.checkTTLs); n != 1 {
   895  		t.Fatalf("expected 1 checkTTL entry but found: %d", n)
   896  	}
   897  	for _, v := range ctx.FakeConsul.checkTTLs {
   898  		if v != 1 {
   899  			t.Fatalf("expected script check to be updated once but found %d", v)
   900  		}
   901  	}
   902  	for _, v := range ctx.FakeConsul.checks {
   903  		if v.Status != "passing" {
   904  			t.Fatalf("expected check to be passing but found %q", v.Status)
   905  		}
   906  	}
   907  }
   908  
   909  // TestConsul_ShutdownBlocked tests the blocked past deadline path for the
   910  // shutdown logic in ServiceClient.
   911  func TestConsul_ShutdownBlocked(t *testing.T) {
   912  	t.Parallel()
   913  	ctx := setupFake(t)
   914  
   915  	// Add a script check to make sure its TTL gets updated
   916  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   917  		{
   918  			Name:    "scriptcheck",
   919  			Type:    "script",
   920  			Command: "true",
   921  			// Make check block until shutdown
   922  			Interval:      9000 * time.Hour,
   923  			Timeout:       9000 * time.Hour,
   924  			InitialStatus: "warning",
   925  		},
   926  	}
   927  
   928  	block := make(chan struct{})
   929  	defer close(block) // cleanup after test
   930  
   931  	// Make Exec block forever
   932  	waiter := make(chan struct{})
   933  	ctx.MockExec.ExecFunc = func(ctx context.Context, cmd string, args []string) ([]byte, int, error) {
   934  		close(waiter)
   935  		<-block
   936  		return []byte{}, 0, nil
   937  	}
   938  
   939  	// Use a short shutdown deadline since we're intentionally blocking forever
   940  	ctx.ServiceClient.shutdownWait = time.Second
   941  
   942  	go ctx.ServiceClient.Run()
   943  
   944  	// Register a task and agent
   945  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
   946  		t.Fatalf("unexpected error registering task: %v", err)
   947  	}
   948  
   949  	// Wait for exec to be called
   950  	<-waiter
   951  
   952  	// Shutdown should block until all enqueued operations finish.
   953  	preShutdown := time.Now()
   954  	err := ctx.ServiceClient.Shutdown()
   955  	if err == nil {
   956  		t.Errorf("expected a timed out error from shutdown")
   957  	}
   958  
   959  	// Shutdown time should have taken shutdownWait; to avoid timing
   960  	// related errors simply test for wait <= shutdown <= wait+3s
   961  	shutdownTime := time.Now().Sub(preShutdown)
   962  	maxWait := ctx.ServiceClient.shutdownWait + (3 * time.Second)
   963  	if shutdownTime < ctx.ServiceClient.shutdownWait || shutdownTime > maxWait {
   964  		t.Errorf("expected shutdown to take >%s and <%s but took: %s", ctx.ServiceClient.shutdownWait, maxWait, shutdownTime)
   965  	}
   966  
   967  	// UpdateTTL should not have been called for the script check
   968  	if n := len(ctx.FakeConsul.checkTTLs); n != 0 {
   969  		t.Fatalf("expected 0 checkTTL entry but found: %d", n)
   970  	}
   971  	for _, v := range ctx.FakeConsul.checks {
   972  		if expected := "warning"; v.Status != expected {
   973  			t.Fatalf("expected check to be %q but found %q", expected, v.Status)
   974  		}
   975  	}
   976  }
   977  
   978  // TestConsul_RemoveScript assert removing a script check removes all objects
   979  // related to that check.
   980  func TestConsul_CancelScript(t *testing.T) {
   981  	ctx := setupFake(t)
   982  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   983  		{
   984  			Name:     "scriptcheckDel",
   985  			Type:     "script",
   986  			Interval: 9000 * time.Hour,
   987  			Timeout:  9000 * time.Hour,
   988  		},
   989  		{
   990  			Name:     "scriptcheckKeep",
   991  			Type:     "script",
   992  			Interval: 9000 * time.Hour,
   993  			Timeout:  9000 * time.Hour,
   994  		},
   995  	}
   996  
   997  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
   998  		t.Fatalf("unexpected error registering task: %v", err)
   999  	}
  1000  
  1001  	if err := ctx.syncOnce(); err != nil {
  1002  		t.Fatalf("unexpected error syncing task: %v", err)
  1003  	}
  1004  
  1005  	if len(ctx.FakeConsul.checks) != 2 {
  1006  		t.Errorf("expected 2 checks but found %d", len(ctx.FakeConsul.checks))
  1007  	}
  1008  
  1009  	if len(ctx.ServiceClient.scripts) != 2 && len(ctx.ServiceClient.runningScripts) != 2 {
  1010  		t.Errorf("expected 2 running script but found scripts=%d runningScripts=%d",
  1011  			len(ctx.ServiceClient.scripts), len(ctx.ServiceClient.runningScripts))
  1012  	}
  1013  
  1014  	for i := 0; i < 2; i++ {
  1015  		select {
  1016  		case <-ctx.MockExec.execs:
  1017  			// Script ran as expected!
  1018  		case <-time.After(3 * time.Second):
  1019  			t.Fatalf("timed out waiting for script check to run")
  1020  		}
  1021  	}
  1022  
  1023  	// Remove a check and update the task
  1024  	origTask := ctx.Task.Copy()
  1025  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
  1026  		{
  1027  			Name:     "scriptcheckKeep",
  1028  			Type:     "script",
  1029  			Interval: 9000 * time.Hour,
  1030  			Timeout:  9000 * time.Hour,
  1031  		},
  1032  	}
  1033  
  1034  	if err := ctx.ServiceClient.UpdateTask(origTask, ctx.Task); err != nil {
  1035  		t.Fatalf("unexpected error registering task: %v", err)
  1036  	}
  1037  
  1038  	if err := ctx.syncOnce(); err != nil {
  1039  		t.Fatalf("unexpected error syncing task: %v", err)
  1040  	}
  1041  
  1042  	if len(ctx.FakeConsul.checks) != 1 {
  1043  		t.Errorf("expected 1 check but found %d", len(ctx.FakeConsul.checks))
  1044  	}
  1045  
  1046  	if len(ctx.ServiceClient.scripts) != 1 && len(ctx.ServiceClient.runningScripts) != 1 {
  1047  		t.Errorf("expected 1 running script but found scripts=%d runningScripts=%d",
  1048  			len(ctx.ServiceClient.scripts), len(ctx.ServiceClient.runningScripts))
  1049  	}
  1050  
  1051  	// Make sure exec wasn't called again
  1052  	select {
  1053  	case <-ctx.MockExec.execs:
  1054  		t.Errorf("unexpected execution of script; was goroutine not cancelled?")
  1055  	case <-time.After(100 * time.Millisecond):
  1056  		// No unexpected script execs
  1057  	}
  1058  
  1059  	// Don't leak goroutines
  1060  	for _, scriptHandle := range ctx.ServiceClient.runningScripts {
  1061  		scriptHandle.cancel()
  1062  	}
  1063  }
  1064  
  1065  // TestConsul_DriverNetwork_AutoUse asserts that if a driver network has
  1066  // auto-use set then services should advertise it unless explicitly set to
  1067  // host. Checks should always use host.
  1068  func TestConsul_DriverNetwork_AutoUse(t *testing.T) {
  1069  	t.Parallel()
  1070  	ctx := setupFake(t)
  1071  
  1072  	ctx.Task.Services = []*structs.Service{
  1073  		{
  1074  			Name:        "auto-advertise-x",
  1075  			PortLabel:   "x",
  1076  			AddressMode: structs.AddressModeAuto,
  1077  			Checks: []*structs.ServiceCheck{
  1078  				{
  1079  					Name:     "default-check-x",
  1080  					Type:     "tcp",
  1081  					Interval: time.Second,
  1082  					Timeout:  time.Second,
  1083  				},
  1084  				{
  1085  					Name:      "weird-y-check",
  1086  					Type:      "http",
  1087  					Interval:  time.Second,
  1088  					Timeout:   time.Second,
  1089  					PortLabel: "y",
  1090  				},
  1091  			},
  1092  		},
  1093  		{
  1094  			Name:        "driver-advertise-y",
  1095  			PortLabel:   "y",
  1096  			AddressMode: structs.AddressModeDriver,
  1097  			Checks: []*structs.ServiceCheck{
  1098  				{
  1099  					Name:     "default-check-y",
  1100  					Type:     "tcp",
  1101  					Interval: time.Second,
  1102  					Timeout:  time.Second,
  1103  				},
  1104  			},
  1105  		},
  1106  		{
  1107  			Name:        "host-advertise-y",
  1108  			PortLabel:   "y",
  1109  			AddressMode: structs.AddressModeHost,
  1110  		},
  1111  	}
  1112  
  1113  	ctx.Task.DriverNetwork = &cstructs.DriverNetwork{
  1114  		PortMap: map[string]int{
  1115  			"x": 8888,
  1116  			"y": 9999,
  1117  		},
  1118  		IP:            "172.18.0.2",
  1119  		AutoAdvertise: true,
  1120  	}
  1121  
  1122  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
  1123  		t.Fatalf("unexpected error registering task: %v", err)
  1124  	}
  1125  
  1126  	if err := ctx.syncOnce(); err != nil {
  1127  		t.Fatalf("unexpected error syncing task: %v", err)
  1128  	}
  1129  
  1130  	if n := len(ctx.FakeConsul.services); n != 3 {
  1131  		t.Fatalf("expected 2 services but found: %d", n)
  1132  	}
  1133  
  1134  	for _, v := range ctx.FakeConsul.services {
  1135  		switch v.Name {
  1136  		case ctx.Task.Services[0].Name: // x
  1137  			// Since DriverNetwork.AutoAdvertise=true, driver ports should be used
  1138  			if v.Port != ctx.Task.DriverNetwork.PortMap["x"] {
  1139  				t.Errorf("expected service %s's port to be %d but found %d",
  1140  					v.Name, ctx.Task.DriverNetwork.PortMap["x"], v.Port)
  1141  			}
  1142  			// The order of checks in Consul is not guaranteed to
  1143  			// be the same as their order in the Task definition,
  1144  			// so check in a loop
  1145  			if expected := 2; len(v.Checks) != expected {
  1146  				t.Errorf("expected %d checks but found %d", expected, len(v.Checks))
  1147  			}
  1148  			for _, c := range v.Checks {
  1149  				// No name on AgentServiceChecks, use type
  1150  				switch {
  1151  				case c.TCP != "":
  1152  					// Checks should always use host port though
  1153  					if c.TCP != ":1234" { // xPort
  1154  						t.Errorf("expected service %s check 1's port to be %d but found %q",
  1155  							v.Name, xPort, c.TCP)
  1156  					}
  1157  				case c.HTTP != "":
  1158  					if c.HTTP != "http://:1235" { // yPort
  1159  						t.Errorf("expected service %s check 2's port to be %d but found %q",
  1160  							v.Name, yPort, c.HTTP)
  1161  					}
  1162  				default:
  1163  					t.Errorf("unexpected check %#v on service %q", c, v.Name)
  1164  				}
  1165  			}
  1166  		case ctx.Task.Services[1].Name: // y
  1167  			// Service should be container ip:port
  1168  			if v.Address != ctx.Task.DriverNetwork.IP {
  1169  				t.Errorf("expected service %s's address to be %s but found %s",
  1170  					v.Name, ctx.Task.DriverNetwork.IP, v.Address)
  1171  			}
  1172  			if v.Port != ctx.Task.DriverNetwork.PortMap["y"] {
  1173  				t.Errorf("expected service %s's port to be %d but found %d",
  1174  					v.Name, ctx.Task.DriverNetwork.PortMap["x"], v.Port)
  1175  			}
  1176  			// Check should be host ip:port
  1177  			if v.Checks[0].TCP != ":1235" { // yPort
  1178  				t.Errorf("expected service %s check's port to be %d but found %s",
  1179  					v.Name, yPort, v.Checks[0].TCP)
  1180  			}
  1181  		case ctx.Task.Services[2].Name: // y + host mode
  1182  			if v.Port != yPort {
  1183  				t.Errorf("expected service %s's port to be %d but found %d",
  1184  					v.Name, yPort, v.Port)
  1185  			}
  1186  		default:
  1187  			t.Errorf("unexpected service name: %q", v.Name)
  1188  		}
  1189  	}
  1190  }
  1191  
  1192  // TestConsul_DriverNetwork_NoAutoUse asserts that if a driver network doesn't
  1193  // set auto-use only services which request the driver's network should
  1194  // advertise it.
  1195  func TestConsul_DriverNetwork_NoAutoUse(t *testing.T) {
  1196  	t.Parallel()
  1197  	ctx := setupFake(t)
  1198  
  1199  	ctx.Task.Services = []*structs.Service{
  1200  		{
  1201  			Name:        "auto-advertise-x",
  1202  			PortLabel:   "x",
  1203  			AddressMode: structs.AddressModeAuto,
  1204  		},
  1205  		{
  1206  			Name:        "driver-advertise-y",
  1207  			PortLabel:   "y",
  1208  			AddressMode: structs.AddressModeDriver,
  1209  		},
  1210  		{
  1211  			Name:        "host-advertise-y",
  1212  			PortLabel:   "y",
  1213  			AddressMode: structs.AddressModeHost,
  1214  		},
  1215  	}
  1216  
  1217  	ctx.Task.DriverNetwork = &cstructs.DriverNetwork{
  1218  		PortMap: map[string]int{
  1219  			"x": 8888,
  1220  			"y": 9999,
  1221  		},
  1222  		IP:            "172.18.0.2",
  1223  		AutoAdvertise: false,
  1224  	}
  1225  
  1226  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
  1227  		t.Fatalf("unexpected error registering task: %v", err)
  1228  	}
  1229  
  1230  	if err := ctx.syncOnce(); err != nil {
  1231  		t.Fatalf("unexpected error syncing task: %v", err)
  1232  	}
  1233  
  1234  	if n := len(ctx.FakeConsul.services); n != 3 {
  1235  		t.Fatalf("expected 3 services but found: %d", n)
  1236  	}
  1237  
  1238  	for _, v := range ctx.FakeConsul.services {
  1239  		switch v.Name {
  1240  		case ctx.Task.Services[0].Name: // x + auto
  1241  			// Since DriverNetwork.AutoAdvertise=false, host ports should be used
  1242  			if v.Port != xPort {
  1243  				t.Errorf("expected service %s's port to be %d but found %d",
  1244  					v.Name, xPort, v.Port)
  1245  			}
  1246  		case ctx.Task.Services[1].Name: // y + driver mode
  1247  			// Service should be container ip:port
  1248  			if v.Address != ctx.Task.DriverNetwork.IP {
  1249  				t.Errorf("expected service %s's address to be %s but found %s",
  1250  					v.Name, ctx.Task.DriverNetwork.IP, v.Address)
  1251  			}
  1252  			if v.Port != ctx.Task.DriverNetwork.PortMap["y"] {
  1253  				t.Errorf("expected service %s's port to be %d but found %d",
  1254  					v.Name, ctx.Task.DriverNetwork.PortMap["x"], v.Port)
  1255  			}
  1256  		case ctx.Task.Services[2].Name: // y + host mode
  1257  			if v.Port != yPort {
  1258  				t.Errorf("expected service %s's port to be %d but found %d",
  1259  					v.Name, yPort, v.Port)
  1260  			}
  1261  		default:
  1262  			t.Errorf("unexpected service name: %q", v.Name)
  1263  		}
  1264  	}
  1265  }
  1266  
  1267  // TestConsul_DriverNetwork_Change asserts that if a driver network is
  1268  // specified and a service updates its use its properly updated in Consul.
  1269  func TestConsul_DriverNetwork_Change(t *testing.T) {
  1270  	t.Parallel()
  1271  	ctx := setupFake(t)
  1272  
  1273  	ctx.Task.Services = []*structs.Service{
  1274  		{
  1275  			Name:        "service-foo",
  1276  			PortLabel:   "x",
  1277  			AddressMode: structs.AddressModeAuto,
  1278  		},
  1279  	}
  1280  
  1281  	ctx.Task.DriverNetwork = &cstructs.DriverNetwork{
  1282  		PortMap: map[string]int{
  1283  			"x": 8888,
  1284  			"y": 9999,
  1285  		},
  1286  		IP:            "172.18.0.2",
  1287  		AutoAdvertise: false,
  1288  	}
  1289  
  1290  	syncAndAssertPort := func(port int) {
  1291  		if err := ctx.syncOnce(); err != nil {
  1292  			t.Fatalf("unexpected error syncing task: %v", err)
  1293  		}
  1294  
  1295  		if n := len(ctx.FakeConsul.services); n != 1 {
  1296  			t.Fatalf("expected 1 service but found: %d", n)
  1297  		}
  1298  
  1299  		for _, v := range ctx.FakeConsul.services {
  1300  			switch v.Name {
  1301  			case ctx.Task.Services[0].Name:
  1302  				if v.Port != port {
  1303  					t.Errorf("expected service %s's port to be %d but found %d",
  1304  						v.Name, port, v.Port)
  1305  				}
  1306  			default:
  1307  				t.Errorf("unexpected service name: %q", v.Name)
  1308  			}
  1309  		}
  1310  	}
  1311  
  1312  	// Initial service should advertise host port x
  1313  	if err := ctx.ServiceClient.RegisterTask(ctx.Task); err != nil {
  1314  		t.Fatalf("unexpected error registering task: %v", err)
  1315  	}
  1316  
  1317  	syncAndAssertPort(xPort)
  1318  
  1319  	// UpdateTask to use Host (shouldn't change anything)
  1320  	origTask := ctx.Task.Copy()
  1321  	ctx.Task.Services[0].AddressMode = structs.AddressModeHost
  1322  
  1323  	if err := ctx.ServiceClient.UpdateTask(origTask, ctx.Task); err != nil {
  1324  		t.Fatalf("unexpected error updating task: %v", err)
  1325  	}
  1326  
  1327  	syncAndAssertPort(xPort)
  1328  
  1329  	// UpdateTask to use Driver (*should* change IP and port)
  1330  	origTask = ctx.Task.Copy()
  1331  	ctx.Task.Services[0].AddressMode = structs.AddressModeDriver
  1332  
  1333  	if err := ctx.ServiceClient.UpdateTask(origTask, ctx.Task); err != nil {
  1334  		t.Fatalf("unexpected error updating task: %v", err)
  1335  	}
  1336  
  1337  	syncAndAssertPort(ctx.Task.DriverNetwork.PortMap["x"])
  1338  }
  1339  
  1340  // TestConsul_CanaryTags asserts CanaryTags are used when Canary=true
  1341  func TestConsul_CanaryTags(t *testing.T) {
  1342  	t.Parallel()
  1343  	require := require.New(t)
  1344  	ctx := setupFake(t)
  1345  
  1346  	canaryTags := []string{"tag1", "canary"}
  1347  	ctx.Task.Canary = true
  1348  	ctx.Task.Services[0].CanaryTags = canaryTags
  1349  
  1350  	require.NoError(ctx.ServiceClient.RegisterTask(ctx.Task))
  1351  	require.NoError(ctx.syncOnce())
  1352  	require.Len(ctx.FakeConsul.services, 1)
  1353  	for _, service := range ctx.FakeConsul.services {
  1354  		require.Equal(canaryTags, service.Tags)
  1355  	}
  1356  
  1357  	// Disable canary and assert tags are not the canary tags
  1358  	origTask := ctx.Task.Copy()
  1359  	ctx.Task.Canary = false
  1360  	require.NoError(ctx.ServiceClient.UpdateTask(origTask, ctx.Task))
  1361  	require.NoError(ctx.syncOnce())
  1362  	require.Len(ctx.FakeConsul.services, 1)
  1363  	for _, service := range ctx.FakeConsul.services {
  1364  		require.NotEqual(canaryTags, service.Tags)
  1365  	}
  1366  
  1367  	ctx.ServiceClient.RemoveTask(ctx.Task)
  1368  	require.NoError(ctx.syncOnce())
  1369  	require.Len(ctx.FakeConsul.services, 0)
  1370  }
  1371  
  1372  // TestConsul_CanaryTags_NoTags asserts Tags are used when Canary=true and there
  1373  // are no specified canary tags
  1374  func TestConsul_CanaryTags_NoTags(t *testing.T) {
  1375  	t.Parallel()
  1376  	require := require.New(t)
  1377  	ctx := setupFake(t)
  1378  
  1379  	tags := []string{"tag1", "foo"}
  1380  	ctx.Task.Canary = true
  1381  	ctx.Task.Services[0].Tags = tags
  1382  
  1383  	require.NoError(ctx.ServiceClient.RegisterTask(ctx.Task))
  1384  	require.NoError(ctx.syncOnce())
  1385  	require.Len(ctx.FakeConsul.services, 1)
  1386  	for _, service := range ctx.FakeConsul.services {
  1387  		require.Equal(tags, service.Tags)
  1388  	}
  1389  
  1390  	// Disable canary and assert tags dont change
  1391  	origTask := ctx.Task.Copy()
  1392  	ctx.Task.Canary = false
  1393  	require.NoError(ctx.ServiceClient.UpdateTask(origTask, ctx.Task))
  1394  	require.NoError(ctx.syncOnce())
  1395  	require.Len(ctx.FakeConsul.services, 1)
  1396  	for _, service := range ctx.FakeConsul.services {
  1397  		require.Equal(tags, service.Tags)
  1398  	}
  1399  
  1400  	ctx.ServiceClient.RemoveTask(ctx.Task)
  1401  	require.NoError(ctx.syncOnce())
  1402  	require.Len(ctx.FakeConsul.services, 0)
  1403  }
  1404  
  1405  // TestConsul_PeriodicSync asserts that Nomad periodically reconciles with
  1406  // Consul.
  1407  func TestConsul_PeriodicSync(t *testing.T) {
  1408  	t.Parallel()
  1409  
  1410  	ctx := setupFake(t)
  1411  	defer ctx.ServiceClient.Shutdown()
  1412  
  1413  	// Lower periodic sync interval to speed up test
  1414  	ctx.ServiceClient.periodicInterval = 2 * time.Millisecond
  1415  
  1416  	// Run for 10ms and assert hits >= 5 because each sync() calls multiple
  1417  	// Consul APIs
  1418  	go ctx.ServiceClient.Run()
  1419  
  1420  	select {
  1421  	case <-ctx.ServiceClient.exitCh:
  1422  		t.Fatalf("exited unexpectedly")
  1423  	case <-time.After(10 * time.Millisecond):
  1424  	}
  1425  
  1426  	minHits := 5
  1427  	if hits := ctx.FakeConsul.getHits(); hits < minHits {
  1428  		t.Fatalf("expected at least %d hits but found %d", minHits, hits)
  1429  	}
  1430  }
  1431  
  1432  // TestIsNomadService asserts the isNomadService helper returns true for Nomad
  1433  // task IDs and false for unknown IDs and Nomad agent IDs (see #2827).
  1434  func TestIsNomadService(t *testing.T) {
  1435  	t.Parallel()
  1436  
  1437  	tests := []struct {
  1438  		id     string
  1439  		result bool
  1440  	}{
  1441  		{"_nomad-client-nomad-client-http", false},
  1442  		{"_nomad-server-nomad-serf", false},
  1443  
  1444  		// Pre-0.7.1 style IDs still match
  1445  		{"_nomad-executor-abc", true},
  1446  		{"_nomad-executor", true},
  1447  
  1448  		// Post-0.7.1 style IDs match
  1449  		{"_nomad-task-FBBK265QN4TMT25ND4EP42TJVMYJ3HR4", true},
  1450  
  1451  		{"not-nomad", false},
  1452  		{"_nomad", false},
  1453  	}
  1454  
  1455  	for _, test := range tests {
  1456  		t.Run(test.id, func(t *testing.T) {
  1457  			actual := isNomadService(test.id)
  1458  			if actual != test.result {
  1459  				t.Errorf("%q should be %t but found %t", test.id, test.result, actual)
  1460  			}
  1461  		})
  1462  	}
  1463  }
  1464  
  1465  // TestCreateCheckReg_HTTP asserts Nomad ServiceCheck structs are properly
  1466  // converted to Consul API AgentCheckRegistrations for HTTP checks.
  1467  func TestCreateCheckReg_HTTP(t *testing.T) {
  1468  	t.Parallel()
  1469  	check := &structs.ServiceCheck{
  1470  		Name:      "name",
  1471  		Type:      "http",
  1472  		Path:      "/path",
  1473  		PortLabel: "label",
  1474  		Method:    "POST",
  1475  		Header: map[string][]string{
  1476  			"Foo": {"bar"},
  1477  		},
  1478  	}
  1479  
  1480  	serviceID := "testService"
  1481  	checkID := check.Hash(serviceID)
  1482  	host := "localhost"
  1483  	port := 41111
  1484  
  1485  	expected := &api.AgentCheckRegistration{
  1486  		ID:        checkID,
  1487  		Name:      "name",
  1488  		ServiceID: serviceID,
  1489  		AgentServiceCheck: api.AgentServiceCheck{
  1490  			Timeout:  "0s",
  1491  			Interval: "0s",
  1492  			HTTP:     fmt.Sprintf("http://%s:%d/path", host, port),
  1493  			Method:   "POST",
  1494  			Header: map[string][]string{
  1495  				"Foo": {"bar"},
  1496  			},
  1497  		},
  1498  	}
  1499  
  1500  	actual, err := createCheckReg(serviceID, checkID, check, host, port)
  1501  	if err != nil {
  1502  		t.Fatalf("err: %v", err)
  1503  	}
  1504  
  1505  	if diff := pretty.Diff(actual, expected); len(diff) > 0 {
  1506  		t.Fatalf("diff:\n%s\n", strings.Join(diff, "\n"))
  1507  	}
  1508  }
  1509  
  1510  // TestCreateCheckReg_GRPC asserts Nomad ServiceCheck structs are properly
  1511  // converted to Consul API AgentCheckRegistrations for GRPC checks.
  1512  func TestCreateCheckReg_GRPC(t *testing.T) {
  1513  	t.Parallel()
  1514  	check := &structs.ServiceCheck{
  1515  		Name:          "name",
  1516  		Type:          "grpc",
  1517  		PortLabel:     "label",
  1518  		GRPCService:   "foo.Bar",
  1519  		GRPCUseTLS:    true,
  1520  		TLSSkipVerify: true,
  1521  		Timeout:       time.Second,
  1522  		Interval:      time.Minute,
  1523  	}
  1524  
  1525  	serviceID := "testService"
  1526  	checkID := check.Hash(serviceID)
  1527  
  1528  	expected := &api.AgentCheckRegistration{
  1529  		ID:        checkID,
  1530  		Name:      "name",
  1531  		ServiceID: serviceID,
  1532  		AgentServiceCheck: api.AgentServiceCheck{
  1533  			Timeout:       "1s",
  1534  			Interval:      "1m0s",
  1535  			GRPC:          "localhost:8080/foo.Bar",
  1536  			GRPCUseTLS:    true,
  1537  			TLSSkipVerify: true,
  1538  		},
  1539  	}
  1540  
  1541  	actual, err := createCheckReg(serviceID, checkID, check, "localhost", 8080)
  1542  	require.NoError(t, err)
  1543  	require.Equal(t, expected, actual)
  1544  }
  1545  
  1546  // TestGetAddress asserts Nomad uses the correct ip and port for services and
  1547  // checks depending on port labels, driver networks, and address mode.
  1548  func TestGetAddress(t *testing.T) {
  1549  	const HostIP = "127.0.0.1"
  1550  
  1551  	cases := []struct {
  1552  		Name string
  1553  
  1554  		// Parameters
  1555  		Mode      string
  1556  		PortLabel string
  1557  		Host      map[string]int // will be converted to structs.Networks
  1558  		Driver    *cstructs.DriverNetwork
  1559  
  1560  		// Results
  1561  		ExpectedIP   string
  1562  		ExpectedPort int
  1563  		ExpectedErr  string
  1564  	}{
  1565  		// Valid Configurations
  1566  		{
  1567  			Name:      "ExampleService",
  1568  			Mode:      structs.AddressModeAuto,
  1569  			PortLabel: "db",
  1570  			Host:      map[string]int{"db": 12435},
  1571  			Driver: &cstructs.DriverNetwork{
  1572  				PortMap: map[string]int{"db": 6379},
  1573  				IP:      "10.1.2.3",
  1574  			},
  1575  			ExpectedIP:   HostIP,
  1576  			ExpectedPort: 12435,
  1577  		},
  1578  		{
  1579  			Name:      "Host",
  1580  			Mode:      structs.AddressModeHost,
  1581  			PortLabel: "db",
  1582  			Host:      map[string]int{"db": 12345},
  1583  			Driver: &cstructs.DriverNetwork{
  1584  				PortMap: map[string]int{"db": 6379},
  1585  				IP:      "10.1.2.3",
  1586  			},
  1587  			ExpectedIP:   HostIP,
  1588  			ExpectedPort: 12345,
  1589  		},
  1590  		{
  1591  			Name:      "Driver",
  1592  			Mode:      structs.AddressModeDriver,
  1593  			PortLabel: "db",
  1594  			Host:      map[string]int{"db": 12345},
  1595  			Driver: &cstructs.DriverNetwork{
  1596  				PortMap: map[string]int{"db": 6379},
  1597  				IP:      "10.1.2.3",
  1598  			},
  1599  			ExpectedIP:   "10.1.2.3",
  1600  			ExpectedPort: 6379,
  1601  		},
  1602  		{
  1603  			Name:      "AutoDriver",
  1604  			Mode:      structs.AddressModeAuto,
  1605  			PortLabel: "db",
  1606  			Host:      map[string]int{"db": 12345},
  1607  			Driver: &cstructs.DriverNetwork{
  1608  				PortMap:       map[string]int{"db": 6379},
  1609  				IP:            "10.1.2.3",
  1610  				AutoAdvertise: true,
  1611  			},
  1612  			ExpectedIP:   "10.1.2.3",
  1613  			ExpectedPort: 6379,
  1614  		},
  1615  		{
  1616  			Name:      "DriverCustomPort",
  1617  			Mode:      structs.AddressModeDriver,
  1618  			PortLabel: "7890",
  1619  			Host:      map[string]int{"db": 12345},
  1620  			Driver: &cstructs.DriverNetwork{
  1621  				PortMap: map[string]int{"db": 6379},
  1622  				IP:      "10.1.2.3",
  1623  			},
  1624  			ExpectedIP:   "10.1.2.3",
  1625  			ExpectedPort: 7890,
  1626  		},
  1627  
  1628  		// Invalid Configurations
  1629  		{
  1630  			Name:        "DriverWithoutNetwork",
  1631  			Mode:        structs.AddressModeDriver,
  1632  			PortLabel:   "db",
  1633  			Host:        map[string]int{"db": 12345},
  1634  			Driver:      nil,
  1635  			ExpectedErr: "no driver network exists",
  1636  		},
  1637  		{
  1638  			Name:      "DriverBadPort",
  1639  			Mode:      structs.AddressModeDriver,
  1640  			PortLabel: "bad-port-label",
  1641  			Host:      map[string]int{"db": 12345},
  1642  			Driver: &cstructs.DriverNetwork{
  1643  				PortMap: map[string]int{"db": 6379},
  1644  				IP:      "10.1.2.3",
  1645  			},
  1646  			ExpectedErr: "invalid port",
  1647  		},
  1648  		{
  1649  			Name:      "DriverZeroPort",
  1650  			Mode:      structs.AddressModeDriver,
  1651  			PortLabel: "0",
  1652  			Driver: &cstructs.DriverNetwork{
  1653  				IP: "10.1.2.3",
  1654  			},
  1655  			ExpectedErr: "invalid port",
  1656  		},
  1657  		{
  1658  			Name:        "HostBadPort",
  1659  			Mode:        structs.AddressModeHost,
  1660  			PortLabel:   "bad-port-label",
  1661  			ExpectedErr: "invalid port",
  1662  		},
  1663  		{
  1664  			Name:        "InvalidMode",
  1665  			Mode:        "invalid-mode",
  1666  			PortLabel:   "80",
  1667  			ExpectedErr: "invalid address mode",
  1668  		},
  1669  		{
  1670  			Name:       "NoPort_AutoMode",
  1671  			Mode:       structs.AddressModeAuto,
  1672  			ExpectedIP: HostIP,
  1673  		},
  1674  		{
  1675  			Name:       "NoPort_HostMode",
  1676  			Mode:       structs.AddressModeHost,
  1677  			ExpectedIP: HostIP,
  1678  		},
  1679  		{
  1680  			Name: "NoPort_DriverMode",
  1681  			Mode: structs.AddressModeDriver,
  1682  			Driver: &cstructs.DriverNetwork{
  1683  				IP: "10.1.2.3",
  1684  			},
  1685  			ExpectedIP: "10.1.2.3",
  1686  		},
  1687  	}
  1688  
  1689  	for _, tc := range cases {
  1690  		t.Run(tc.Name, func(t *testing.T) {
  1691  			// convert host port map into a structs.Networks
  1692  			networks := []*structs.NetworkResource{
  1693  				{
  1694  					IP:            HostIP,
  1695  					ReservedPorts: make([]structs.Port, len(tc.Host)),
  1696  				},
  1697  			}
  1698  
  1699  			i := 0
  1700  			for label, port := range tc.Host {
  1701  				networks[0].ReservedPorts[i].Label = label
  1702  				networks[0].ReservedPorts[i].Value = port
  1703  				i++
  1704  			}
  1705  
  1706  			// Run getAddress
  1707  			ip, port, err := getAddress(tc.Mode, tc.PortLabel, networks, tc.Driver)
  1708  
  1709  			// Assert the results
  1710  			assert.Equal(t, tc.ExpectedIP, ip, "IP mismatch")
  1711  			assert.Equal(t, tc.ExpectedPort, port, "Port mismatch")
  1712  			if tc.ExpectedErr == "" {
  1713  				assert.Nil(t, err)
  1714  			} else {
  1715  				if err == nil {
  1716  					t.Fatalf("expected error containing %q but err=nil", tc.ExpectedErr)
  1717  				} else {
  1718  					assert.Contains(t, err.Error(), tc.ExpectedErr)
  1719  				}
  1720  			}
  1721  		})
  1722  	}
  1723  }