From fcfd2442691f7bd6f3b29303a0c9005028b8b65e Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Thu, 29 Apr 2021 17:44:32 -0400 Subject: [PATCH] config-entry: use custom MarshalJSON for mesh type So that the Kind field is added to the JSON object. --- agent/config_endpoint_test.go | 48 ++++++++++++++++++++++- agent/structs/config_entry_mesh.go | 17 ++++++++ api/config_entry_cluster.go | 17 +++++++- command/config/write/config_write_test.go | 31 +++++++++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) diff --git a/agent/config_endpoint_test.go b/agent/config_endpoint_test.go index ec18747e0..13ff251d8 100644 --- a/agent/config_endpoint_test.go +++ b/agent/config_endpoint_test.go @@ -7,10 +7,11 @@ import ( "net/http/httptest" "testing" - "github.com/hashicorp/consul/agent/structs" - "github.com/hashicorp/consul/testrpc" "github.com/pkg/errors" "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/testrpc" ) func TestConfig_Get(t *testing.T) { @@ -48,6 +49,18 @@ func TestConfig_Get(t *testing.T) { }, }, }, + { + Datacenter: "dc1", + Entry: &structs.MeshConfigEntry{ + TransparentProxy: structs.TransparentProxyMeshConfig{ + CatalogDestinationsOnly: true, + }, + Meta: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + }, + }, } for _, req := range reqs { out := false @@ -95,6 +108,37 @@ func TestConfig_Get(t *testing.T) { _, err := a.srv.Config(resp, req) require.Error(t, errors.New("Must provide either a kind or both kind and name"), err) }) + t.Run("get the single mesh config", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/v1/config/mesh/mesh", nil) + resp := httptest.NewRecorder() + obj, err := a.srv.Config(resp, req) + require.NoError(t, err) + + ce, ok := obj.(*structs.MeshConfigEntry) + require.True(t, ok, "wrong type %T", obj) + // Set indexes to expected values for assertions + ce.CreateIndex = 12 + ce.ModifyIndex = 13 + + out, err := a.srv.marshalJSON(req, obj) + require.NoError(t, err) + + expected := ` +{ + "Kind": "mesh", + "TransparentProxy": { + "CatalogDestinationsOnly": true + }, + "Meta":{ + "key1": "value1", + "key2": "value2" + }, + "CreateIndex": 12, + "ModifyIndex": 13 +} +` + require.JSONEq(t, expected, string(out)) + }) } func TestConfig_Delete(t *testing.T) { diff --git a/agent/structs/config_entry_mesh.go b/agent/structs/config_entry_mesh.go index 74d70765a..33792b381 100644 --- a/agent/structs/config_entry_mesh.go +++ b/agent/structs/config_entry_mesh.go @@ -1,6 +1,7 @@ package structs import ( + "encoding/json" "fmt" "github.com/hashicorp/consul/acl" @@ -88,3 +89,19 @@ func (e *MeshConfigEntry) GetEnterpriseMeta() *EnterpriseMeta { return &e.EnterpriseMeta } + +// MarshalJSON adds the Kind field so that the JSON can be decoded back into the +// correct type. +// This method is implemented on the structs type (as apposed to the api type) +// because that is what the API currently uses to return a response. +func (e *MeshConfigEntry) MarshalJSON() ([]byte, error) { + type Alias MeshConfigEntry + source := &struct { + Kind string + *Alias + }{ + Kind: MeshConfig, + Alias: (*Alias)(e), + } + return json.Marshal(source) +} diff --git a/api/config_entry_cluster.go b/api/config_entry_cluster.go index 915fab5d3..48fcc44fc 100644 --- a/api/config_entry_cluster.go +++ b/api/config_entry_cluster.go @@ -1,7 +1,8 @@ package api +import "encoding/json" + type MeshConfigEntry struct { - Name string Namespace string `json:",omitempty"` TransparentProxy TransparentProxyMeshConfig `alias:"transparent_proxy"` Meta map[string]string `json:",omitempty"` @@ -36,3 +37,17 @@ func (e *MeshConfigEntry) GetCreateIndex() uint64 { func (e *MeshConfigEntry) GetModifyIndex() uint64 { return e.ModifyIndex } + +// MarshalJSON adds the Kind field so that the JSON can be decoded back into the +// correct type. +func (e *MeshConfigEntry) MarshalJSON() ([]byte, error) { + type Alias MeshConfigEntry + source := &struct { + Kind string + *Alias + }{ + Kind: MeshConfig, + Alias: (*Alias)(e), + } + return json.Marshal(source) +} diff --git a/command/config/write/config_write_test.go b/command/config/write/config_write_test.go index 8b3fe120b..20f20af43 100644 --- a/command/config/write/config_write_test.go +++ b/command/config/write/config_write_test.go @@ -1,6 +1,7 @@ package write import ( + "bytes" "io" "strings" "testing" @@ -113,6 +114,36 @@ func TestConfigWrite(t *testing.T) { require.NotEqual(t, 0, code) require.NotEmpty(t, ui.ErrorWriter.String()) }) + + t.Run("mesh config entry", func(t *testing.T) { + stdin := new(bytes.Buffer) + stdin.WriteString(` +kind = "mesh" +meta { + "foo" = "bar" + "gir" = "zim" +} +transparent_proxy { + catalog_destinations_only = true +} +`) + + ui := cli.NewMockUi() + c := New(ui) + c.testStdin = stdin + + code := c.Run([]string{"-http-addr=" + a.HTTPAddr(), "-"}) + require.Empty(t, ui.ErrorWriter.String()) + require.Contains(t, ui.OutputWriter.String(), + `Config entry written: mesh/mesh`) + require.Equal(t, 0, code) + + entry, _, err := client.ConfigEntries().Get(api.MeshConfig, api.MeshConfigMesh, nil) + require.NoError(t, err) + proxy, ok := entry.(*api.MeshConfigEntry) + require.True(t, ok) + require.Equal(t, map[string]string{"foo": "bar", "gir": "zim"}, proxy.Meta) + }) } // TestParseConfigEntry is the 'api' mirror image of