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 }