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