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