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