github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/client/allocrunner/groupservice_hook_test.go (about)

     1  package allocrunner
     2  
     3  import (
     4  	"io/ioutil"
     5  	"testing"
     6  	"time"
     7  
     8  	consulapi "github.com/hashicorp/consul/api"
     9  	ctestutil "github.com/hashicorp/consul/sdk/testutil"
    10  	"github.com/hashicorp/nomad/client/allocrunner/interfaces"
    11  	"github.com/hashicorp/nomad/client/consul"
    12  	"github.com/hashicorp/nomad/client/taskenv"
    13  	agentconsul "github.com/hashicorp/nomad/command/agent/consul"
    14  	"github.com/hashicorp/nomad/helper"
    15  	"github.com/hashicorp/nomad/helper/testlog"
    16  	"github.com/hashicorp/nomad/nomad/mock"
    17  	"github.com/hashicorp/nomad/nomad/structs"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  var _ interfaces.RunnerPrerunHook = (*groupServiceHook)(nil)
    22  var _ interfaces.RunnerUpdateHook = (*groupServiceHook)(nil)
    23  var _ interfaces.RunnerPostrunHook = (*groupServiceHook)(nil)
    24  var _ interfaces.RunnerPreKillHook = (*groupServiceHook)(nil)
    25  var _ interfaces.RunnerTaskRestartHook = (*groupServiceHook)(nil)
    26  
    27  // TestGroupServiceHook_NoGroupServices asserts calling group service hooks
    28  // without group services does not error.
    29  func TestGroupServiceHook_NoGroupServices(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	alloc := mock.Alloc()
    33  	alloc.Job.TaskGroups[0].Services = []*structs.Service{{
    34  		Name:      "foo",
    35  		PortLabel: "9999",
    36  	}}
    37  	logger := testlog.HCLogger(t)
    38  	consulClient := consul.NewMockConsulServiceClient(t, logger)
    39  
    40  	h := newGroupServiceHook(groupServiceHookConfig{
    41  		alloc:          alloc,
    42  		consul:         consulClient,
    43  		restarter:      agentconsul.NoopRestarter(),
    44  		taskEnvBuilder: taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
    45  		logger:         logger,
    46  	})
    47  	require.NoError(t, h.Prerun())
    48  
    49  	req := &interfaces.RunnerUpdateRequest{Alloc: alloc}
    50  	require.NoError(t, h.Update(req))
    51  
    52  	require.NoError(t, h.Postrun())
    53  
    54  	require.NoError(t, h.PreTaskRestart())
    55  
    56  	ops := consulClient.GetOps()
    57  	require.Len(t, ops, 7)
    58  	require.Equal(t, "add", ops[0].Op)    // Prerun
    59  	require.Equal(t, "update", ops[1].Op) // Update
    60  	require.Equal(t, "remove", ops[2].Op) // Postrun (1st)
    61  	require.Equal(t, "remove", ops[3].Op) // Postrun (2nd)
    62  	require.Equal(t, "remove", ops[4].Op) // Restart -> preKill (1st)
    63  	require.Equal(t, "remove", ops[5].Op) // Restart -> preKill (2nd)
    64  	require.Equal(t, "add", ops[6].Op)    // Restart -> preRun
    65  }
    66  
    67  // TestGroupServiceHook_ShutdownDelayUpdate asserts calling group service hooks
    68  // update updates the hooks delay value.
    69  func TestGroupServiceHook_ShutdownDelayUpdate(t *testing.T) {
    70  	t.Parallel()
    71  
    72  	alloc := mock.Alloc()
    73  	alloc.Job.TaskGroups[0].ShutdownDelay = helper.TimeToPtr(10 * time.Second)
    74  
    75  	logger := testlog.HCLogger(t)
    76  	consulClient := consul.NewMockConsulServiceClient(t, logger)
    77  
    78  	h := newGroupServiceHook(groupServiceHookConfig{
    79  		alloc:          alloc,
    80  		consul:         consulClient,
    81  		restarter:      agentconsul.NoopRestarter(),
    82  		taskEnvBuilder: taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
    83  		logger:         logger,
    84  	})
    85  	require.NoError(t, h.Prerun())
    86  
    87  	// Incease shutdown Delay
    88  	alloc.Job.TaskGroups[0].ShutdownDelay = helper.TimeToPtr(15 * time.Second)
    89  	req := &interfaces.RunnerUpdateRequest{Alloc: alloc}
    90  	require.NoError(t, h.Update(req))
    91  
    92  	// Assert that update updated the delay value
    93  	require.Equal(t, h.delay, 15*time.Second)
    94  
    95  	// Remove shutdown delay
    96  	alloc.Job.TaskGroups[0].ShutdownDelay = nil
    97  	req = &interfaces.RunnerUpdateRequest{Alloc: alloc}
    98  	require.NoError(t, h.Update(req))
    99  
   100  	// Assert that update updated the delay value
   101  	require.Equal(t, h.delay, 0*time.Second)
   102  }
   103  
   104  // TestGroupServiceHook_GroupServices asserts group service hooks with group
   105  // services does not error.
   106  func TestGroupServiceHook_GroupServices(t *testing.T) {
   107  	t.Parallel()
   108  
   109  	alloc := mock.ConnectAlloc()
   110  	logger := testlog.HCLogger(t)
   111  	consulClient := consul.NewMockConsulServiceClient(t, logger)
   112  
   113  	h := newGroupServiceHook(groupServiceHookConfig{
   114  		alloc:          alloc,
   115  		consul:         consulClient,
   116  		restarter:      agentconsul.NoopRestarter(),
   117  		taskEnvBuilder: taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
   118  		logger:         logger,
   119  	})
   120  	require.NoError(t, h.Prerun())
   121  
   122  	req := &interfaces.RunnerUpdateRequest{Alloc: alloc}
   123  	require.NoError(t, h.Update(req))
   124  
   125  	require.NoError(t, h.Postrun())
   126  
   127  	require.NoError(t, h.PreTaskRestart())
   128  
   129  	ops := consulClient.GetOps()
   130  	require.Len(t, ops, 7)
   131  	require.Equal(t, "add", ops[0].Op)    // Prerun
   132  	require.Equal(t, "update", ops[1].Op) // Update
   133  	require.Equal(t, "remove", ops[2].Op) // Postrun (1st)
   134  	require.Equal(t, "remove", ops[3].Op) // Postrun (2nd)
   135  	require.Equal(t, "remove", ops[4].Op) // Restart -> preKill (1st)
   136  	require.Equal(t, "remove", ops[5].Op) // Restart -> preKill (2nd)
   137  	require.Equal(t, "add", ops[6].Op)    // Restart -> preRun
   138  }
   139  
   140  // TestGroupServiceHook_Error asserts group service hooks with group
   141  // services but no group network is handled gracefully.
   142  func TestGroupServiceHook_NoNetwork(t *testing.T) {
   143  	t.Parallel()
   144  
   145  	alloc := mock.Alloc()
   146  	alloc.Job.TaskGroups[0].Networks = []*structs.NetworkResource{}
   147  	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
   148  	tg.Services = []*structs.Service{
   149  		{
   150  			Name:      "testconnect",
   151  			PortLabel: "9999",
   152  			Connect: &structs.ConsulConnect{
   153  				SidecarService: &structs.ConsulSidecarService{},
   154  			},
   155  		},
   156  	}
   157  	logger := testlog.HCLogger(t)
   158  
   159  	consulClient := consul.NewMockConsulServiceClient(t, logger)
   160  
   161  	h := newGroupServiceHook(groupServiceHookConfig{
   162  		alloc:          alloc,
   163  		consul:         consulClient,
   164  		restarter:      agentconsul.NoopRestarter(),
   165  		taskEnvBuilder: taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
   166  		logger:         logger,
   167  	})
   168  	require.NoError(t, h.Prerun())
   169  
   170  	req := &interfaces.RunnerUpdateRequest{Alloc: alloc}
   171  	require.NoError(t, h.Update(req))
   172  
   173  	require.NoError(t, h.Postrun())
   174  
   175  	require.NoError(t, h.PreTaskRestart())
   176  
   177  	ops := consulClient.GetOps()
   178  	require.Len(t, ops, 7)
   179  	require.Equal(t, "add", ops[0].Op)    // Prerun
   180  	require.Equal(t, "update", ops[1].Op) // Update
   181  	require.Equal(t, "remove", ops[2].Op) // Postrun (1st)
   182  	require.Equal(t, "remove", ops[3].Op) // Postrun (2nd)
   183  	require.Equal(t, "remove", ops[4].Op) // Restart -> preKill (1st)
   184  	require.Equal(t, "remove", ops[5].Op) // Restart -> preKill (2nd)
   185  	require.Equal(t, "add", ops[6].Op)    // Restart -> preRun
   186  }
   187  
   188  func TestGroupServiceHook_getWorkloadServices(t *testing.T) {
   189  	t.Parallel()
   190  
   191  	alloc := mock.Alloc()
   192  	alloc.Job.TaskGroups[0].Networks = []*structs.NetworkResource{}
   193  	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
   194  	tg.Services = []*structs.Service{
   195  		{
   196  			Name:      "testconnect",
   197  			PortLabel: "9999",
   198  			Connect: &structs.ConsulConnect{
   199  				SidecarService: &structs.ConsulSidecarService{},
   200  			},
   201  		},
   202  	}
   203  	logger := testlog.HCLogger(t)
   204  
   205  	consulClient := consul.NewMockConsulServiceClient(t, logger)
   206  
   207  	h := newGroupServiceHook(groupServiceHookConfig{
   208  		alloc:          alloc,
   209  		consul:         consulClient,
   210  		restarter:      agentconsul.NoopRestarter(),
   211  		taskEnvBuilder: taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
   212  		logger:         logger,
   213  	})
   214  
   215  	services := h.getWorkloadServices()
   216  	require.Len(t, services.Services, 1)
   217  }
   218  
   219  // TestGroupServiceHook_Update08Alloc asserts that adding group services to a previously
   220  // 0.8 alloc works.
   221  //
   222  // COMPAT(0.11) Only valid for upgrades from 0.8.
   223  func TestGroupServiceHook_Update08Alloc(t *testing.T) {
   224  	// Create an embedded Consul server
   225  	testconsul, err := ctestutil.NewTestServerConfigT(t, func(c *ctestutil.TestServerConfig) {
   226  		// If -v wasn't specified squelch consul logging
   227  		if !testing.Verbose() {
   228  			c.Stdout = ioutil.Discard
   229  			c.Stderr = ioutil.Discard
   230  		}
   231  	})
   232  	if err != nil {
   233  		t.Fatalf("error starting test consul server: %v", err)
   234  	}
   235  	defer testconsul.Stop()
   236  
   237  	consulConfig := consulapi.DefaultConfig()
   238  	consulConfig.Address = testconsul.HTTPAddr
   239  	consulClient, err := consulapi.NewClient(consulConfig)
   240  	require.NoError(t, err)
   241  	serviceClient := agentconsul.NewServiceClient(consulClient.Agent(), testlog.HCLogger(t), true)
   242  
   243  	// Lower periodicInterval to ensure periodic syncing doesn't improperly
   244  	// remove Connect services.
   245  	//const interval = 50 * time.Millisecond
   246  	//serviceClient.periodicInterval = interval
   247  
   248  	// Disable deregistration probation to test syncing
   249  	//serviceClient.deregisterProbationExpiry = time.Time{}
   250  
   251  	go serviceClient.Run()
   252  	defer serviceClient.Shutdown()
   253  
   254  	// Create new 0.10-style alloc
   255  	alloc := mock.Alloc()
   256  	alloc.AllocatedResources.Shared.Networks = []*structs.NetworkResource{
   257  		{
   258  			Mode: "bridge",
   259  			IP:   "10.0.0.1",
   260  			DynamicPorts: []structs.Port{
   261  				{
   262  					Label: "connect-proxy-testconnect",
   263  					Value: 9999,
   264  					To:    9998,
   265  				},
   266  			},
   267  		},
   268  	}
   269  	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
   270  	tg.Services = []*structs.Service{
   271  		{
   272  			Name:      "testconnect",
   273  			PortLabel: "9999",
   274  			Connect: &structs.ConsulConnect{
   275  				SidecarService: &structs.ConsulSidecarService{
   276  					Proxy: &structs.ConsulProxy{
   277  						LocalServicePort: 9000,
   278  					},
   279  				},
   280  			},
   281  		},
   282  	}
   283  
   284  	// Create old 0.8-style alloc from new alloc
   285  	oldAlloc := alloc.Copy()
   286  	oldAlloc.AllocatedResources = nil
   287  	oldAlloc.Job.LookupTaskGroup(alloc.TaskGroup).Services = nil
   288  
   289  	// Create the group service hook
   290  	h := newGroupServiceHook(groupServiceHookConfig{
   291  		alloc:          oldAlloc,
   292  		consul:         serviceClient,
   293  		restarter:      agentconsul.NoopRestarter(),
   294  		taskEnvBuilder: taskenv.NewBuilder(mock.Node(), oldAlloc, nil, oldAlloc.Job.Region),
   295  		logger:         testlog.HCLogger(t),
   296  	})
   297  
   298  	require.NoError(t, h.Prerun())
   299  	require.NoError(t, h.Update(&interfaces.RunnerUpdateRequest{Alloc: alloc}))
   300  
   301  	// Assert the group and sidecar services are registered
   302  	require.Eventually(t, func() bool {
   303  		services, err := consulClient.Agent().Services()
   304  		require.NoError(t, err)
   305  		return len(services) == 2
   306  	}, 3*time.Second, 100*time.Millisecond)
   307  
   308  }