github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/serviceregistration/wrapper/wrapper.go (about)

     1  package wrapper
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/go-hclog"
     7  	"github.com/hashicorp/nomad/client/serviceregistration"
     8  	"github.com/hashicorp/nomad/nomad/structs"
     9  )
    10  
    11  // HandlerWrapper is used to wrap service registration implementations of the
    12  // Handler interface. We do not use a map or similar to store the handlers, so
    13  // we can avoid having to use a lock. This may need to be updated if we ever
    14  // support additional registration providers.
    15  type HandlerWrapper struct {
    16  	log hclog.Logger
    17  
    18  	// consulServiceProvider is the handler for services where Consul is the
    19  	// provider. This provider is always created and available.
    20  	consulServiceProvider serviceregistration.Handler
    21  
    22  	// nomadServiceProvider is the handler for services where Nomad is the
    23  	// provider.
    24  	nomadServiceProvider serviceregistration.Handler
    25  }
    26  
    27  // NewHandlerWrapper configures and returns a HandlerWrapper for use within
    28  // client hooks that need to interact with service and check registrations. It
    29  // mimics the serviceregistration.Handler interface, but returns the
    30  // implementation to allow future flexibility and is initially only intended
    31  // for use with the alloc and task runner service hooks.
    32  func NewHandlerWrapper(
    33  	log hclog.Logger, consulProvider, nomadProvider serviceregistration.Handler) *HandlerWrapper {
    34  	return &HandlerWrapper{
    35  		log:                   log,
    36  		nomadServiceProvider:  nomadProvider,
    37  		consulServiceProvider: consulProvider,
    38  	}
    39  }
    40  
    41  // RegisterWorkload wraps the serviceregistration.Handler RegisterWorkload
    42  // function. It determines which backend provider to call and passes the
    43  // workload unless the provider is unknown, in which case an error will be
    44  // returned.
    45  func (h *HandlerWrapper) RegisterWorkload(workload *serviceregistration.WorkloadServices) error {
    46  
    47  	// Don't rely on callers to check there are no services to register.
    48  	if len(workload.Services) == 0 {
    49  		return nil
    50  	}
    51  
    52  	provider := workload.RegistrationProvider()
    53  
    54  	switch provider {
    55  	case structs.ServiceProviderNomad:
    56  		return h.nomadServiceProvider.RegisterWorkload(workload)
    57  	case structs.ServiceProviderConsul:
    58  		return h.consulServiceProvider.RegisterWorkload(workload)
    59  	default:
    60  		return fmt.Errorf("unknown service registration provider: %q", provider)
    61  	}
    62  }
    63  
    64  // RemoveWorkload wraps the serviceregistration.Handler RemoveWorkload
    65  // function. It determines which backend provider to call and passes the
    66  // workload unless the provider is unknown.
    67  func (h *HandlerWrapper) RemoveWorkload(services *serviceregistration.WorkloadServices) {
    68  
    69  	var provider string
    70  
    71  	// It is possible the services field is empty depending on the exact
    72  	// situation which resulted in the call.
    73  	if len(services.Services) > 0 {
    74  		provider = services.RegistrationProvider()
    75  	}
    76  
    77  	// Call the correct provider, if we have managed to identify it. An empty
    78  	// string means you didn't find a provider, therefore default to consul.
    79  	//
    80  	// In certain situations this function is called with zero services,
    81  	// therefore meaning we make an assumption on the provider. When this
    82  	// happens, we need to ensure the allocation is removed from the Consul
    83  	// implementation. This tracking (allocRegistrations) is used by the
    84  	// allochealth tracker and so is critical to be removed. The test
    85  	// allocrunner.TestAllocRunner_Restore_RunningTerminal covers the case
    86  	// described here.
    87  	switch provider {
    88  	case structs.ServiceProviderNomad:
    89  		h.nomadServiceProvider.RemoveWorkload(services)
    90  	case structs.ServiceProviderConsul, "":
    91  		h.consulServiceProvider.RemoveWorkload(services)
    92  	default:
    93  		h.log.Error("unknown service registration provider", "provider", provider)
    94  	}
    95  }
    96  
    97  // UpdateWorkload identifies which provider to call for the new and old
    98  // workloads provided. In the event both use the same provider, the
    99  // UpdateWorkload function will be called, otherwise the register and remove
   100  // functions will be called.
   101  func (h *HandlerWrapper) UpdateWorkload(old, new *serviceregistration.WorkloadServices) error {
   102  
   103  	// Hot path to exit if there is nothing to do.
   104  	if len(old.Services) == 0 && len(new.Services) == 0 {
   105  		return nil
   106  	}
   107  
   108  	newProvider := new.RegistrationProvider()
   109  	oldProvider := old.RegistrationProvider()
   110  
   111  	// If the new and old services use the same provider, call the
   112  	// UpdateWorkload and leave it at that.
   113  	if newProvider == oldProvider {
   114  		switch newProvider {
   115  		case structs.ServiceProviderNomad:
   116  			return h.nomadServiceProvider.UpdateWorkload(old, new)
   117  		case structs.ServiceProviderConsul:
   118  			return h.consulServiceProvider.UpdateWorkload(old, new)
   119  		default:
   120  			return fmt.Errorf("unknown service registration provider for update: %q", newProvider)
   121  		}
   122  	}
   123  
   124  	// If we have new services, call the relevant provider. Registering can
   125  	// return an error. Do this before RemoveWorkload, so we can halt the
   126  	// process if needed, otherwise we may leave the task/group
   127  	// registration-less.
   128  	if len(new.Services) > 0 {
   129  		if err := h.RegisterWorkload(new); err != nil {
   130  			return err
   131  		}
   132  	}
   133  
   134  	if len(old.Services) > 0 {
   135  		h.RemoveWorkload(old)
   136  	}
   137  
   138  	return nil
   139  }