diff --git a/nomad/structs/service_registration.go b/nomad/structs/service_registration.go new file mode 100644 index 000000000..9ebbe873b --- /dev/null +++ b/nomad/structs/service_registration.go @@ -0,0 +1,107 @@ +package structs + +import "github.com/hashicorp/nomad/helper" + +// ServiceRegistration is the internal representation of a Nomad service +// registration. +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. This is not unique. + ServiceName string + + // Namespace is Job.Namespace and therefore the namespace in which this + // service registration resides. + 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. It is denormalized here to allow filtering services by datacenter without looking up every node. + 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 +} + +// Copy creates a deep copy of the service registration. This copy can then be +// safely modified. It handles nil objects. +func (s *ServiceRegistration) Copy() *ServiceRegistration { + if s == nil { + return nil + } + + ns := new(ServiceRegistration) + *ns = *s + ns.Tags = helper.CopySliceString(ns.Tags) + + return ns +} + +// Equals performs an equality check on the two service registrations. It +// handles nil objects. +func (s *ServiceRegistration) Equals(o *ServiceRegistration) bool { + if s == nil || o == nil { + return s == o + } + if s.ID != o.ID { + return false + } + if s.ServiceName != o.ServiceName { + return false + } + if s.NodeID != o.NodeID { + return false + } + if s.Datacenter != o.Datacenter { + return false + } + if s.JobID != o.JobID { + return false + } + if s.AllocID != o.AllocID { + return false + } + if s.Namespace != o.Namespace { + return false + } + if s.Address != o.Address { + return false + } + if s.Port != o.Port { + return false + } + if !helper.CompareSliceSetString(s.Tags, o.Tags) { + return false + } + return true +} diff --git a/nomad/structs/service_registration_test.go b/nomad/structs/service_registration_test.go new file mode 100644 index 000000000..2d362b821 --- /dev/null +++ b/nomad/structs/service_registration_test.go @@ -0,0 +1,383 @@ +package structs + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestServiceRegistration_Copy(t *testing.T) { + sr := &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + } + newSR := sr.Copy() + require.True(t, sr.Equals(newSR)) +} + +func TestServiceRegistration_Equal(t *testing.T) { + testCases := []struct { + serviceReg1 *ServiceRegistration + serviceReg2 *ServiceRegistration + expectedOutput bool + name string + }{ + { + serviceReg1: nil, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "nil service registration composed", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: nil, + expectedOutput: false, + name: "nil service registration func input", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-group-2873cf75-42e5-7c45-ca1c-415f3e18be3dcache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "ID not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "platform-example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "service name not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "ba991c17-7ce5-9c20-78b7-311e63578583", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "node ID not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc2", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "datacenter not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "platform-example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "job ID not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "ba991c17-7ce5-9c20-78b7-311e63578583", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "alloc ID not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "platform", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "namespace not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "10.10.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "address not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 33813, + }, + expectedOutput: false, + name: "port not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"canary"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: false, + name: "tags not equal", + }, + { + serviceReg1: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + serviceReg2: &ServiceRegistration{ + ID: "_nomad-task-2873cf75-42e5-7c45-ca1c-415f3e18be3d-group-cache-example-cache-db", + ServiceName: "example-cache", + Namespace: "default", + NodeID: "17a6d1c0-811e-2ca9-ded0-3d5d6a54904c", + Datacenter: "dc1", + JobID: "example", + AllocID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d", + Tags: []string{"foo"}, + Address: "192.168.13.13", + Port: 23813, + }, + expectedOutput: true, + name: "both equal", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actualOutput := tc.serviceReg1.Equals(tc.serviceReg2) + require.Equal(t, tc.expectedOutput, actualOutput) + }) + } +}