github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/client/allocrunner/taskrunner/service_hook.go (about) 1 package taskrunner 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 8 log "github.com/hashicorp/go-hclog" 9 "github.com/hashicorp/nomad/client/allocrunner/interfaces" 10 tinterfaces "github.com/hashicorp/nomad/client/allocrunner/taskrunner/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/nomad/structs" 15 "github.com/hashicorp/nomad/plugins/drivers" 16 ) 17 18 var _ interfaces.TaskPoststartHook = &serviceHook{} 19 var _ interfaces.TaskPreKillHook = &serviceHook{} 20 var _ interfaces.TaskExitedHook = &serviceHook{} 21 var _ interfaces.TaskStopHook = &serviceHook{} 22 23 type serviceHookConfig struct { 24 alloc *structs.Allocation 25 task *structs.Task 26 consul consul.ConsulServiceAPI 27 28 // Restarter is a subset of the TaskLifecycle interface 29 restarter agentconsul.WorkloadRestarter 30 31 logger log.Logger 32 } 33 34 type serviceHook struct { 35 consul consul.ConsulServiceAPI 36 allocID string 37 taskName string 38 restarter agentconsul.WorkloadRestarter 39 logger log.Logger 40 41 // The following fields may be updated 42 driverExec tinterfaces.ScriptExecutor 43 driverNet *drivers.DriverNetwork 44 canary bool 45 services []*structs.Service 46 networks structs.Networks 47 ports structs.AllocatedPorts 48 taskEnv *taskenv.TaskEnv 49 50 // initialRegistrations tracks if Poststart has completed, initializing 51 // fields required in other lifecycle funcs 52 initialRegistration bool 53 54 // Since Update() may be called concurrently with any other hook all 55 // hook methods must be fully serialized 56 mu sync.Mutex 57 } 58 59 func newServiceHook(c serviceHookConfig) *serviceHook { 60 h := &serviceHook{ 61 consul: c.consul, 62 allocID: c.alloc.ID, 63 taskName: c.task.Name, 64 services: c.task.Services, 65 restarter: c.restarter, 66 ports: c.alloc.AllocatedResources.Shared.Ports, 67 } 68 69 if res := c.alloc.AllocatedResources.Tasks[c.task.Name]; res != nil { 70 h.networks = res.Networks 71 } 72 73 if c.alloc.DeploymentStatus != nil && c.alloc.DeploymentStatus.Canary { 74 h.canary = true 75 } 76 77 h.logger = c.logger.Named(h.Name()) 78 return h 79 } 80 81 func (h *serviceHook) Name() string { 82 return "consul_services" 83 } 84 85 func (h *serviceHook) Poststart(ctx context.Context, req *interfaces.TaskPoststartRequest, _ *interfaces.TaskPoststartResponse) error { 86 h.mu.Lock() 87 defer h.mu.Unlock() 88 89 // Store the TaskEnv for interpolating now and when Updating 90 h.driverExec = req.DriverExec 91 h.driverNet = req.DriverNetwork 92 h.taskEnv = req.TaskEnv 93 h.initialRegistration = true 94 95 // Create task services struct with request's driver metadata 96 workloadServices := h.getWorkloadServices() 97 98 return h.consul.RegisterWorkload(workloadServices) 99 } 100 101 func (h *serviceHook) Update(ctx context.Context, req *interfaces.TaskUpdateRequest, _ *interfaces.TaskUpdateResponse) error { 102 h.mu.Lock() 103 defer h.mu.Unlock() 104 if !h.initialRegistration { 105 // no op Consul since initial registration has not finished 106 // only update hook fields 107 return h.updateHookFields(req) 108 } 109 110 // Create old task services struct with request's driver metadata as it 111 // can't change due to Updates 112 oldWorkloadServices := h.getWorkloadServices() 113 114 if err := h.updateHookFields(req); err != nil { 115 return err 116 } 117 118 // Create new task services struct with those new values 119 newWorkloadServices := h.getWorkloadServices() 120 121 return h.consul.UpdateWorkload(oldWorkloadServices, newWorkloadServices) 122 } 123 124 func (h *serviceHook) updateHookFields(req *interfaces.TaskUpdateRequest) error { 125 // Store new updated values out of request 126 canary := false 127 if req.Alloc.DeploymentStatus != nil { 128 canary = req.Alloc.DeploymentStatus.Canary 129 } 130 131 var networks structs.Networks 132 if res := req.Alloc.AllocatedResources.Tasks[h.taskName]; res != nil { 133 networks = res.Networks 134 } 135 136 task := req.Alloc.LookupTask(h.taskName) 137 if task == nil { 138 return fmt.Errorf("task %q not found in updated alloc", h.taskName) 139 } 140 141 // Update service hook fields 142 h.taskEnv = req.TaskEnv 143 h.services = task.Services 144 h.networks = networks 145 h.canary = canary 146 h.ports = req.Alloc.AllocatedResources.Shared.Ports 147 148 return nil 149 } 150 151 func (h *serviceHook) PreKilling(ctx context.Context, req *interfaces.TaskPreKillRequest, resp *interfaces.TaskPreKillResponse) error { 152 h.mu.Lock() 153 defer h.mu.Unlock() 154 155 // Deregister before killing task 156 h.deregister() 157 158 return nil 159 } 160 161 func (h *serviceHook) Exited(context.Context, *interfaces.TaskExitedRequest, *interfaces.TaskExitedResponse) error { 162 h.mu.Lock() 163 defer h.mu.Unlock() 164 h.deregister() 165 return nil 166 } 167 168 // deregister services from Consul. 169 func (h *serviceHook) deregister() { 170 workloadServices := h.getWorkloadServices() 171 h.consul.RemoveWorkload(workloadServices) 172 173 // Canary flag may be getting flipped when the alloc is being 174 // destroyed, so remove both variations of the service 175 workloadServices.Canary = !workloadServices.Canary 176 h.consul.RemoveWorkload(workloadServices) 177 h.initialRegistration = false 178 } 179 180 func (h *serviceHook) Stop(ctx context.Context, req *interfaces.TaskStopRequest, resp *interfaces.TaskStopResponse) error { 181 h.mu.Lock() 182 defer h.mu.Unlock() 183 h.deregister() 184 return nil 185 } 186 187 func (h *serviceHook) getWorkloadServices() *agentconsul.WorkloadServices { 188 // Interpolate with the task's environment 189 interpolatedServices := taskenv.InterpolateServices(h.taskEnv, h.services) 190 191 // Create task services struct with request's driver metadata 192 return &agentconsul.WorkloadServices{ 193 AllocID: h.allocID, 194 Task: h.taskName, 195 Restarter: h.restarter, 196 Services: interpolatedServices, 197 DriverExec: h.driverExec, 198 DriverNetwork: h.driverNet, 199 Networks: h.networks, 200 Canary: h.canary, 201 Ports: h.ports, 202 } 203 }