From 16cb776f9c91f2c26f50bcd91043b3109ef1b2cd Mon Sep 17 00:00:00 2001 From: James Rasell Date: Thu, 3 Mar 2022 12:14:00 +0100 Subject: [PATCH] api: add service registration HTTP API wrapper. --- api/allocations.go | 8 ++ api/allocations_test.go | 4 + api/jobs.go | 8 ++ api/jobs_test.go | 4 + api/service_registrations.go | 129 ++++++++++++++++++++++++++++++ api/service_registrations_test.go | 17 ++++ 6 files changed, 170 insertions(+) create mode 100644 api/service_registrations.go create mode 100644 api/service_registrations_test.go diff --git a/api/allocations.go b/api/allocations.go index 128541ca3..67fc6ca29 100644 --- a/api/allocations.go +++ b/api/allocations.go @@ -147,6 +147,14 @@ func (a *Allocations) Signal(alloc *Allocation, q *QueryOptions, task, signal st return err } +// Services is used to return a list of service registrations associated to the +// specified allocID. +func (a *Allocations) Services(allocID string, q *QueryOptions) ([]*ServiceRegistration, *QueryMeta, error) { + var resp []*ServiceRegistration + qm, err := a.client.query("/v1/allocation/"+allocID+"/services", &resp, q) + return resp, qm, err +} + // Allocation is used for serialization of allocations. type Allocation struct { ID string diff --git a/api/allocations_test.go b/api/allocations_test.go index cc306ec88..31fcb7d74 100644 --- a/api/allocations_test.go +++ b/api/allocations_test.go @@ -396,3 +396,7 @@ func TestAllocations_ShouldMigrate(t *testing.T) { require.False(t, DesiredTransition{}.ShouldMigrate()) require.False(t, DesiredTransition{Migrate: boolToPtr(false)}.ShouldMigrate()) } + +func TestAllocations_Services(t *testing.T) { + // TODO(jrasell) add tests once registration process is in place. +} diff --git a/api/jobs.go b/api/jobs.go index 61d0c2892..aa934bfb8 100644 --- a/api/jobs.go +++ b/api/jobs.go @@ -459,6 +459,14 @@ func (j *Jobs) Stable(jobID string, version uint64, stable bool, return &resp, wm, nil } +// Services is used to return a list of service registrations associated to the +// specified jobID. +func (j *Jobs) Services(jobID string, q *QueryOptions) ([]*ServiceRegistration, *QueryMeta, error) { + var resp []*ServiceRegistration + qm, err := j.client.query("/v1/job/"+jobID+"/services", &resp, q) + return resp, qm, err +} + // periodicForceResponse is used to deserialize a force response type periodicForceResponse struct { EvalID string diff --git a/api/jobs_test.go b/api/jobs_test.go index f21f7cab2..ed5909c28 100644 --- a/api/jobs_test.go +++ b/api/jobs_test.go @@ -2356,3 +2356,7 @@ func TestJobs_ScaleStatus(t *testing.T) { // Check that the result is what we expect require.Equal(groupCount, result.TaskGroups[groupName].Desired) } + +func TestJobs_Services(t *testing.T) { + // TODO(jrasell) add tests once registration process is in place. +} diff --git a/api/service_registrations.go b/api/service_registrations.go new file mode 100644 index 000000000..ebf1418b4 --- /dev/null +++ b/api/service_registrations.go @@ -0,0 +1,129 @@ +package api + +import ( + "fmt" + "net/url" +) + +// ServiceRegistrations is used to query the service endpoints. +type ServiceRegistrations struct { + client *Client +} + +// ServiceRegistration is an instance of a single allocation advertising itself +// as a named service with a specific address. Each registration is constructed +// from the job specification Service block. Whether the service is registered +// within Nomad, and therefore generates a ServiceRegistration is controlled by +// the Service.Provider parameter. +type ServiceRegistration struct { + + // ID is the unique identifier for this registration. It currently follows + // the Consul service registration format to provide consistency between + // the two solutions. + ID string + + // ServiceName is the human friendly identifier for this service + // registration. + ServiceName string + + // Namespace represents the namespace within which this service is + // registered. + Namespace string + + // NodeID is Node.ID on which this service registration is currently + // running. + NodeID string + + // Datacenter is the DC identifier of the node as identified by + // Node.Datacenter. + Datacenter string + + // JobID is Job.ID and represents the job which contained the service block + // which resulted in this service registration. + JobID string + + // AllocID is Allocation.ID and represents the allocation within which this + // service is running. + AllocID string + + // Tags are determined from either Service.Tags or Service.CanaryTags and + // help identify this service. Tags can also be used to perform lookups of + // services depending on their state and role. + Tags []string + + // Address is the IP address of this service registration. This information + // comes from the client and is not guaranteed to be routable; this depends + // on cluster network topology. + Address string + + // Port is the port number on which this service registration is bound. It + // is determined by a combination of factors on the client. + Port int + + CreateIndex uint64 + ModifyIndex uint64 +} + +// ServiceRegistrationListStub represents all service registrations held within a +// single namespace. +type ServiceRegistrationListStub struct { + + // Namespace details the namespace in which these services have been + // registered. + Namespace string + + // Services is a list of services found within the namespace. + Services []*ServiceRegistrationStub +} + +// ServiceRegistrationStub is the stub object describing an individual +// namespaced service. The object is built in a manner which would allow us to +// add additional fields in the future, if we wanted. +type ServiceRegistrationStub struct { + + // ServiceName is the human friendly name for this service as specified + // within Service.Name. + ServiceName string + + // Tags is a list of unique tags found for this service. The list is + // de-duplicated automatically by Nomad. + Tags []string +} + +// ServiceRegistrations returns a new handle on the services endpoints. +func (c *Client) ServiceRegistrations() *ServiceRegistrations { + return &ServiceRegistrations{client: c} +} + +// List can be used to list all service registrations currently stored within +// the target namespace. It returns a stub response object. +func (s *ServiceRegistrations) List(q *QueryOptions) ([]*ServiceRegistrationListStub, *QueryMeta, error) { + var resp []*ServiceRegistrationListStub + qm, err := s.client.query("/v1/services", &resp, q) + if err != nil { + return nil, qm, err + } + return resp, qm, nil +} + +// Get is used to return a list of service registrations whose name matches the +// specified parameter. +func (s *ServiceRegistrations) Get(serviceName string, q *QueryOptions) ([]*ServiceRegistration, *QueryMeta, error) { + var resp []*ServiceRegistration + qm, err := s.client.query("/v1/service/"+url.PathEscape(serviceName), &resp, q) + if err != nil { + return nil, qm, err + } + return resp, qm, nil +} + +// Delete can be used to delete an individual service registration as defined +// by its service name and service ID. +func (s *ServiceRegistrations) Delete(serviceName, serviceID string, q *WriteOptions) (*WriteMeta, error) { + path := fmt.Sprintf("/v1/service/%s/%s", url.PathEscape(serviceName), url.PathEscape(serviceID)) + wm, err := s.client.delete(path, nil, q) + if err != nil { + return nil, err + } + return wm, nil +} diff --git a/api/service_registrations_test.go b/api/service_registrations_test.go new file mode 100644 index 000000000..b957e194b --- /dev/null +++ b/api/service_registrations_test.go @@ -0,0 +1,17 @@ +package api + +import ( + "testing" +) + +func TestServiceRegistrations_List(t *testing.T) { + // TODO(jrasell) add tests once registration process is in place. +} + +func TestServiceRegistrations_Get(t *testing.T) { + // TODO(jrasell) add tests once registration process is in place. +} + +func TestServiceRegistrations_Delete(t *testing.T) { + // TODO(jrasell) add tests once registration process is in place. +}