From 324d45b8713b85e972f01e8a449cde021b009dfc Mon Sep 17 00:00:00 2001 From: Tyler Date: Sun, 11 Oct 2020 06:35:18 -0400 Subject: [PATCH] initial commit --- discovery.go | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 8 ++++ host.go | 14 ++++++ 3 files changed, 149 insertions(+) create mode 100644 discovery.go create mode 100644 go.mod create mode 100644 host.go diff --git a/discovery.go b/discovery.go new file mode 100644 index 0000000..71ece2d --- /dev/null +++ b/discovery.go @@ -0,0 +1,127 @@ +package discovery + +import ( + "context" + "encoding/json" + "github.com/coreos/etcd/clientv3" + "github.com/google/uuid" + "path" +) + +type Registry struct { + client *clientv3.Client + path string +} + +func New(endpoints []string) (*Registry, error) { + client, err := clientv3.New(clientv3.Config{ + Endpoints: endpoints, + }) + + if err != nil { + return nil, err + } + + return &Registry{ + client: client, + }, nil +} + +func (r *Registry) Register(ctx context.Context, service string, host Host) error { + resp, err := r.client.Grant(context.Background(), 30) + + if err != nil { + return err + } + + if host.UUID == "" { + host.UUID = uuid.New().String() + } + + b, err := json.Marshal(host) + + if err != nil { + return err + } + + key := path.Join(r.path, service, host.UUID) + + _, err = r.client.Put(context.Background(), key, string(b), clientv3.WithLease(resp.ID)) + + if err != nil { + return err + } + + ch, err := r.client.KeepAlive(ctx, resp.ID) + + if err != nil { + return err + } + + go func() { + for { + select { + case ka := <-ch: + if ka == nil { + return + } + + // Removed + } + } + }() + + return nil +} + +func (r *Registry) Get(service string) ([]Host, error) { + res, err := r.client.Get(context.Background(), path.Join(r.path, service), clientv3.WithPrefix()) + + if err != nil { + return nil, err + } + + var host Host + + hosts := make([]Host, len(res.Kvs)) + + for i, kv := range res.Kvs { + host = Host{} + + if err = json.Unmarshal(kv.Value, &host); err != nil { + continue + } + + hosts[i] = host + } + + return hosts, nil +} + +func (r *Registry) Watch(service string) (chan Host, chan string) { + ch := r.client.Watch(context.Background(), path.Join(r.path, service), clientv3.WithPrefix()) + + upChan := make(chan Host) + downChan := make(chan string) + + go func() { + for resp := range ch { + for _, e := range resp.Events { + if e.Type == clientv3.EventTypeDelete { + // Removed + downChan <- string(e.Kv.Key) + } else if e.IsCreate() || e.IsModify() { + host := Host{} + + if err := json.Unmarshal(e.Kv.Value, &host); err != nil { + continue + } + + upChan <- host + } + } + } + }() + + return upChan, downChan +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1a8647c --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module meow.tf/go/service-discovery + +go 1.15 + +require ( + github.com/coreos/etcd v3.3.13+incompatible + github.com/google/uuid v1.1.1 +) \ No newline at end of file diff --git a/host.go b/host.go new file mode 100644 index 0000000..70a01a3 --- /dev/null +++ b/host.go @@ -0,0 +1,14 @@ +package discovery + +type Ports map[string]string + +type Host struct { + // UUID is the host registration's UUID + UUID string `json:"uuid"` + // Hostname is the primary hostname or ip of the host. + Hostname string `json:"hostname"` + // PrivateHostname is the private/local network hostname or ip of the host. + PrivateHostname string `json:"private_hostname,omitempty"` + // Ports defines which ports the services on the host use. + Ports Ports `json:"ports"` +}