github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/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  	taskEnv    *taskenv.TaskEnv
    48  
    49  	// initialRegistrations tracks if Poststart has completed, initializing
    50  	// fields required in other lifecycle funcs
    51  	initialRegistration bool
    52  
    53  	// Since Update() may be called concurrently with any other hook all
    54  	// hook methods must be fully serialized
    55  	mu sync.Mutex
    56  }
    57  
    58  func newServiceHook(c serviceHookConfig) *serviceHook {
    59  	h := &serviceHook{
    60  		consul:    c.consul,
    61  		allocID:   c.alloc.ID,
    62  		taskName:  c.task.Name,
    63  		services:  c.task.Services,
    64  		restarter: c.restarter,
    65  	}
    66  
    67  	if res := c.alloc.AllocatedResources.Tasks[c.task.Name]; res != nil {
    68  		h.networks = res.Networks
    69  	}
    70  
    71  	if c.alloc.DeploymentStatus != nil && c.alloc.DeploymentStatus.Canary {
    72  		h.canary = true
    73  	}
    74  
    75  	h.logger = c.logger.Named(h.Name())
    76  	return h
    77  }
    78  
    79  func (h *serviceHook) Name() string {
    80  	return "consul_services"
    81  }
    82  
    83  func (h *serviceHook) Poststart(ctx context.Context, req *interfaces.TaskPoststartRequest, _ *interfaces.TaskPoststartResponse) error {
    84  	h.mu.Lock()
    85  	defer h.mu.Unlock()
    86  
    87  	// Store the TaskEnv for interpolating now and when Updating
    88  	h.driverExec = req.DriverExec
    89  	h.driverNet = req.DriverNetwork
    90  	h.taskEnv = req.TaskEnv
    91  	h.initialRegistration = true
    92  
    93  	// Create task services struct with request's driver metadata
    94  	workloadServices := h.getWorkloadServices()
    95  
    96  	return h.consul.RegisterWorkload(workloadServices)
    97  }
    98  
    99  func (h *serviceHook) Update(ctx context.Context, req *interfaces.TaskUpdateRequest, _ *interfaces.TaskUpdateResponse) error {
   100  	h.mu.Lock()
   101  	defer h.mu.Unlock()
   102  	if !h.initialRegistration {
   103  		// no op Consul since initial registration has not finished
   104  		// only update hook fields
   105  		return h.updateHookFields(req)
   106  	}
   107  
   108  	// Create old task services struct with request's driver metadata as it
   109  	// can't change due to Updates
   110  	oldWorkloadServices := h.getWorkloadServices()
   111  
   112  	if err := h.updateHookFields(req); err != nil {
   113  		return err
   114  	}
   115  
   116  	// Create new task services struct with those new values
   117  	newWorkloadServices := h.getWorkloadServices()
   118  
   119  	return h.consul.UpdateWorkload(oldWorkloadServices, newWorkloadServices)
   120  }
   121  
   122  func (h *serviceHook) updateHookFields(req *interfaces.TaskUpdateRequest) error {
   123  	// Store new updated values out of request
   124  	canary := false
   125  	if req.Alloc.DeploymentStatus != nil {
   126  		canary = req.Alloc.DeploymentStatus.Canary
   127  	}
   128  
   129  	var networks structs.Networks
   130  	if res := req.Alloc.AllocatedResources.Tasks[h.taskName]; res != nil {
   131  		networks = res.Networks
   132  	}
   133  
   134  	task := req.Alloc.LookupTask(h.taskName)
   135  	if task == nil {
   136  		return fmt.Errorf("task %q not found in updated alloc", h.taskName)
   137  	}
   138  
   139  	// Update service hook fields
   140  	h.taskEnv = req.TaskEnv
   141  	h.services = task.Services
   142  	h.networks = networks
   143  	h.canary = canary
   144  
   145  	return nil
   146  }
   147  
   148  func (h *serviceHook) PreKilling(ctx context.Context, req *interfaces.TaskPreKillRequest, resp *interfaces.TaskPreKillResponse) error {
   149  	h.mu.Lock()
   150  	defer h.mu.Unlock()
   151  
   152  	// Deregister before killing task
   153  	h.deregister()
   154  
   155  	return nil
   156  }
   157  
   158  func (h *serviceHook) Exited(context.Context, *interfaces.TaskExitedRequest, *interfaces.TaskExitedResponse) error {
   159  	h.mu.Lock()
   160  	defer h.mu.Unlock()
   161  	h.deregister()
   162  	return nil
   163  }
   164  
   165  // deregister services from Consul.
   166  func (h *serviceHook) deregister() {
   167  	workloadServices := h.getWorkloadServices()
   168  	h.consul.RemoveWorkload(workloadServices)
   169  
   170  	// Canary flag may be getting flipped when the alloc is being
   171  	// destroyed, so remove both variations of the service
   172  	workloadServices.Canary = !workloadServices.Canary
   173  	h.consul.RemoveWorkload(workloadServices)
   174  	h.initialRegistration = false
   175  }
   176  
   177  func (h *serviceHook) Stop(ctx context.Context, req *interfaces.TaskStopRequest, resp *interfaces.TaskStopResponse) error {
   178  	h.mu.Lock()
   179  	defer h.mu.Unlock()
   180  	h.deregister()
   181  	return nil
   182  }
   183  
   184  func (h *serviceHook) getWorkloadServices() *agentconsul.WorkloadServices {
   185  	// Interpolate with the task's environment
   186  	interpolatedServices := taskenv.InterpolateServices(h.taskEnv, h.services)
   187  
   188  	// Create task services struct with request's driver metadata
   189  	return &agentconsul.WorkloadServices{
   190  		AllocID:       h.allocID,
   191  		Task:          h.taskName,
   192  		Restarter:     h.restarter,
   193  		Services:      interpolatedServices,
   194  		DriverExec:    h.driverExec,
   195  		DriverNetwork: h.driverNet,
   196  		Networks:      h.networks,
   197  		Canary:        h.canary,
   198  	}
   199  }