From 461c1e18a5f696b654f0646618b81b73797d7d71 Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Thu, 23 Oct 2014 22:22:25 -0400 Subject: [PATCH] Add multiple service definition support This change-set adds another key to the configuration decoding called `services`, which is expected to be a list of service definitions. It follows the established convention of only allowing one of the keys: `service`, `check`, `services`. For every entry in the list it calls the corresponding decode method and appends it to the Servics of the resulting Config. While a similar result could be achieved with changing the Services member of the Config struct to have named mapstruct tag it lacks the proper time conversions provided by DecodeServiceDefinition. --- command/agent/config.go | 15 +++- command/agent/config_test.go | 80 +++++++++++++++++++ .../source/docs/agent/services.html.markdown | 38 +++++++++ 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/command/agent/config.go b/command/agent/config.go index 9fe653a333..791f5ede9d 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -392,9 +392,20 @@ func DecodeConfig(r io.Reader) (*Config, error) { // Check the result type if obj, ok := raw.(map[string]interface{}); ok { - // Check for a "service" or "check" key, meaning + // Check for a "services", "service" or "check" key, meaning // this is actually a definition entry - if sub, ok := obj["service"]; ok { + if sub, ok := obj["services"]; ok { + if list, ok := sub.([]interface{}); ok { + for _, srv := range list { + service, err := DecodeServiceDefinition(srv) + if err != nil { + return nil, err + } + result.Services = append(result.Services, service) + } + return &result, nil + } + } else if sub, ok := obj["service"]; ok { service, err := DecodeServiceDefinition(sub) result.Services = append(result.Services, service) return &result, err diff --git a/command/agent/config_test.go b/command/agent/config_test.go index f1c109b0f6..e4a8b779e4 100644 --- a/command/agent/config_test.go +++ b/command/agent/config_test.go @@ -516,6 +516,86 @@ func TestDecodeConfig(t *testing.T) { } } +func TestDecodeConfig_Services(t *testing.T) { + input := `{ + "services": [ + { + "id": "red0", + "name": "redis", + "tags": [ + "master" + ], + "port": 6000, + "check": { + "script": "/bin/check_redis -p 6000", + "interval": "5s", + "ttl": "20s" + } + }, + { + "id": "red1", + "name": "redis", + "tags": [ + "delayed", + "slave" + ], + "port": 7000, + "check": { + "script": "/bin/check_redis -p 7000", + "interval": "30s", + "ttl": "60s" + } + } + ] + }` + + config, err := DecodeConfig(bytes.NewReader([]byte(input))) + if err != nil { + t.Fatalf("err: %s", err) + } + + if len(config.Services) != 2 { + t.Fatalf("missing services") + } + + expected := &ServiceDefinition{ + Check: CheckType{ + Interval: 5 * time.Second, + Script: "/bin/check_redis -p 6000", + TTL: 20 * time.Second, + }, + ID: "red0", + Name: "redis", + Tags: []string{ + "master", + }, + Port: 6000, + } + + if !reflect.DeepEqual(config.Services[0], expected) { + t.Fatalf("services do not match:\n%+v\n%+v", config.Services[0], expected) + } + + expected = &ServiceDefinition{ + Check: CheckType{ + Interval: 30 * time.Second, + Script: "/bin/check_redis -p 7000", + TTL: 60 * time.Second, + }, + ID: "red1", + Name: "redis", + Tags: []string{ + "delayed", + "slave", + }, + Port: 7000, + } + + if !reflect.DeepEqual(config.Services[1], expected) { + t.Fatalf("services do not match:\n%+v\n%+v", config.Services[1], expected) + } +} + func TestDecodeConfig_Service(t *testing.T) { // Basics input := `{"service": {"id": "red1", "name": "redis", "tags": ["master"], "port":8000, "check": {"script": "/bin/check_redis", "interval": "10s", "ttl": "15s" }}}` diff --git a/website/source/docs/agent/services.html.markdown b/website/source/docs/agent/services.html.markdown index 3e99fb855e..6dc8e30eea 100644 --- a/website/source/docs/agent/services.html.markdown +++ b/website/source/docs/agent/services.html.markdown @@ -62,3 +62,41 @@ end in the ".json" extension to be loaded by Consul. Check definitions can also be updated by sending a `SIGHUP` to the agent. Alternatively, the service can be registered dynamically using the [HTTP API](/docs/agent/http.html). +## Multiple Service Definitions + +Multiple services definitions can be provided at once. Single and mutiple service definitions can't be provided together in one configuration file. + +```javascript +{ + "services": [ + { + "id": "red0", + "name": "redis", + "tags": [ + "master" + ], + "port": 6000, + "check": { + "script": "/bin/check_redis -p 6000", + "interval": "5s", + "ttl": "20s" + } + }, + { + "id": "red1", + "name": "redis", + "tags": [ + "delayed", + "slave" + ], + "port": 7000, + "check": { + "script": "/bin/check_redis -p 7000", + "interval": "30s", + "ttl": "60s" + } + }, + ... + ] +} +```