Skip to content

Commit

Permalink
Use consul go library to register service
Browse files Browse the repository at this point in the history
  • Loading branch information
jskswamy committed Oct 11, 2018
1 parent 923f844 commit 4bb5da3
Show file tree
Hide file tree
Showing 25 changed files with 1,314 additions and 200 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 13 additions & 23 deletions cmd/stolonctl/cmd/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,21 @@ var Register = &cobra.Command{
Version: cmd.Version,
}

type registerConfig struct {
registerBackend string
registerEndpoints string
}

var rCfg registerConfig
var rCfg register.Config
var log = slog.S()

func init() {
Register.PersistentFlags().StringVar(&rCfg.registerBackend, "register-backend", "consul", "register backend type (consul)")
Register.PersistentFlags().StringVar(&rCfg.registerEndpoints, "register-endpoints", "http://127.0.0.1:8500", "a common-delimited list of store endpoints (use https scheme for tls communication) defaults: http://127.0.0.1:8500 for consul")
Register.PersistentFlags().StringVar(&rCfg.Backend, "register-backend", "consul", "register backend type (consul)")
Register.PersistentFlags().StringVar(&rCfg.Endpoints, "register-endpoints", "http://127.0.0.1:8500", "a common-delimited list of register endpoints (use https scheme for tls communication) defaults: http://127.0.0.1:8500 for consul")
Register.PersistentFlags().BoolVar(&cfg.Debug, "debug", false, "enable debug logging")
CmdStolonCtl.AddCommand(Register)
}

