github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/allocrunner/groupservice_hook.go (about) 1 package allocrunner 2 3 import ( 4 "sync" 5 "time" 6 7 log "github.com/hashicorp/go-hclog" 8 "github.com/hashicorp/nomad/client/allocrunner/interfaces" 9 "github.com/hashicorp/nomad/client/consul" 10 "github.com/hashicorp/nomad/client/taskenv" 11 agentconsul "github.com/hashicorp/nomad/command/agent/consul" 12 "github.com/hashicorp/nomad/nomad/structs" 13 "github.com/hashicorp/nomad/plugins/drivers" 14 ) 15 16 // groupServiceHook manages task group Consul service registration and 17 // deregistration. 18 type groupServiceHook struct { 19 allocID string 20 group string 21 restarter agentconsul.WorkloadRestarter 22 consulClient consul.ConsulServiceAPI 23 prerun bool 24 delay time.Duration 25 deregistered bool 26 27 logger log.Logger 28 29 // The following fields may be updated 30 canary bool 31 services []*structs.Service 32 networks structs.Networks 33 taskEnvBuilder *taskenv.Builder 34 35 // Since Update() may be called concurrently with any other hook all 36 // hook methods must be fully serialized 37 mu sync.Mutex 38 } 39 40 type groupServiceHookConfig struct { 41 alloc *structs.Allocation 42 consul consul.ConsulServiceAPI 43 restarter agentconsul.WorkloadRestarter 44 taskEnvBuilder *taskenv.Builder 45 logger log.Logger 46 } 47 48 func newGroupServiceHook(cfg groupServiceHookConfig) *groupServiceHook { 49 var shutdownDelay time.Duration 50 tg := cfg.alloc.Job.LookupTaskGroup(cfg.alloc.TaskGroup) 51 52 if tg.ShutdownDelay != nil { 53 shutdownDelay = *tg.ShutdownDelay 54 } 55 56 h := &groupServiceHook{ 57 allocID: cfg.alloc.ID, 58 group: cfg.alloc.TaskGroup, 59 restarter: cfg.restarter, 60 consulClient: cfg.consul, 61 taskEnvBuilder: cfg.taskEnvBuilder, 62 delay: shutdownDelay, 63 } 64 h.logger = cfg.logger.Named(h.Name()) 65 h.services = cfg.alloc.Job.LookupTaskGroup(h.group).Services 66 67 if cfg.alloc.AllocatedResources != nil { 68 h.networks = cfg.alloc.AllocatedResources.Shared.Networks 69 } 70 71 if cfg.alloc.DeploymentStatus != nil { 72 h.canary = cfg.alloc.DeploymentStatus.Canary 73 } 74 return h 75 } 76 77 func (*groupServiceHook) Name() string { 78 return "group_services" 79 } 80 81 func (h *groupServiceHook) Prerun() error { 82 h.mu.Lock() 83 defer func() { 84 // Mark prerun as true to unblock Updates 85 h.prerun = true 86 h.mu.Unlock() 87 }() 88 89 if len(h.services) == 0 { 90 return nil 91 } 92 93 services := h.getWorkloadServices() 94 return h.consulClient.RegisterWorkload(services) 95 } 96 97 func (h *groupServiceHook) Update(req *interfaces.RunnerUpdateRequest) error { 98 h.mu.Lock() 99 defer h.mu.Unlock() 100 101 oldWorkloadServices := h.getWorkloadServices() 102 103 // Store new updated values out of request 104 canary := false 105 if req.Alloc.DeploymentStatus != nil { 106 canary = req.Alloc.DeploymentStatus.Canary 107 } 108 109 var networks structs.Networks 110 if req.Alloc.AllocatedResources != nil { 111 networks = req.Alloc.AllocatedResources.Shared.Networks 112 } 113 114 tg := req.Alloc.Job.LookupTaskGroup(h.group) 115 var shutdown time.Duration 116 if tg.ShutdownDelay != nil { 117 shutdown = *tg.ShutdownDelay 118 } 119 120 // Update group service hook fields 121 h.networks = networks 122 h.services = tg.Services 123 h.canary = canary 124 h.delay = shutdown 125 h.taskEnvBuilder.UpdateTask(req.Alloc, nil) 126 127 // Create new task services struct with those new values 128 newWorkloadServices := h.getWorkloadServices() 129 130 if !h.prerun { 131 // Update called before Prerun. Update alloc and exit to allow 132 // Prerun to do initial registration. 133 return nil 134 } 135 136 return h.consulClient.UpdateWorkload(oldWorkloadServices, newWorkloadServices) 137 } 138 139 func (h *groupServiceHook) PreKill() { 140 h.mu.Lock() 141 defer h.mu.Unlock() 142 143 // If we have a shutdown delay deregister 144 // group services and then wait 145 // before continuing to kill tasks 146 h.deregister() 147 h.deregistered = true 148 149 if h.delay == 0 { 150 return 151 } 152 153 h.logger.Debug("waiting before removing group service", "shutdown_delay", h.delay) 154 155 // Wait for specified shutdown_delay 156 // this will block an agent from shutting down 157 <-time.After(h.delay) 158 } 159 160 func (h *groupServiceHook) Postrun() error { 161 h.mu.Lock() 162 defer h.mu.Unlock() 163 164 if !h.deregistered { 165 h.deregister() 166 } 167 return nil 168 } 169 170 func (h *groupServiceHook) driverNet() *drivers.DriverNetwork { 171 if len(h.networks) == 0 { 172 return nil 173 } 174 175 //TODO(schmichael) only support one network for now 176 net := h.networks[0] 177 //TODO(schmichael) there's probably a better way than hacking driver network 178 return &drivers.DriverNetwork{ 179 AutoAdvertise: true, 180 IP: net.IP, 181 // Copy PortLabels from group network 182 PortMap: net.PortLabels(), 183 } 184 } 185 186 // deregister services from Consul. 187 func (h *groupServiceHook) deregister() { 188 if len(h.services) > 0 { 189 workloadServices := h.getWorkloadServices() 190 h.consulClient.RemoveWorkload(workloadServices) 191 192 // Canary flag may be getting flipped when the alloc is being 193 // destroyed, so remove both variations of the service 194 workloadServices.Canary = !workloadServices.Canary 195 h.consulClient.RemoveWorkload(workloadServices) 196 } 197 } 198 199 func (h *groupServiceHook) getWorkloadServices() *agentconsul.WorkloadServices { 200 // Interpolate with the task's environment 201 interpolatedServices := taskenv.InterpolateServices(h.taskEnvBuilder.Build(), h.services) 202 203 // Create task services struct with request's driver metadata 204 return &agentconsul.WorkloadServices{ 205 AllocID: h.allocID, 206 Group: h.group, 207 Restarter: h.restarter, 208 Services: interpolatedServices, 209 DriverNetwork: h.driverNet(), 210 Networks: h.networks, 211 Canary: h.canary, 212 } 213 }