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