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 }