github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/allocrunner/group_service_hook_test.go (about)

     1  package allocrunner
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/hashicorp/nomad/ci"
     8  	"github.com/hashicorp/nomad/client/allocrunner/interfaces"
     9  	regMock "github.com/hashicorp/nomad/client/serviceregistration/mock"
    10  	"github.com/hashicorp/nomad/client/serviceregistration/wrapper"
    11  	"github.com/hashicorp/nomad/client/taskenv"
    12  	agentconsul "github.com/hashicorp/nomad/command/agent/consul"
    13  	"github.com/hashicorp/nomad/helper/pointer"
    14  	"github.com/hashicorp/nomad/helper/testlog"
    15  	"github.com/hashicorp/nomad/nomad/mock"
    16  	"github.com/hashicorp/nomad/nomad/structs"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  var _ interfaces.RunnerPrerunHook = (*groupServiceHook)(nil)
    21  var _ interfaces.RunnerUpdateHook = (*groupServiceHook)(nil)
    22  var _ interfaces.RunnerPostrunHook = (*groupServiceHook)(nil)
    23  var _ interfaces.RunnerPreKillHook = (*groupServiceHook)(nil)
    24  var _ interfaces.RunnerTaskRestartHook = (*groupServiceHook)(nil)
    25  
    26  // TestGroupServiceHook_NoGroupServices asserts calling group service hooks
    27  // without group services does not error.
    28  func TestGroupServiceHook_NoGroupServices(t *testing.T) {
    29  	ci.Parallel(t)
    30  
    31  	alloc := mock.Alloc()
    32  	alloc.Job.TaskGroups[0].Services = []*structs.Service{{
    33  		Name:      "foo",
    34  		Provider:  "consul",
    35  		PortLabel: "9999",
    36  	}}
    37  	logger := testlog.HCLogger(t)
    38  
    39  	consulMockClient := regMock.NewServiceRegistrationHandler(logger)
    40  
    41  	regWrapper := wrapper.NewHandlerWrapper(
    42  		logger,
    43  		consulMockClient,
    44  		regMock.NewServiceRegistrationHandler(logger))
    45  
    46  	h := newGroupServiceHook(groupServiceHookConfig{
    47  		alloc:             alloc,
    48  		serviceRegWrapper: regWrapper,
    49  		restarter:         agentconsul.NoopRestarter(),
    50  		taskEnvBuilder:    taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
    51  		logger:            logger,
    52  	})
    53  	require.NoError(t, h.Prerun())
    54  
    55  	req := &interfaces.RunnerUpdateRequest{Alloc: alloc}
    56  	require.NoError(t, h.Update(req))
    57  
    58  	require.NoError(t, h.Postrun())
    59  
    60  	require.NoError(t, h.PreTaskRestart())
    61  
    62  	ops := consulMockClient.GetOps()
    63  	require.Len(t, ops, 5)
    64  	require.Equal(t, "add", ops[0].Op)    // Prerun
    65  	require.Equal(t, "update", ops[1].Op) // Update
    66  	require.Equal(t, "remove", ops[2].Op) // Postrun
    67  	require.Equal(t, "remove", ops[3].Op) // Restart -> preKill
    68  	require.Equal(t, "add", ops[4].Op)    // Restart -> preRun
    69  }
    70  
    71  // TestGroupServiceHook_ShutdownDelayUpdate asserts calling group service hooks
    72  // update updates the hooks delay value.
    73  func TestGroupServiceHook_ShutdownDelayUpdate(t *testing.T) {
    74  	ci.Parallel(t)
    75  
    76  	alloc := mock.Alloc()
    77  	alloc.Job.TaskGroups[0].ShutdownDelay = pointer.Of(10 * time.Second)
    78  
    79  	logger := testlog.HCLogger(t)
    80  	consulMockClient := regMock.NewServiceRegistrationHandler(logger)
    81  
    82  	regWrapper := wrapper.NewHandlerWrapper(
    83  		logger,
    84  		consulMockClient,
    85  		regMock.NewServiceRegistrationHandler(logger),
    86  	)
    87  
    88  	h := newGroupServiceHook(groupServiceHookConfig{
    89  		alloc:             alloc,
    90  		serviceRegWrapper: regWrapper,
    91  		restarter:         agentconsul.NoopRestarter(),
    92  		taskEnvBuilder:    taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
    93  		logger:            logger,
    94  	})
    95  	require.NoError(t, h.Prerun())
    96  
    97  	// Incease shutdown Delay
    98  	alloc.Job.TaskGroups[0].ShutdownDelay = pointer.Of(15 * time.Second)
    99  	req := &interfaces.RunnerUpdateRequest{Alloc: alloc}
   100  	require.NoError(t, h.Update(req))
   101  
   102  	// Assert that update updated the delay value
   103  	require.Equal(t, h.delay, 15*time.Second)
   104  
   105  	// Remove shutdown delay
   106  	alloc.Job.TaskGroups[0].ShutdownDelay = nil
   107  	req = &interfaces.RunnerUpdateRequest{Alloc: alloc}
   108  	require.NoError(t, h.Update(req))
   109  
   110  	// Assert that update updated the delay value
   111  	require.Equal(t, h.delay, 0*time.Second)
   112  }
   113  
   114  // TestGroupServiceHook_GroupServices asserts group service hooks with group
   115  // services does not error.
   116  func TestGroupServiceHook_GroupServices(t *testing.T) {
   117  	ci.Parallel(t)
   118  
   119  	alloc := mock.ConnectAlloc()
   120  	alloc.Job.Canonicalize()
   121  	logger := testlog.HCLogger(t)
   122  	consulMockClient := regMock.NewServiceRegistrationHandler(logger)
   123  
   124  	regWrapper := wrapper.NewHandlerWrapper(
   125  		logger,
   126  		consulMockClient,
   127  		regMock.NewServiceRegistrationHandler(logger))
   128  
   129  	h := newGroupServiceHook(groupServiceHookConfig{
   130  		alloc:             alloc,
   131  		serviceRegWrapper: regWrapper,
   132  		restarter:         agentconsul.NoopRestarter(),
   133  		taskEnvBuilder:    taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
   134  		logger:            logger,
   135  	})
   136  	require.NoError(t, h.Prerun())
   137  
   138  	req := &interfaces.RunnerUpdateRequest{Alloc: alloc}
   139  	require.NoError(t, h.Update(req))
   140  
   141  	require.NoError(t, h.Postrun())
   142  
   143  	require.NoError(t, h.PreTaskRestart())
   144  
   145  	ops := consulMockClient.GetOps()
   146  	require.Len(t, ops, 5)
   147  	require.Equal(t, "add", ops[0].Op)    // Prerun
   148  	require.Equal(t, "update", ops[1].Op) // Update
   149  	require.Equal(t, "remove", ops[2].Op) // Postrun
   150  	require.Equal(t, "remove", ops[3].Op) // Restart -> preKill
   151  	require.Equal(t, "add", ops[4].Op)    // Restart -> preRun
   152  }
   153  
   154  // TestGroupServiceHook_GroupServices_Nomad asserts group service hooks with
   155  // group services does not error when using the Nomad provider.
   156  func TestGroupServiceHook_GroupServices_Nomad(t *testing.T) {
   157  	ci.Parallel(t)
   158  
   159  	// Create a mock alloc, and add a group service using provider Nomad.
   160  	alloc := mock.Alloc()
   161  	alloc.Job.TaskGroups[0].Services = []*structs.Service{
   162  		{
   163  			Name:     "nomad-provider-service",
   164  			Provider: structs.ServiceProviderNomad,
   165  		},
   166  	}
   167  
   168  	// Create our base objects and our subsequent wrapper.
   169  	logger := testlog.HCLogger(t)
   170  	consulMockClient := regMock.NewServiceRegistrationHandler(logger)
   171  	nomadMockClient := regMock.NewServiceRegistrationHandler(logger)
   172  
   173  	regWrapper := wrapper.NewHandlerWrapper(logger, consulMockClient, nomadMockClient)
   174  
   175  	h := newGroupServiceHook(groupServiceHookConfig{
   176  		alloc:             alloc,
   177  		serviceRegWrapper: regWrapper,
   178  		restarter:         agentconsul.NoopRestarter(),
   179  		taskEnvBuilder:    taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
   180  		logger:            logger,
   181  	})
   182  	require.NoError(t, h.Prerun())
   183  
   184  	// Trigger our hook requests.
   185  	req := &interfaces.RunnerUpdateRequest{Alloc: alloc}
   186  	require.NoError(t, h.Update(req))
   187  	require.NoError(t, h.Postrun())
   188  	require.NoError(t, h.PreTaskRestart())
   189  
   190  	// Ensure the Nomad mock provider has the expected operations.
   191  	ops := nomadMockClient.GetOps()
   192  	require.Len(t, ops, 5)
   193  	require.Equal(t, "add", ops[0].Op)    // Prerun
   194  	require.Equal(t, "update", ops[1].Op) // Update
   195  	require.Equal(t, "remove", ops[2].Op) // Postrun
   196  	require.Equal(t, "remove", ops[3].Op) // Restart -> preKill
   197  	require.Equal(t, "add", ops[4].Op)    // Restart -> preRun
   198  
   199  	// Ensure the Consul mock provider has zero operations.
   200  	require.Len(t, consulMockClient.GetOps(), 0)
   201  }
   202  
   203  // TestGroupServiceHook_Error asserts group service hooks with group
   204  // services but no group network is handled gracefully.
   205  func TestGroupServiceHook_NoNetwork(t *testing.T) {
   206  	ci.Parallel(t)
   207  
   208  	alloc := mock.Alloc()
   209  	alloc.Job.TaskGroups[0].Networks = []*structs.NetworkResource{}
   210  	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
   211  	tg.Services = []*structs.Service{
   212  		{
   213  			Name:      "testconnect",
   214  			Provider:  "consul",
   215  			PortLabel: "9999",
   216  			Connect: &structs.ConsulConnect{
   217  				SidecarService: &structs.ConsulSidecarService{},
   218  			},
   219  		},
   220  	}
   221  	logger := testlog.HCLogger(t)
   222  
   223  	consulMockClient := regMock.NewServiceRegistrationHandler(logger)
   224  
   225  	regWrapper := wrapper.NewHandlerWrapper(
   226  		logger,
   227  		consulMockClient,
   228  		regMock.NewServiceRegistrationHandler(logger))
   229  
   230  	h := newGroupServiceHook(groupServiceHookConfig{
   231  		alloc:             alloc,
   232  		serviceRegWrapper: regWrapper,
   233  		restarter:         agentconsul.NoopRestarter(),
   234  		taskEnvBuilder:    taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
   235  		logger:            logger,
   236  	})
   237  	require.NoError(t, h.Prerun())
   238  
   239  	req := &interfaces.RunnerUpdateRequest{Alloc: alloc}
   240  	require.NoError(t, h.Update(req))
   241  
   242  	require.NoError(t, h.Postrun())
   243  
   244  	require.NoError(t, h.PreTaskRestart())
   245  
   246  	ops := consulMockClient.GetOps()
   247  	require.Len(t, ops, 5)
   248  	require.Equal(t, "add", ops[0].Op)    // Prerun
   249  	require.Equal(t, "update", ops[1].Op) // Update
   250  	require.Equal(t, "remove", ops[2].Op) // Postrun
   251  	require.Equal(t, "remove", ops[3].Op) // Restart -> preKill
   252  	require.Equal(t, "add", ops[4].Op)    // Restart -> preRun
   253  }
   254  
   255  func TestGroupServiceHook_getWorkloadServices(t *testing.T) {
   256  	ci.Parallel(t)
   257  
   258  	alloc := mock.Alloc()
   259  	alloc.Job.TaskGroups[0].Networks = []*structs.NetworkResource{}
   260  	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
   261  	tg.Services = []*structs.Service{
   262  		{
   263  			Name:      "testconnect",
   264  			PortLabel: "9999",
   265  			Connect: &structs.ConsulConnect{
   266  				SidecarService: &structs.ConsulSidecarService{},
   267  			},
   268  		},
   269  	}
   270  	logger := testlog.HCLogger(t)
   271  
   272  	consulMockClient := regMock.NewServiceRegistrationHandler(logger)
   273  
   274  	regWrapper := wrapper.NewHandlerWrapper(
   275  		logger,
   276  		consulMockClient,
   277  		regMock.NewServiceRegistrationHandler(logger))
   278  
   279  	h := newGroupServiceHook(groupServiceHookConfig{
   280  		alloc:             alloc,
   281  		serviceRegWrapper: regWrapper,
   282  		restarter:         agentconsul.NoopRestarter(),
   283  		taskEnvBuilder:    taskenv.NewBuilder(mock.Node(), alloc, nil, alloc.Job.Region),
   284  		logger:            logger,
   285  	})
   286  
   287  	services := h.getWorkloadServices()
   288  	require.Len(t, services.Services, 1)
   289  }