github.com/jrxfive/nomad@v0.6.1-0.20170802162750-1fef470e89bf/command/agent/consul/unit_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"reflect"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/hashicorp/consul/api"
    15  	cstructs "github.com/hashicorp/nomad/client/structs"
    16  	"github.com/hashicorp/nomad/nomad/structs"
    17  )
    18  
    19  const (
    20  	// Ports used in testTask
    21  	xPort = 1234
    22  	yPort = 1235
    23  )
    24  
    25  func testLogger() *log.Logger {
    26  	if testing.Verbose() {
    27  		return log.New(os.Stderr, "", log.LstdFlags)
    28  	}
    29  	return log.New(ioutil.Discard, "", 0)
    30  }
    31  
    32  func testTask() *structs.Task {
    33  	return &structs.Task{
    34  		Name: "taskname",
    35  		Resources: &structs.Resources{
    36  			Networks: []*structs.NetworkResource{
    37  				{
    38  					DynamicPorts: []structs.Port{
    39  						{Label: "x", Value: xPort},
    40  						{Label: "y", Value: yPort},
    41  					},
    42  				},
    43  			},
    44  		},
    45  		Services: []*structs.Service{
    46  			{
    47  				Name:      "taskname-service",
    48  				PortLabel: "x",
    49  				Tags:      []string{"tag1", "tag2"},
    50  			},
    51  		},
    52  	}
    53  }
    54  
    55  // testFakeCtx contains a fake Consul AgentAPI and implements the Exec
    56  // interface to allow testing without running Consul.
    57  type testFakeCtx struct {
    58  	ServiceClient *ServiceClient
    59  	FakeConsul    *fakeConsul
    60  	Task          *structs.Task
    61  
    62  	// Ticked whenever a script is called
    63  	execs chan int
    64  
    65  	// If non-nil will be called by script checks
    66  	ExecFunc func(ctx context.Context, cmd string, args []string) ([]byte, int, error)
    67  }
    68  
    69  // Exec implements the ScriptExecutor interface and will use an alternate
    70  // implementation t.ExecFunc if non-nil.
    71  func (t *testFakeCtx) Exec(ctx context.Context, cmd string, args []string) ([]byte, int, error) {
    72  	select {
    73  	case t.execs <- 1:
    74  	default:
    75  	}
    76  	if t.ExecFunc == nil {
    77  		// Default impl is just "ok"
    78  		return []byte("ok"), 0, nil
    79  	}
    80  	return t.ExecFunc(ctx, cmd, args)
    81  }
    82  
    83  var errNoOps = fmt.Errorf("testing error: no pending operations")
    84  
    85  // syncOps simulates one iteration of the ServiceClient.Run loop and returns
    86  // any errors returned by sync() or errNoOps if no pending operations.
    87  func (t *testFakeCtx) syncOnce() error {
    88  	select {
    89  	case ops := <-t.ServiceClient.opCh:
    90  		t.ServiceClient.merge(ops)
    91  		return t.ServiceClient.sync()
    92  	default:
    93  		return errNoOps
    94  	}
    95  }
    96  
    97  // setupFake creates a testFakeCtx with a ServiceClient backed by a fakeConsul.
    98  // A test Task is also provided.
    99  func setupFake() *testFakeCtx {
   100  	fc := newFakeConsul()
   101  	return &testFakeCtx{
   102  		ServiceClient: NewServiceClient(fc, true, testLogger()),
   103  		FakeConsul:    fc,
   104  		Task:          testTask(),
   105  		execs:         make(chan int, 100),
   106  	}
   107  }
   108  
   109  // fakeConsul is a fake in-memory Consul backend for ServiceClient.
   110  type fakeConsul struct {
   111  	// maps of what services and checks have been registered
   112  	services map[string]*api.AgentServiceRegistration
   113  	checks   map[string]*api.AgentCheckRegistration
   114  	mu       sync.Mutex
   115  
   116  	// when UpdateTTL is called the check ID will have its counter inc'd
   117  	checkTTLs map[string]int
   118  
   119  	// What check status to return from Checks()
   120  	checkStatus string
   121  }
   122  
   123  func newFakeConsul() *fakeConsul {
   124  	return &fakeConsul{
   125  		services:    make(map[string]*api.AgentServiceRegistration),
   126  		checks:      make(map[string]*api.AgentCheckRegistration),
   127  		checkTTLs:   make(map[string]int),
   128  		checkStatus: api.HealthPassing,
   129  	}
   130  }
   131  
   132  func (c *fakeConsul) Services() (map[string]*api.AgentService, error) {
   133  	c.mu.Lock()
   134  	defer c.mu.Unlock()
   135  
   136  	r := make(map[string]*api.AgentService, len(c.services))
   137  	for k, v := range c.services {
   138  		r[k] = &api.AgentService{
   139  			ID:                v.ID,
   140  			Service:           v.Name,
   141  			Tags:              make([]string, len(v.Tags)),
   142  			Port:              v.Port,
   143  			Address:           v.Address,
   144  			EnableTagOverride: v.EnableTagOverride,
   145  		}
   146  		copy(r[k].Tags, v.Tags)
   147  	}
   148  	return r, nil
   149  }
   150  
   151  func (c *fakeConsul) Checks() (map[string]*api.AgentCheck, error) {
   152  	c.mu.Lock()
   153  	defer c.mu.Unlock()
   154  
   155  	r := make(map[string]*api.AgentCheck, len(c.checks))
   156  	for k, v := range c.checks {
   157  		r[k] = &api.AgentCheck{
   158  			CheckID:     v.ID,
   159  			Name:        v.Name,
   160  			Status:      c.checkStatus,
   161  			Notes:       v.Notes,
   162  			ServiceID:   v.ServiceID,
   163  			ServiceName: c.services[v.ServiceID].Name,
   164  		}
   165  	}
   166  	return r, nil
   167  }
   168  
   169  func (c *fakeConsul) CheckRegister(check *api.AgentCheckRegistration) error {
   170  	c.mu.Lock()
   171  	defer c.mu.Unlock()
   172  	c.checks[check.ID] = check
   173  
   174  	// Be nice and make checks reachable-by-service
   175  	scheck := check.AgentServiceCheck
   176  	c.services[check.ServiceID].Checks = append(c.services[check.ServiceID].Checks, &scheck)
   177  	return nil
   178  }
   179  
   180  func (c *fakeConsul) CheckDeregister(checkID string) error {
   181  	c.mu.Lock()
   182  	defer c.mu.Unlock()
   183  	delete(c.checks, checkID)
   184  	delete(c.checkTTLs, checkID)
   185  	return nil
   186  }
   187  
   188  func (c *fakeConsul) ServiceRegister(service *api.AgentServiceRegistration) error {
   189  	c.mu.Lock()
   190  	defer c.mu.Unlock()
   191  	c.services[service.ID] = service
   192  	return nil
   193  }
   194  
   195  func (c *fakeConsul) ServiceDeregister(serviceID string) error {
   196  	c.mu.Lock()
   197  	defer c.mu.Unlock()
   198  	delete(c.services, serviceID)
   199  	return nil
   200  }
   201  
   202  func (c *fakeConsul) UpdateTTL(id string, output string, status string) error {
   203  	c.mu.Lock()
   204  	defer c.mu.Unlock()
   205  	check, ok := c.checks[id]
   206  	if !ok {
   207  		return fmt.Errorf("unknown check id: %q", id)
   208  	}
   209  	// Flip initial status to passing
   210  	check.Status = "passing"
   211  	c.checkTTLs[id]++
   212  	return nil
   213  }
   214  
   215  func TestConsul_ChangeTags(t *testing.T) {
   216  	ctx := setupFake()
   217  
   218  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, nil, nil); err != nil {
   219  		t.Fatalf("unexpected error registering task: %v", err)
   220  	}
   221  
   222  	if err := ctx.syncOnce(); err != nil {
   223  		t.Fatalf("unexpected error syncing task: %v", err)
   224  	}
   225  
   226  	if n := len(ctx.FakeConsul.services); n != 1 {
   227  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   228  	}
   229  
   230  	origKey := ""
   231  	for k, v := range ctx.FakeConsul.services {
   232  		origKey = k
   233  		if v.Name != ctx.Task.Services[0].Name {
   234  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   235  		}
   236  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   237  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   238  		}
   239  	}
   240  
   241  	origTask := ctx.Task
   242  	ctx.Task = testTask()
   243  	ctx.Task.Services[0].Tags[0] = "newtag"
   244  	if err := ctx.ServiceClient.UpdateTask("allocid", origTask, ctx.Task, nil, nil); err != nil {
   245  		t.Fatalf("unexpected error registering task: %v", err)
   246  	}
   247  	if err := ctx.syncOnce(); err != nil {
   248  		t.Fatalf("unexpected error syncing task: %v", err)
   249  	}
   250  
   251  	if n := len(ctx.FakeConsul.services); n != 1 {
   252  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   253  	}
   254  
   255  	for k, v := range ctx.FakeConsul.services {
   256  		if k == origKey {
   257  			t.Errorf("expected key to change but found %q", k)
   258  		}
   259  		if v.Name != ctx.Task.Services[0].Name {
   260  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   261  		}
   262  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   263  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   264  		}
   265  	}
   266  }
   267  
   268  // TestConsul_ChangePorts asserts that changing the ports on a service updates
   269  // it in Consul. Since ports are not part of the service ID this is a slightly
   270  // different code path than changing tags.
   271  func TestConsul_ChangePorts(t *testing.T) {
   272  	ctx := setupFake()
   273  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   274  		{
   275  			Name:      "c1",
   276  			Type:      "tcp",
   277  			Interval:  time.Second,
   278  			Timeout:   time.Second,
   279  			PortLabel: "x",
   280  		},
   281  		{
   282  			Name:     "c2",
   283  			Type:     "script",
   284  			Interval: 9000 * time.Hour,
   285  			Timeout:  time.Second,
   286  		},
   287  		{
   288  			Name:      "c3",
   289  			Type:      "http",
   290  			Protocol:  "http",
   291  			Path:      "/",
   292  			Interval:  time.Second,
   293  			Timeout:   time.Second,
   294  			PortLabel: "y",
   295  		},
   296  	}
   297  
   298  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, ctx, nil); err != nil {
   299  		t.Fatalf("unexpected error registering task: %v", err)
   300  	}
   301  
   302  	if err := ctx.syncOnce(); err != nil {
   303  		t.Fatalf("unexpected error syncing task: %v", err)
   304  	}
   305  
   306  	if n := len(ctx.FakeConsul.services); n != 1 {
   307  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   308  	}
   309  
   310  	origServiceKey := ""
   311  	for k, v := range ctx.FakeConsul.services {
   312  		origServiceKey = k
   313  		if v.Name != ctx.Task.Services[0].Name {
   314  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   315  		}
   316  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   317  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   318  		}
   319  		if v.Port != xPort {
   320  			t.Errorf("expected Port x=%v but found: %v", xPort, v.Port)
   321  		}
   322  	}
   323  
   324  	if n := len(ctx.FakeConsul.checks); n != 3 {
   325  		t.Fatalf("expected 3 checks but found %d:\n%#v", n, ctx.FakeConsul.checks)
   326  	}
   327  
   328  	origTCPKey := ""
   329  	origScriptKey := ""
   330  	origHTTPKey := ""
   331  	for k, v := range ctx.FakeConsul.checks {
   332  		switch v.Name {
   333  		case "c1":
   334  			origTCPKey = k
   335  			if expected := fmt.Sprintf(":%d", xPort); v.TCP != expected {
   336  				t.Errorf("expected Port x=%v but found: %v", expected, v.TCP)
   337  			}
   338  		case "c2":
   339  			origScriptKey = k
   340  			select {
   341  			case <-ctx.execs:
   342  				if n := len(ctx.execs); n > 0 {
   343  					t.Errorf("expected 1 exec but found: %d", n+1)
   344  				}
   345  			case <-time.After(3 * time.Second):
   346  				t.Errorf("script not called in time")
   347  			}
   348  		case "c3":
   349  			origHTTPKey = k
   350  			if expected := fmt.Sprintf("http://:%d/", yPort); v.HTTP != expected {
   351  				t.Errorf("expected Port y=%v but found: %v", expected, v.HTTP)
   352  			}
   353  		default:
   354  			t.Fatalf("unexpected check: %q", v.Name)
   355  		}
   356  	}
   357  
   358  	// Now update the PortLabel on the Service and Check c3
   359  	origTask := ctx.Task
   360  	ctx.Task = testTask()
   361  	ctx.Task.Services[0].PortLabel = "y"
   362  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   363  		{
   364  			Name:      "c1",
   365  			Type:      "tcp",
   366  			Interval:  time.Second,
   367  			Timeout:   time.Second,
   368  			PortLabel: "x",
   369  		},
   370  		{
   371  			Name:     "c2",
   372  			Type:     "script",
   373  			Interval: 9000 * time.Hour,
   374  			Timeout:  time.Second,
   375  		},
   376  		{
   377  			Name:     "c3",
   378  			Type:     "http",
   379  			Protocol: "http",
   380  			Path:     "/",
   381  			Interval: time.Second,
   382  			Timeout:  time.Second,
   383  			// Removed PortLabel; should default to service's (y)
   384  		},
   385  	}
   386  	if err := ctx.ServiceClient.UpdateTask("allocid", origTask, ctx.Task, ctx, nil); err != nil {
   387  		t.Fatalf("unexpected error registering task: %v", err)
   388  	}
   389  	if err := ctx.syncOnce(); err != nil {
   390  		t.Fatalf("unexpected error syncing task: %v", err)
   391  	}
   392  
   393  	if n := len(ctx.FakeConsul.services); n != 1 {
   394  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   395  	}
   396  
   397  	for k, v := range ctx.FakeConsul.services {
   398  		if k != origServiceKey {
   399  			t.Errorf("unexpected key change; was: %q -- but found %q", origServiceKey, k)
   400  		}
   401  		if v.Name != ctx.Task.Services[0].Name {
   402  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   403  		}
   404  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   405  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   406  		}
   407  		if v.Port != yPort {
   408  			t.Errorf("expected Port y=%v but found: %v", yPort, v.Port)
   409  		}
   410  	}
   411  
   412  	if n := len(ctx.FakeConsul.checks); n != 3 {
   413  		t.Fatalf("expected 3 check but found %d:\n%#v", n, ctx.FakeConsul.checks)
   414  	}
   415  
   416  	for k, v := range ctx.FakeConsul.checks {
   417  		switch v.Name {
   418  		case "c1":
   419  			if k != origTCPKey {
   420  				t.Errorf("unexpected key change for %s from %q to %q", v.Name, origTCPKey, k)
   421  			}
   422  			if expected := fmt.Sprintf(":%d", xPort); v.TCP != expected {
   423  				t.Errorf("expected Port x=%v but found: %v", expected, v.TCP)
   424  			}
   425  		case "c2":
   426  			if k != origScriptKey {
   427  				t.Errorf("unexpected key change for %s from %q to %q", v.Name, origScriptKey, k)
   428  			}
   429  			select {
   430  			case <-ctx.execs:
   431  				if n := len(ctx.execs); n > 0 {
   432  					t.Errorf("expected 1 exec but found: %d", n+1)
   433  				}
   434  			case <-time.After(3 * time.Second):
   435  				t.Errorf("script not called in time")
   436  			}
   437  		case "c3":
   438  			if k == origHTTPKey {
   439  				t.Errorf("expected %s key to change from %q", v.Name, k)
   440  			}
   441  			if expected := fmt.Sprintf("http://:%d/", yPort); v.HTTP != expected {
   442  				t.Errorf("expected Port y=%v but found: %v", expected, v.HTTP)
   443  			}
   444  		default:
   445  			t.Errorf("Unkown check: %q", k)
   446  		}
   447  	}
   448  }
   449  
   450  // TestConsul_ChangeChecks asserts that updating only the checks on a service
   451  // properly syncs with Consul.
   452  func TestConsul_ChangeChecks(t *testing.T) {
   453  	ctx := setupFake()
   454  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   455  		{
   456  			Name:      "c1",
   457  			Type:      "tcp",
   458  			Interval:  time.Second,
   459  			Timeout:   time.Second,
   460  			PortLabel: "x",
   461  		},
   462  	}
   463  
   464  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, ctx, nil); err != nil {
   465  		t.Fatalf("unexpected error registering task: %v", err)
   466  	}
   467  
   468  	if err := ctx.syncOnce(); err != nil {
   469  		t.Fatalf("unexpected error syncing task: %v", err)
   470  	}
   471  
   472  	if n := len(ctx.FakeConsul.services); n != 1 {
   473  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   474  	}
   475  
   476  	origServiceKey := ""
   477  	for k, v := range ctx.FakeConsul.services {
   478  		origServiceKey = k
   479  		if v.Name != ctx.Task.Services[0].Name {
   480  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   481  		}
   482  		if v.Port != xPort {
   483  			t.Errorf("expected Port x=%v but found: %v", xPort, v.Port)
   484  		}
   485  	}
   486  
   487  	if n := len(ctx.FakeConsul.checks); n != 1 {
   488  		t.Fatalf("expected 1 check but found %d:\n%#v", n, ctx.FakeConsul.checks)
   489  	}
   490  	for _, v := range ctx.FakeConsul.checks {
   491  		if v.Name != "c1" {
   492  			t.Fatalf("expected check c1 but found %q", v.Name)
   493  		}
   494  	}
   495  
   496  	// Now add a check
   497  	origTask := ctx.Task.Copy()
   498  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   499  		{
   500  			Name:      "c1",
   501  			Type:      "tcp",
   502  			Interval:  time.Second,
   503  			Timeout:   time.Second,
   504  			PortLabel: "x",
   505  		},
   506  		{
   507  			Name:      "c2",
   508  			Type:      "http",
   509  			Path:      "/",
   510  			Interval:  time.Second,
   511  			Timeout:   time.Second,
   512  			PortLabel: "x",
   513  		},
   514  	}
   515  	if err := ctx.ServiceClient.UpdateTask("allocid", origTask, ctx.Task, ctx, nil); err != nil {
   516  		t.Fatalf("unexpected error registering task: %v", err)
   517  	}
   518  	if err := ctx.syncOnce(); err != nil {
   519  		t.Fatalf("unexpected error syncing task: %v", err)
   520  	}
   521  
   522  	if n := len(ctx.FakeConsul.services); n != 1 {
   523  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   524  	}
   525  
   526  	if _, ok := ctx.FakeConsul.services[origServiceKey]; !ok {
   527  		t.Errorf("unexpected key change; was: %q -- but found %#v", origServiceKey, ctx.FakeConsul.services)
   528  	}
   529  
   530  	if n := len(ctx.FakeConsul.checks); n != 2 {
   531  		t.Fatalf("expected 2 check but found %d:\n%#v", n, ctx.FakeConsul.checks)
   532  	}
   533  
   534  	for k, v := range ctx.FakeConsul.checks {
   535  		switch v.Name {
   536  		case "c1":
   537  			if expected := fmt.Sprintf(":%d", xPort); v.TCP != expected {
   538  				t.Errorf("expected Port x=%v but found: %v", expected, v.TCP)
   539  			}
   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("Unkown check: %q", k)
   546  		}
   547  	}
   548  }
   549  
   550  // TestConsul_RegServices tests basic service registration.
   551  func TestConsul_RegServices(t *testing.T) {
   552  	ctx := setupFake()
   553  
   554  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, nil, nil); err != nil {
   555  		t.Fatalf("unexpected error registering task: %v", err)
   556  	}
   557  
   558  	if err := ctx.syncOnce(); err != nil {
   559  		t.Fatalf("unexpected error syncing task: %v", err)
   560  	}
   561  
   562  	if n := len(ctx.FakeConsul.services); n != 1 {
   563  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   564  	}
   565  	for _, v := range ctx.FakeConsul.services {
   566  		if v.Name != ctx.Task.Services[0].Name {
   567  			t.Errorf("expected Name=%q != %q", ctx.Task.Services[0].Name, v.Name)
   568  		}
   569  		if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   570  			t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   571  		}
   572  		if v.Port != xPort {
   573  			t.Errorf("expected Port=%d != %d", xPort, v.Port)
   574  		}
   575  	}
   576  
   577  	// Make a change which will register a new service
   578  	ctx.Task.Services[0].Name = "taskname-service2"
   579  	ctx.Task.Services[0].Tags[0] = "tag3"
   580  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, nil, nil); err != nil {
   581  		t.Fatalf("unpexpected error registering task: %v", err)
   582  	}
   583  
   584  	// Make sure changes don't take affect until sync() is called (since
   585  	// Run() isn't running)
   586  	if n := len(ctx.FakeConsul.services); n != 1 {
   587  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   588  	}
   589  	for _, v := range ctx.FakeConsul.services {
   590  		if reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   591  			t.Errorf("expected Tags to differ, changes applied before sync()")
   592  		}
   593  	}
   594  
   595  	// Now sync() and re-check for the applied updates
   596  	if err := ctx.syncOnce(); err != nil {
   597  		t.Fatalf("unexpected error syncing task: %v", err)
   598  	}
   599  	if n := len(ctx.FakeConsul.services); n != 2 {
   600  		t.Fatalf("expected 2 services but found %d:\n%#v", n, ctx.FakeConsul.services)
   601  	}
   602  	found := false
   603  	for _, v := range ctx.FakeConsul.services {
   604  		if v.Name == ctx.Task.Services[0].Name {
   605  			if found {
   606  				t.Fatalf("found new service name %q twice", v.Name)
   607  			}
   608  			found = true
   609  			if !reflect.DeepEqual(v.Tags, ctx.Task.Services[0].Tags) {
   610  				t.Errorf("expected Tags=%v != %v", ctx.Task.Services[0].Tags, v.Tags)
   611  			}
   612  		}
   613  	}
   614  	if !found {
   615  		t.Fatalf("did not find new service %q", ctx.Task.Services[0].Name)
   616  	}
   617  
   618  	// Remove the new task
   619  	ctx.ServiceClient.RemoveTask("allocid", ctx.Task)
   620  	if err := ctx.syncOnce(); err != nil {
   621  		t.Fatalf("unexpected error syncing task: %v", err)
   622  	}
   623  	if n := len(ctx.FakeConsul.services); n != 1 {
   624  		t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services)
   625  	}
   626  	for _, v := range ctx.FakeConsul.services {
   627  		if v.Name != "taskname-service" {
   628  			t.Errorf("expected original task to survive not %q", v.Name)
   629  		}
   630  	}
   631  }
   632  
   633  // TestConsul_ShutdownOK tests the ok path for the shutdown logic in
   634  // ServiceClient.
   635  func TestConsul_ShutdownOK(t *testing.T) {
   636  	ctx := setupFake()
   637  
   638  	// Add a script check to make sure its TTL gets updated
   639  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   640  		{
   641  			Name:    "scriptcheck",
   642  			Type:    "script",
   643  			Command: "true",
   644  			// Make check block until shutdown
   645  			Interval:      9000 * time.Hour,
   646  			Timeout:       10 * time.Second,
   647  			InitialStatus: "warning",
   648  		},
   649  	}
   650  
   651  	go ctx.ServiceClient.Run()
   652  
   653  	// Register a task and agent
   654  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, ctx, nil); err != nil {
   655  		t.Fatalf("unexpected error registering task: %v", err)
   656  	}
   657  
   658  	agentServices := []*structs.Service{
   659  		{
   660  			Name:      "http",
   661  			Tags:      []string{"nomad"},
   662  			PortLabel: "localhost:2345",
   663  		},
   664  	}
   665  	if err := ctx.ServiceClient.RegisterAgent("client", agentServices); err != nil {
   666  		t.Fatalf("unexpected error registering agent: %v", err)
   667  	}
   668  
   669  	// Shutdown should block until scripts finish
   670  	if err := ctx.ServiceClient.Shutdown(); err != nil {
   671  		t.Errorf("unexpected error shutting down client: %v", err)
   672  	}
   673  
   674  	// UpdateTTL should have been called once for the script check
   675  	if n := len(ctx.FakeConsul.checkTTLs); n != 1 {
   676  		t.Fatalf("expected 1 checkTTL entry but found: %d", n)
   677  	}
   678  	for _, v := range ctx.FakeConsul.checkTTLs {
   679  		if v != 1 {
   680  			t.Fatalf("expected script check to be updated once but found %d", v)
   681  		}
   682  	}
   683  	for _, v := range ctx.FakeConsul.checks {
   684  		if v.Status != "passing" {
   685  			t.Fatalf("expected check to be passing but found %q", v.Status)
   686  		}
   687  	}
   688  }
   689  
   690  // TestConsul_ShutdownSlow tests the slow but ok path for the shutdown logic in
   691  // ServiceClient.
   692  func TestConsul_ShutdownSlow(t *testing.T) {
   693  	t.Parallel() // run the slow tests in parallel
   694  	ctx := setupFake()
   695  
   696  	// Add a script check to make sure its TTL gets updated
   697  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   698  		{
   699  			Name:    "scriptcheck",
   700  			Type:    "script",
   701  			Command: "true",
   702  			// Make check block until shutdown
   703  			Interval:      9000 * time.Hour,
   704  			Timeout:       5 * time.Second,
   705  			InitialStatus: "warning",
   706  		},
   707  	}
   708  
   709  	// Make Exec slow, but not too slow
   710  	waiter := make(chan struct{})
   711  	ctx.ExecFunc = func(ctx context.Context, cmd string, args []string) ([]byte, int, error) {
   712  		select {
   713  		case <-waiter:
   714  		default:
   715  			close(waiter)
   716  		}
   717  		time.Sleep(time.Second)
   718  		return []byte{}, 0, nil
   719  	}
   720  
   721  	// Make shutdown wait time just a bit longer than ctx.Exec takes
   722  	ctx.ServiceClient.shutdownWait = 3 * time.Second
   723  
   724  	go ctx.ServiceClient.Run()
   725  
   726  	// Register a task and agent
   727  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, ctx, nil); err != nil {
   728  		t.Fatalf("unexpected error registering task: %v", err)
   729  	}
   730  
   731  	// wait for Exec to get called before shutting down
   732  	<-waiter
   733  
   734  	// Shutdown should block until all enqueued operations finish.
   735  	preShutdown := time.Now()
   736  	if err := ctx.ServiceClient.Shutdown(); err != nil {
   737  		t.Errorf("unexpected error shutting down client: %v", err)
   738  	}
   739  
   740  	// Shutdown time should have taken: 1s <= shutdown <= 3s
   741  	shutdownTime := time.Now().Sub(preShutdown)
   742  	if shutdownTime < time.Second || shutdownTime > ctx.ServiceClient.shutdownWait {
   743  		t.Errorf("expected shutdown to take >1s and <%s but took: %s", ctx.ServiceClient.shutdownWait, shutdownTime)
   744  	}
   745  
   746  	// UpdateTTL should have been called once for the script check
   747  	if n := len(ctx.FakeConsul.checkTTLs); n != 1 {
   748  		t.Fatalf("expected 1 checkTTL entry but found: %d", n)
   749  	}
   750  	for _, v := range ctx.FakeConsul.checkTTLs {
   751  		if v != 1 {
   752  			t.Fatalf("expected script check to be updated once but found %d", v)
   753  		}
   754  	}
   755  	for _, v := range ctx.FakeConsul.checks {
   756  		if v.Status != "passing" {
   757  			t.Fatalf("expected check to be passing but found %q", v.Status)
   758  		}
   759  	}
   760  }
   761  
   762  // TestConsul_ShutdownBlocked tests the blocked past deadline path for the
   763  // shutdown logic in ServiceClient.
   764  func TestConsul_ShutdownBlocked(t *testing.T) {
   765  	t.Parallel() // run the slow tests in parallel
   766  	ctx := setupFake()
   767  
   768  	// Add a script check to make sure its TTL gets updated
   769  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   770  		{
   771  			Name:    "scriptcheck",
   772  			Type:    "script",
   773  			Command: "true",
   774  			// Make check block until shutdown
   775  			Interval:      9000 * time.Hour,
   776  			Timeout:       9000 * time.Hour,
   777  			InitialStatus: "warning",
   778  		},
   779  	}
   780  
   781  	block := make(chan struct{})
   782  	defer close(block) // cleanup after test
   783  
   784  	// Make Exec block forever
   785  	waiter := make(chan struct{})
   786  	ctx.ExecFunc = func(ctx context.Context, cmd string, args []string) ([]byte, int, error) {
   787  		close(waiter)
   788  		<-block
   789  		return []byte{}, 0, nil
   790  	}
   791  
   792  	// Use a short shutdown deadline since we're intentionally blocking forever
   793  	ctx.ServiceClient.shutdownWait = time.Second
   794  
   795  	go ctx.ServiceClient.Run()
   796  
   797  	// Register a task and agent
   798  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, ctx, nil); err != nil {
   799  		t.Fatalf("unexpected error registering task: %v", err)
   800  	}
   801  
   802  	// Wait for exec to be called
   803  	<-waiter
   804  
   805  	// Shutdown should block until all enqueued operations finish.
   806  	preShutdown := time.Now()
   807  	err := ctx.ServiceClient.Shutdown()
   808  	if err == nil {
   809  		t.Errorf("expected a timed out error from shutdown")
   810  	}
   811  
   812  	// Shutdown time should have taken shutdownWait; to avoid timing
   813  	// related errors simply test for wait <= shutdown <= wait+3s
   814  	shutdownTime := time.Now().Sub(preShutdown)
   815  	maxWait := ctx.ServiceClient.shutdownWait + (3 * time.Second)
   816  	if shutdownTime < ctx.ServiceClient.shutdownWait || shutdownTime > maxWait {
   817  		t.Errorf("expected shutdown to take >%s and <%s but took: %s", ctx.ServiceClient.shutdownWait, maxWait, shutdownTime)
   818  	}
   819  
   820  	// UpdateTTL should not have been called for the script check
   821  	if n := len(ctx.FakeConsul.checkTTLs); n != 0 {
   822  		t.Fatalf("expected 0 checkTTL entry but found: %d", n)
   823  	}
   824  	for _, v := range ctx.FakeConsul.checks {
   825  		if expected := "warning"; v.Status != expected {
   826  			t.Fatalf("expected check to be %q but found %q", expected, v.Status)
   827  		}
   828  	}
   829  }
   830  
   831  // TestConsul_NoTLSSkipVerifySupport asserts that checks with
   832  // TLSSkipVerify=true are skipped when Consul doesn't support TLSSkipVerify.
   833  func TestConsul_NoTLSSkipVerifySupport(t *testing.T) {
   834  	ctx := setupFake()
   835  	ctx.ServiceClient = NewServiceClient(ctx.FakeConsul, false, testLogger())
   836  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   837  		// This check sets TLSSkipVerify so it should get dropped
   838  		{
   839  			Name:          "tls-check-skip",
   840  			Type:          "http",
   841  			Protocol:      "https",
   842  			Path:          "/",
   843  			TLSSkipVerify: true,
   844  		},
   845  		// This check doesn't set TLSSkipVerify so it should work fine
   846  		{
   847  			Name:          "tls-check-noskip",
   848  			Type:          "http",
   849  			Protocol:      "https",
   850  			Path:          "/",
   851  			TLSSkipVerify: false,
   852  		},
   853  	}
   854  
   855  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, nil, nil); err != nil {
   856  		t.Fatalf("unexpected error registering task: %v", err)
   857  	}
   858  
   859  	if err := ctx.syncOnce(); err != nil {
   860  		t.Fatalf("unexpected error syncing task: %v", err)
   861  	}
   862  
   863  	if len(ctx.FakeConsul.checks) != 1 {
   864  		t.Errorf("expected 1 check but found %d", len(ctx.FakeConsul.checks))
   865  	}
   866  	for _, v := range ctx.FakeConsul.checks {
   867  		if expected := "tls-check-noskip"; v.Name != expected {
   868  			t.Errorf("only expected %q but found: %q", expected, v.Name)
   869  		}
   870  		if v.TLSSkipVerify {
   871  			t.Errorf("TLSSkipVerify=true when TLSSkipVerify not supported!")
   872  		}
   873  	}
   874  }
   875  
   876  // TestConsul_RemoveScript assert removing a script check removes all objects
   877  // related to that check.
   878  func TestConsul_CancelScript(t *testing.T) {
   879  	ctx := setupFake()
   880  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   881  		{
   882  			Name:     "scriptcheckDel",
   883  			Type:     "script",
   884  			Interval: 9000 * time.Hour,
   885  			Timeout:  9000 * time.Hour,
   886  		},
   887  		{
   888  			Name:     "scriptcheckKeep",
   889  			Type:     "script",
   890  			Interval: 9000 * time.Hour,
   891  			Timeout:  9000 * time.Hour,
   892  		},
   893  	}
   894  
   895  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, ctx, nil); err != nil {
   896  		t.Fatalf("unexpected error registering task: %v", err)
   897  	}
   898  
   899  	if err := ctx.syncOnce(); err != nil {
   900  		t.Fatalf("unexpected error syncing task: %v", err)
   901  	}
   902  
   903  	if len(ctx.FakeConsul.checks) != 2 {
   904  		t.Errorf("expected 2 checks but found %d", len(ctx.FakeConsul.checks))
   905  	}
   906  
   907  	if len(ctx.ServiceClient.scripts) != 2 && len(ctx.ServiceClient.runningScripts) != 2 {
   908  		t.Errorf("expected 2 running script but found scripts=%d runningScripts=%d",
   909  			len(ctx.ServiceClient.scripts), len(ctx.ServiceClient.runningScripts))
   910  	}
   911  
   912  	for i := 0; i < 2; i++ {
   913  		select {
   914  		case <-ctx.execs:
   915  			// Script ran as expected!
   916  		case <-time.After(3 * time.Second):
   917  			t.Fatalf("timed out waiting for script check to run")
   918  		}
   919  	}
   920  
   921  	// Remove a check and update the task
   922  	origTask := ctx.Task.Copy()
   923  	ctx.Task.Services[0].Checks = []*structs.ServiceCheck{
   924  		{
   925  			Name:     "scriptcheckKeep",
   926  			Type:     "script",
   927  			Interval: 9000 * time.Hour,
   928  			Timeout:  9000 * time.Hour,
   929  		},
   930  	}
   931  
   932  	if err := ctx.ServiceClient.UpdateTask("allocid", origTask, ctx.Task, ctx, nil); err != nil {
   933  		t.Fatalf("unexpected error registering task: %v", err)
   934  	}
   935  
   936  	if err := ctx.syncOnce(); err != nil {
   937  		t.Fatalf("unexpected error syncing task: %v", err)
   938  	}
   939  
   940  	if len(ctx.FakeConsul.checks) != 1 {
   941  		t.Errorf("expected 1 check but found %d", len(ctx.FakeConsul.checks))
   942  	}
   943  
   944  	if len(ctx.ServiceClient.scripts) != 1 && len(ctx.ServiceClient.runningScripts) != 1 {
   945  		t.Errorf("expected 1 running script but found scripts=%d runningScripts=%d",
   946  			len(ctx.ServiceClient.scripts), len(ctx.ServiceClient.runningScripts))
   947  	}
   948  
   949  	// Make sure exec wasn't called again
   950  	select {
   951  	case <-ctx.execs:
   952  		t.Errorf("unexpected execution of script; was goroutine not cancelled?")
   953  	case <-time.After(100 * time.Millisecond):
   954  		// No unexpected script execs
   955  	}
   956  
   957  	// Don't leak goroutines
   958  	for _, scriptHandle := range ctx.ServiceClient.runningScripts {
   959  		scriptHandle.cancel()
   960  	}
   961  }
   962  
   963  // TestConsul_DriverNetwork_AutoUse asserts that if a driver network has
   964  // auto-use set then services should advertise it unless explicitly set to
   965  // host. Checks should always use host.
   966  func TestConsul_DriverNetwork_AutoUse(t *testing.T) {
   967  	ctx := setupFake()
   968  
   969  	ctx.Task.Services = []*structs.Service{
   970  		{
   971  			Name:        "auto-advertise-x",
   972  			PortLabel:   "x",
   973  			AddressMode: structs.AddressModeAuto,
   974  			Checks: []*structs.ServiceCheck{
   975  				{
   976  					Name:     "default-check-x",
   977  					Type:     "tcp",
   978  					Interval: time.Second,
   979  					Timeout:  time.Second,
   980  				},
   981  				{
   982  					Name:      "weird-y-check",
   983  					Type:      "http",
   984  					Interval:  time.Second,
   985  					Timeout:   time.Second,
   986  					PortLabel: "y",
   987  				},
   988  			},
   989  		},
   990  		{
   991  			Name:        "driver-advertise-y",
   992  			PortLabel:   "y",
   993  			AddressMode: structs.AddressModeDriver,
   994  			Checks: []*structs.ServiceCheck{
   995  				{
   996  					Name:     "default-check-y",
   997  					Type:     "tcp",
   998  					Interval: time.Second,
   999  					Timeout:  time.Second,
  1000  				},
  1001  			},
  1002  		},
  1003  		{
  1004  			Name:        "host-advertise-y",
  1005  			PortLabel:   "y",
  1006  			AddressMode: structs.AddressModeHost,
  1007  		},
  1008  	}
  1009  
  1010  	net := &cstructs.DriverNetwork{
  1011  		PortMap: map[string]int{
  1012  			"x": 8888,
  1013  			"y": 9999,
  1014  		},
  1015  		IP:            "172.18.0.2",
  1016  		AutoAdvertise: true,
  1017  	}
  1018  
  1019  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, ctx, net); err != nil {
  1020  		t.Fatalf("unexpected error registering task: %v", err)
  1021  	}
  1022  
  1023  	if err := ctx.syncOnce(); err != nil {
  1024  		t.Fatalf("unexpected error syncing task: %v", err)
  1025  	}
  1026  
  1027  	if n := len(ctx.FakeConsul.services); n != 3 {
  1028  		t.Fatalf("expected 2 services but found: %d", n)
  1029  	}
  1030  
  1031  	for _, v := range ctx.FakeConsul.services {
  1032  		switch v.Name {
  1033  		case ctx.Task.Services[0].Name: // x
  1034  			// Since DriverNetwork.AutoAdvertise=true, driver ports should be used
  1035  			if v.Port != net.PortMap["x"] {
  1036  				t.Errorf("expected service %s's port to be %d but found %d",
  1037  					v.Name, net.PortMap["x"], v.Port)
  1038  			}
  1039  			// The order of checks in Consul is not guaranteed to
  1040  			// be the same as their order in the Task definition,
  1041  			// so check in a loop
  1042  			if expected := 2; len(v.Checks) != expected {
  1043  				t.Errorf("expected %d checks but found %d", expected, len(v.Checks))
  1044  			}
  1045  			for _, c := range v.Checks {
  1046  				// No name on AgentServiceChecks, use type
  1047  				switch {
  1048  				case c.TCP != "":
  1049  					// Checks should always use host port though
  1050  					if c.TCP != ":1234" { // xPort
  1051  						t.Errorf("exepcted service %s check 1's port to be %d but found %q",
  1052  							v.Name, xPort, c.TCP)
  1053  					}
  1054  				case c.HTTP != "":
  1055  					if c.HTTP != "http://:1235" { // yPort
  1056  						t.Errorf("exepcted service %s check 2's port to be %d but found %q",
  1057  							v.Name, yPort, c.HTTP)
  1058  					}
  1059  				default:
  1060  					t.Errorf("unexpected check %#v on service %q", c, v.Name)
  1061  				}
  1062  			}
  1063  		case ctx.Task.Services[1].Name: // y
  1064  			// Service should be container ip:port
  1065  			if v.Address != net.IP {
  1066  				t.Errorf("expected service %s's address to be %s but found %s",
  1067  					v.Name, net.IP, v.Address)
  1068  			}
  1069  			if v.Port != net.PortMap["y"] {
  1070  				t.Errorf("expected service %s's port to be %d but found %d",
  1071  					v.Name, net.PortMap["x"], v.Port)
  1072  			}
  1073  			// Check should be host ip:port
  1074  			if v.Checks[0].TCP != ":1235" { // yPort
  1075  				t.Errorf("expected service %s check's port to be %d but found %s",
  1076  					v.Name, yPort, v.Checks[0].TCP)
  1077  			}
  1078  		case ctx.Task.Services[2].Name: // y + host mode
  1079  			if v.Port != yPort {
  1080  				t.Errorf("expected service %s's port to be %d but found %d",
  1081  					v.Name, yPort, v.Port)
  1082  			}
  1083  		default:
  1084  			t.Errorf("unexpected service name: %q", v.Name)
  1085  		}
  1086  	}
  1087  }
  1088  
  1089  // TestConsul_DriverNetwork_NoAutoUse asserts that if a driver network doesn't
  1090  // set auto-use only services which request the driver's network should
  1091  // advertise it.
  1092  func TestConsul_DriverNetwork_NoAutoUse(t *testing.T) {
  1093  	ctx := setupFake()
  1094  
  1095  	ctx.Task.Services = []*structs.Service{
  1096  		{
  1097  			Name:        "auto-advertise-x",
  1098  			PortLabel:   "x",
  1099  			AddressMode: structs.AddressModeAuto,
  1100  		},
  1101  		{
  1102  			Name:        "driver-advertise-y",
  1103  			PortLabel:   "y",
  1104  			AddressMode: structs.AddressModeDriver,
  1105  		},
  1106  		{
  1107  			Name:        "host-advertise-y",
  1108  			PortLabel:   "y",
  1109  			AddressMode: structs.AddressModeHost,
  1110  		},
  1111  	}
  1112  
  1113  	net := &cstructs.DriverNetwork{
  1114  		PortMap: map[string]int{
  1115  			"x": 8888,
  1116  			"y": 9999,
  1117  		},
  1118  		IP:            "172.18.0.2",
  1119  		AutoAdvertise: false,
  1120  	}
  1121  
  1122  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, ctx, net); err != nil {
  1123  		t.Fatalf("unexpected error registering task: %v", err)
  1124  	}
  1125  
  1126  	if err := ctx.syncOnce(); err != nil {
  1127  		t.Fatalf("unexpected error syncing task: %v", err)
  1128  	}
  1129  
  1130  	if n := len(ctx.FakeConsul.services); n != 3 {
  1131  		t.Fatalf("expected 3 services but found: %d", n)
  1132  	}
  1133  
  1134  	for _, v := range ctx.FakeConsul.services {
  1135  		switch v.Name {
  1136  		case ctx.Task.Services[0].Name: // x + auto
  1137  			// Since DriverNetwork.AutoAdvertise=false, host ports should be used
  1138  			if v.Port != xPort {
  1139  				t.Errorf("expected service %s's port to be %d but found %d",
  1140  					v.Name, xPort, v.Port)
  1141  			}
  1142  		case ctx.Task.Services[1].Name: // y + driver mode
  1143  			// Service should be container ip:port
  1144  			if v.Address != net.IP {
  1145  				t.Errorf("expected service %s's address to be %s but found %s",
  1146  					v.Name, net.IP, v.Address)
  1147  			}
  1148  			if v.Port != net.PortMap["y"] {
  1149  				t.Errorf("expected service %s's port to be %d but found %d",
  1150  					v.Name, net.PortMap["x"], v.Port)
  1151  			}
  1152  		case ctx.Task.Services[2].Name: // y + host mode
  1153  			if v.Port != yPort {
  1154  				t.Errorf("expected service %s's port to be %d but found %d",
  1155  					v.Name, yPort, v.Port)
  1156  			}
  1157  		default:
  1158  			t.Errorf("unexpected service name: %q", v.Name)
  1159  		}
  1160  	}
  1161  }
  1162  
  1163  // TestConsul_DriverNetwork_Change asserts that if a driver network is
  1164  // specified and a service updates its use its properly updated in Consul.
  1165  func TestConsul_DriverNetwork_Change(t *testing.T) {
  1166  	ctx := setupFake()
  1167  
  1168  	ctx.Task.Services = []*structs.Service{
  1169  		{
  1170  			Name:        "service-foo",
  1171  			PortLabel:   "x",
  1172  			AddressMode: structs.AddressModeAuto,
  1173  		},
  1174  	}
  1175  
  1176  	net := &cstructs.DriverNetwork{
  1177  		PortMap: map[string]int{
  1178  			"x": 8888,
  1179  			"y": 9999,
  1180  		},
  1181  		IP:            "172.18.0.2",
  1182  		AutoAdvertise: false,
  1183  	}
  1184  
  1185  	syncAndAssertPort := func(port int) {
  1186  		if err := ctx.syncOnce(); err != nil {
  1187  			t.Fatalf("unexpected error syncing task: %v", err)
  1188  		}
  1189  
  1190  		if n := len(ctx.FakeConsul.services); n != 1 {
  1191  			t.Fatalf("expected 1 service but found: %d", n)
  1192  		}
  1193  
  1194  		for _, v := range ctx.FakeConsul.services {
  1195  			switch v.Name {
  1196  			case ctx.Task.Services[0].Name:
  1197  				if v.Port != port {
  1198  					t.Errorf("expected service %s's port to be %d but found %d",
  1199  						v.Name, port, v.Port)
  1200  				}
  1201  			default:
  1202  				t.Errorf("unexpected service name: %q", v.Name)
  1203  			}
  1204  		}
  1205  	}
  1206  
  1207  	// Initial service should advertise host port x
  1208  	if err := ctx.ServiceClient.RegisterTask("allocid", ctx.Task, ctx, net); err != nil {
  1209  		t.Fatalf("unexpected error registering task: %v", err)
  1210  	}
  1211  
  1212  	syncAndAssertPort(xPort)
  1213  
  1214  	// UpdateTask to use Host (shouldn't change anything)
  1215  	orig := ctx.Task.Copy()
  1216  	ctx.Task.Services[0].AddressMode = structs.AddressModeHost
  1217  
  1218  	if err := ctx.ServiceClient.UpdateTask("allocid", orig, ctx.Task, ctx, net); err != nil {
  1219  		t.Fatalf("unexpected error updating task: %v", err)
  1220  	}
  1221  
  1222  	syncAndAssertPort(xPort)
  1223  
  1224  	// UpdateTask to use Driver (*should* change IP and port)
  1225  	orig = ctx.Task.Copy()
  1226  	ctx.Task.Services[0].AddressMode = structs.AddressModeDriver
  1227  
  1228  	if err := ctx.ServiceClient.UpdateTask("allocid", orig, ctx.Task, ctx, net); err != nil {
  1229  		t.Fatalf("unexpected error updating task: %v", err)
  1230  	}
  1231  
  1232  	syncAndAssertPort(net.PortMap["x"])
  1233  }
  1234  
  1235  // TestIsNomadService asserts the isNomadService helper returns true for Nomad
  1236  // task IDs and false for unknown IDs and Nomad agent IDs (see #2827).
  1237  func TestIsNomadService(t *testing.T) {
  1238  	tests := []struct {
  1239  		id     string
  1240  		result bool
  1241  	}{
  1242  		{"_nomad-client-nomad-client-http", false},
  1243  		{"_nomad-server-nomad-serf", false},
  1244  		{"_nomad-executor-abc", true},
  1245  		{"_nomad-executor", true},
  1246  		{"not-nomad", false},
  1247  	}
  1248  
  1249  	for _, test := range tests {
  1250  		t.Run(test.id, func(t *testing.T) {
  1251  			actual := isNomadService(test.id)
  1252  			if actual != test.result {
  1253  				t.Errorf("%q should be %t but found %t", test.id, test.result, actual)
  1254  			}
  1255  		})
  1256  	}
  1257  }