-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Service Discovery Integration #219
Changes from all commits
df6ddc0
06185d8
1327ea1
500f468
b6fda97
a542d44
7385abf
1b6f96c
13e709a
7db8deb
436ae98
7353697
56219f0
a47457b
9647476
2ef0462
0122144
29096fc
fd46a84
8b53f16
8336aa1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package discovery | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/consul/api" | ||
) | ||
|
||
// ConsulDiscovery is a back-end for service discovery which can be used | ||
// to populate a local Consul agent with service information. Because | ||
// Consul already has information about the local node, some shortcuts | ||
// can be taken in this back-end. Specifically, the IP address of the Nomad | ||
// agent does not need to be used, because Consul has this information | ||
// already and may even be configured to expose services on an alternate | ||
// advertise address. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I may be misreading this but I'm not sure this is a safe assumption to make. Since nomad may be managing tasks across multiple IPs and those services will be bound to a specific interface, the IP is actually important. Even if nomad or consul is listening on a particular IP, tasks are not necessarily registered on the same IP. |
||
type ConsulDiscovery struct { | ||
client *api.Client | ||
Context | ||
} | ||
|
||
// NewConsulDiscovery creates a new Consul discovery provider using the | ||
// configuration provided in the client options. | ||
func NewConsulDiscovery(ctx Context) (Provider, error) { | ||
// Build the config | ||
conf := api.DefaultConfig() | ||
conf.Datacenter = ctx.node.Datacenter | ||
conf.Address = ctx.config.Read("discovery.consul.address") | ||
conf.Scheme = ctx.config.Read("discovery.consul.scheme") | ||
conf.Token = ctx.config.Read("discovery.consul.token") | ||
|
||
// Create the client | ||
client, err := api.NewClient(conf) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Create and return the discovery provider | ||
return &ConsulDiscovery{client, ctx}, nil | ||
} | ||
|
||
// Name returns the name of the discovery provider. | ||
func (c *ConsulDiscovery) Name() string { | ||
return "consul" | ||
} | ||
|
||
// Enabled determines if the Consul layer is enabled. This just looks at a | ||
// client option and doesn't do any connection testing, as Consul may or may | ||
// not be available at the time of Nomad's start. | ||
func (c *ConsulDiscovery) Enabled() bool { | ||
return c.config.Read("discovery.consul.enable") == "true" | ||
} | ||
|
||
// Register registers a service name into a Consul agent. The agent will then | ||
// sync this definition into the service catalog. | ||
func (c *ConsulDiscovery) Register(allocID, name string, port int) error { | ||
// Build the service definition | ||
serviceID := fmt.Sprintf("%s:%s", name, allocID) | ||
svc := &api.AgentServiceRegistration{ | ||
ID: serviceID, | ||
Name: name, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets inject the |
||
Port: port, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think as mentioned, we wanted a richer |
||
} | ||
|
||
// Attempt to register | ||
return c.client.Agent().ServiceRegister(svc) | ||
} | ||
|
||
// Deregister removes a service from the Consul agent. Anti-entropy will | ||
// then handle deregistering the service from the catalog. | ||
func (c *ConsulDiscovery) Deregister(allocID, name string) error { | ||
// Send the deregister request | ||
serviceID := fmt.Sprintf("%s:%s", name, allocID) | ||
return c.client.Agent().ServiceDeregister(serviceID) | ||
} | ||
|
||
// DiscoverName returns the service name in Consul, given the parts of the | ||
// name. This is a simple hyphen-joined string so that we can easily support | ||
// DNS lookups from Consul. | ||
func (c *ConsulDiscovery) DiscoverName(parts []string) string { | ||
return strings.Join(parts, "-") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package discovery | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"log" | ||
"os" | ||
"testing" | ||
|
||
consulapi "github.com/hashicorp/consul/api" | ||
consultest "github.com/hashicorp/consul/testutil" | ||
"github.com/hashicorp/nomad/client/config" | ||
"github.com/hashicorp/nomad/nomad/structs" | ||
) | ||
|
||
func TestConsulDiscovery_Register(t *testing.T) { | ||
srv := consultest.NewTestServerConfig(t, func(c *consultest.TestServerConfig) { | ||
c.Bootstrap = false | ||
}) | ||
defer srv.Stop() | ||
|
||
// Make the consul client | ||
consulConfig := consulapi.DefaultConfig() | ||
consulConfig.Address = srv.HTTPAddr | ||
consulClient, err := consulapi.NewClient(consulConfig) | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
|
||
// Build the context | ||
conf := &config.Config{} | ||
logBuf := new(bytes.Buffer) | ||
logger := log.New(io.MultiWriter(logBuf, os.Stdout), "", log.LstdFlags) | ||
node := &structs.Node{} | ||
ctx := Context{ | ||
config: conf, | ||
logger: logger, | ||
node: node, | ||
} | ||
|
||
// Create the discovery layer | ||
disc, err := NewConsulDiscovery(ctx) | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
|
||
// Returns false if not enabled | ||
if disc.Enabled() { | ||
t.Fatalf("should not be enabled") | ||
} | ||
|
||
// Enable the discovery layer | ||
conf.Options = map[string]string{ | ||
"discovery.consul.enable": "true", | ||
"discovery.consul.address": srv.HTTPAddr, | ||
} | ||
disc, err = NewConsulDiscovery(ctx) | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
if !disc.Enabled() { | ||
t.Fatalf("should be enabled") | ||
} | ||
|
||
// Should register a service | ||
if err := disc.Register("alloc1", "foobar", 123); err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
|
||
// Check that the service exists | ||
services, err := consulClient.Agent().Services() | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
for _, svc := range services { | ||
if svc.Service == "foobar" { | ||
if svc.ID != "foobar:alloc1" { | ||
t.Fatalf("bad id: %s", svc.ID) | ||
} | ||
if svc.Port != 123 { | ||
t.Fatalf("bad port: %d", svc.Port) | ||
} | ||
goto REGISTERED | ||
} | ||
} | ||
t.Fatalf("missing service") | ||
|
||
REGISTERED: | ||
// Deregister the service | ||
if err := disc.Deregister("alloc1", "foobar"); err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
|
||
// Check that the service is gone | ||
services, err = consulClient.Agent().Services() | ||
if err != nil { | ||
t.Fatalf("err: %s", err) | ||
} | ||
for _, svc := range services { | ||
if svc.Service == "foobar" { | ||
t.Fatalf("foobar service should be deregistered") | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can probably collapse these as avail is never used other than in the log.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I was just trying to stay consistent with what drivers and fingerprints do. I can
strings.Join()
them or something, if you think that's better, but we should do the others too in that case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just suggesting:
c.logger.Printf("[DEBUG] client: available discovery layers: %v", disc.EnabledProviders())