func checkConfig(cfg *config, rCfg *registerConfig) error {
func checkConfig(cfg *config, rCfg *register.Config) error {
if err := cmd.CheckCommonConfig(&cfg.CommonConfig); err != nil {
return err
}
switch rCfg.registerBackend {
case "consul":
default:
return fmt.Errorf("unknown register backend: %q", rCfg.registerBackend)
}
return nil
return rCfg.Validate()
}

func runRegister(c *cobra.Command, _ []string) {
Expand Down Expand Up @@ -87,7 +77,7 @@ func runRegister(c *cobra.Command, _ []string) {
}
}

func registerCluster(cfg *config, rCfg *registerConfig) error {
func registerCluster(cfg *config, rCfg *register.Config) error {
store, err := cmd.NewStore(&cfg.CommonConfig)
if err != nil {
return err
Expand All @@ -98,29 +88,29 @@ func registerCluster(cfg *config, rCfg *registerConfig) error {
return fmt.Errorf("cannot get cluster data: %v", err)
}

service, err := register.NewServiceDiscovery(rCfg.registerBackend, rCfg.registerEndpoints)
service, err := register.NewServiceDiscovery(rCfg)
if err != nil {
return err
}

if master, err := cluster.Master(); err == nil {
log.Debugf("found master %s with uid %s", master.Name, master.Id)
log.Debugf("found master %s with uid %s", master.Name, master.ID)
if err = service.Register(master); err != nil {
log.Errorf("unable to register master %s with uid %s, reason: %s", master.Name, master.Id, err.Error())
log.Errorf("unable to register master %s with uid %s, reason: %s", master.Name, master.ID, err.Error())
} else {
log.Infof("successfully registered master %s with uid %s", master.Name, master.Id)
log.Infof("successfully registered master %s with uid %s", master.Name, master.ID)
}
} else {
log.Warnf("no master found %s", err.Error())
}

if slaves, err := cluster.Slaves(); err == nil {
for _, slave := range slaves {
log.Debugf("found slave %s with uid %s", slave.Name, slave.Id)
log.Debugf("found slave %s with uid %s", slave.Name, slave.ID)
if err = service.Register(&slave); err != nil {
log.Errorf("unable to register slave %s with uid %s, reason: %s", slave.Name, slave.Id, err.Error())
log.Errorf("unable to register slave %s with uid %s, reason: %s", slave.Name, slave.ID, err.Error())
} else {
log.Infof("successfully registered slave %s with uid %s", slave.Name, slave.Id)
log.Infof("successfully registered slave %s with uid %s", slave.Name, slave.ID)
}
}
} else {
Expand Down
55 changes: 55 additions & 0 deletions cmd/stolonctl/cmd/register/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2015 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and

package register

import (
"fmt"
"net/url"
"strings"

"github.com/hashicorp/consul/api"
)

// Config represents necessary configurations which can passed
// for registering master and slave info for service discovery
type Config struct {
Backend string
Endpoints string
}

// Validate returns nil if the config is valid, else returns error with
// appropriate reason
func (config *Config) Validate() error {
switch config.Backend {
case "consul":
addresses := strings.Split(config.Endpoints, ",")
if len(addresses) != 1 {
return fmt.Errorf("consul does not support multiple endpoints: %s", config.Endpoints)
}
_, err := url.Parse(config.Endpoints)
return err
default:
return fmt.Errorf("unknown register backend: %q", config.Backend)
}
}

// ConsulConfig returns consul.api.ConsulConfig if register endpoint is valid consul url
// else will return error with appropriate reason
func (config *Config) ConsulConfig() (*api.Config, error) {
url, err := url.Parse(config.Endpoints)
if err != nil {
return nil, err
}
return &api.Config{Address: url.Host, Scheme: url.Scheme}, nil
}
66 changes: 66 additions & 0 deletions cmd/stolonctl/cmd/register/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2015 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and

package register

import "testing"

func TestRegisterConfig(t *testing.T) {
t.Run("validate", func(t *testing.T) {
t.Run("should check for consul register backend", func(t *testing.T) {
config := Config{Backend: "something other than consul"}
err := config.Validate()

if err == nil || err.Error() != "unknown register backend: \"something other than consul\"" {
t.Errorf("expected unknown register backend but got %s", err.Error())
}
})

t.Run("should not return any error if all valid configurations are specified", func(t *testing.T) {
config := Config{Backend: "consul"}
err := config.Validate()

if err != nil {
t.Errorf("expected no error but got '%v'", err.Error())
}
})

t.Run("should not support multiple addresses", func(t *testing.T) {
config := Config{Backend: "consul", Endpoints: "http://127.0.0.1:8500,http://127.0.0.2:8500"}
err := config.Validate()

if err == nil || err.Error() != "consul does not support multiple endpoints: http://127.0.0.1:8500,http://127.0.0.2:8500" {
t.Errorf("expected unknown register backend but got %s", err.Error())
}
})
})

t.Run("config", func(t *testing.T) {
t.Run("should return config", func(t *testing.T) {
c := Config{Backend: "consul", Endpoints: "http://127.0.0.1:8500"}
config, err := c.ConsulConfig()

if err != nil {
t.Errorf("expected error to be nil but got %s", err.Error())
}

if config.Address != "127.0.0.1:8500" {
t.Errorf("expected address to be %s but got %s", c.Endpoints, config.Address)
}

if config.Scheme != "http" {
t.Errorf("expected address to be http but got %s", config.Scheme)
}
})
})
}
69 changes: 20 additions & 49 deletions cmd/stolonctl/cmd/register/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,9 @@
package register

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"path"

"github.com/hashicorp/consul/api"
)

// ServiceDiscovery helps to register service
Expand All @@ -30,63 +26,38 @@ type ServiceDiscovery interface {
}

// NewServiceDiscovery creates a Discovery from registerBackend and registerEndpoints
func NewServiceDiscovery(registerBackend string, registerEndpoints string) (ServiceDiscovery, error) {
switch registerBackend {
func NewServiceDiscovery(config *Config) (ServiceDiscovery, error) {
switch config.Backend {
case "consul":
return NewConsulServiceDiscovery(&http.Client{}, registerEndpoints), nil
if apiConfig, err := config.ConsulConfig(); err != nil {
return nil, err
} else if client, err := api.NewClient(apiConfig); err != nil {
return nil, err
} else {
agent := client.Agent()
return NewConsulServiceDiscovery(agent), nil
}
default:
return nil, errors.New("register backend not supported")
}
}

// A HTTPClient has necessary method to make http calls
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}

// ConsulServiceDiscovery helps to register service to consul
type ConsulServiceDiscovery struct {
client HTTPClient
registerEndpoints string
agent ConsulAgent
}

// NewConsulServiceDiscovery creates a new ConsulDiscovery
func NewConsulServiceDiscovery(client HTTPClient, registerEndpoints string) ServiceDiscovery {
return &ConsulServiceDiscovery{client: client, registerEndpoints: registerEndpoints}
// ConsulAgent interface holds all the necessary methods to interact with consul agent
type ConsulAgent interface {
ServiceRegister(service *api.AgentServiceRegistration) error
}

func (cd *ConsulServiceDiscovery) baseURL() (*url.URL, error) {
return url.Parse(cd.registerEndpoints)
}

func (cd *ConsulServiceDiscovery) registerURL() (*string, error) {
baseURL, err := cd.baseURL()
if err != nil {
return nil, err
}

baseURL.Path = path.Join(baseURL.Path, "/v1/agent/service/register")
result := baseURL.String()
return &result, nil
// NewConsulServiceDiscovery creates a new ConsulDiscovery
func NewConsulServiceDiscovery(agent ConsulAgent) ServiceDiscovery {
return &ConsulServiceDiscovery{agent: agent}
}

// Register registers the given service info to consul
func (cd *ConsulServiceDiscovery) Register(info *ServiceInfo) error {
registerURL, err := cd.registerURL()
if err != nil {
return err
}
body, err := json.Marshal(info)
request, err := http.NewRequest(http.MethodPut, *registerURL, bytes.NewBuffer(body))
request.Header.Set("Content-Type", "application/json")
if err != nil {
return err
}

if response, err := cd.client.Do(request); err != nil {
return err
} else if response.StatusCode != 200 {
return fmt.Errorf("expected 200 to be returned but instead got %v from %s", response.StatusCode, *registerURL)
}
return nil
return cd.agent.ServiceRegister(info.ConsulAgentServiceRegistration())
}
Loading

0 comments on commit 4bb5da3

Please sign in to comment.