From 74f225fcd280774c281570c00e62e038aa4e5d93 Mon Sep 17 00:00:00 2001 From: Kyle Havlovitz Date: Wed, 23 May 2018 14:44:24 -0700 Subject: [PATCH] Add client api support for CA config endpoints --- api/connect_ca.go | 72 ++++++++++++++++++++++++++++++++++++++++++ api/connect_ca_test.go | 41 ++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/api/connect_ca.go b/api/connect_ca.go index ed0ac5e8fa..c43339969f 100644 --- a/api/connect_ca.go +++ b/api/connect_ca.go @@ -1,9 +1,44 @@ package api import ( + "fmt" "time" + + "github.com/mitchellh/mapstructure" ) +// CAConfig is the structure for the Connect CA configuration. +type CAConfig struct { + // Provider is the CA provider implementation to use. + Provider string + + // Configuration is arbitrary configuration for the provider. This + // should only contain primitive values and containers (such as lists + // and maps). + Config map[string]interface{} + + CreateIndex uint64 + ModifyIndex uint64 +} + +// ConsulCAProviderConfig is the config for the built-in Consul CA provider. +type ConsulCAProviderConfig struct { + PrivateKey string + RootCert string + RotationPeriod time.Duration +} + +// ParseConsulCAConfig takes a raw config map and returns a parsed +// ConsulCAProviderConfig. +func ParseConsulCAConfig(raw map[string]interface{}) (*ConsulCAProviderConfig, error) { + var config ConsulCAProviderConfig + if err := mapstructure.WeakDecode(raw, &config); err != nil { + return nil, fmt.Errorf("error decoding config: %s", err) + } + + return &config, nil +} + // CARootList is the structure for the results of listing roots. type CARootList struct { ActiveRootID string @@ -79,3 +114,40 @@ func (h *Connect) CARoots(q *QueryOptions) (*CARootList, *QueryMeta, error) { } return &out, qm, nil } + +// CAGetConfig returns the current CA configuration. +func (h *Connect) CAGetConfig(q *QueryOptions) (*CAConfig, *QueryMeta, error) { + r := h.c.newRequest("GET", "/v1/connect/ca/configuration") + r.setQueryOptions(q) + rtt, resp, err := requireOK(h.c.doRequest(r)) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + qm := &QueryMeta{} + parseQueryMeta(resp, qm) + qm.RequestTime = rtt + + var out CAConfig + if err := decodeBody(resp, &out); err != nil { + return nil, nil, err + } + return &out, qm, nil +} + +// CASetConfig sets the current CA configuration. +func (h *Connect) CASetConfig(conf *CAConfig, q *WriteOptions) (*WriteMeta, error) { + r := h.c.newRequest("PUT", "/v1/connect/ca/configuration") + r.setWriteOptions(q) + r.obj = conf + rtt, resp, err := requireOK(h.c.doRequest(r)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + wm := &WriteMeta{} + wm.RequestTime = rtt + return wm, nil +} diff --git a/api/connect_ca_test.go b/api/connect_ca_test.go index 36fb12b56d..9cfaa9a0ac 100644 --- a/api/connect_ca_test.go +++ b/api/connect_ca_test.go @@ -2,6 +2,9 @@ package api import ( "testing" + "time" + + "github.com/pascaldekloe/goe/verify" "github.com/hashicorp/consul/testutil" "github.com/hashicorp/consul/testutil/retry" @@ -51,3 +54,41 @@ func TestAPI_ConnectCARoots_list(t *testing.T) { }) } + +func TestAPI_ConnectCAConfig_get_set(t *testing.T) { + t.Parallel() + + c, s := makeClient(t) + defer s.Stop() + + expected := &ConsulCAProviderConfig{ + RotationPeriod: 90 * 24 * time.Hour, + } + + // This fails occasionally if server doesn't have time to bootstrap CA so + // retry + retry.Run(t, func(r *retry.R) { + connect := c.Connect() + + conf, _, err := connect.CAGetConfig(nil) + r.Check(err) + if conf.Provider != "consul" { + r.Fatalf("expected default provider, got %q", conf.Provider) + } + parsed, err := ParseConsulCAConfig(conf.Config) + r.Check(err) + verify.Values(r, "", parsed, expected) + + // Change a config value and update + conf.Config["RotationPeriod"] = 120 * 24 * time.Hour + _, err = connect.CASetConfig(conf, nil) + r.Check(err) + + updated, _, err := connect.CAGetConfig(nil) + r.Check(err) + expected.RotationPeriod = 120 * 24 * time.Hour + parsed, err = ParseConsulCAConfig(updated.Config) + r.Check(err) + verify.Values(r, "", parsed, expected) + }) +}