mirror of https://github.com/status-im/consul.git
Add config entry for terminating gateways (#7545)
This config entry will be used to configure terminating gateways. It accepts the name of the gateway and a list of services the gateway will represent. For each service users will be able to specify: its name, namespace, and additional options for TLS origination. Co-authored-by: Kyle Havlovitz <kylehav@gmail.com> Co-authored-by: Chris Piraino <cpiraino@hashicorp.com>
This commit is contained in:
parent
c911174327
commit
90576060bc
|
@ -182,6 +182,63 @@ func TestConfig_Apply(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConfig_Apply_TerminatingGateway(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
a := NewTestAgent(t, t.Name(), "")
|
||||
defer a.Shutdown()
|
||||
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
|
||||
|
||||
// Create some config entries.
|
||||
body := bytes.NewBuffer([]byte(`
|
||||
{
|
||||
"Kind": "terminating-gateway",
|
||||
"Name": "west-gw-01",
|
||||
"Services": [
|
||||
{
|
||||
"Name": "web",
|
||||
"CAFile": "/etc/web/ca.crt",
|
||||
"CertFile": "/etc/web/client.crt",
|
||||
"KeyFile": "/etc/web/tls.key"
|
||||
},
|
||||
{
|
||||
"Name": "api"
|
||||
}
|
||||
]
|
||||
}`))
|
||||
|
||||
req, _ := http.NewRequest("PUT", "/v1/config", body)
|
||||
resp := httptest.NewRecorder()
|
||||
_, err := a.srv.ConfigApply(resp, req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Code, "!200 Response Code: %s", resp.Body.String())
|
||||
|
||||
// Get the remaining entry.
|
||||
{
|
||||
args := structs.ConfigEntryQuery{
|
||||
Kind: structs.TerminatingGateway,
|
||||
Name: "west-gw-01",
|
||||
Datacenter: "dc1",
|
||||
}
|
||||
var out structs.ConfigEntryResponse
|
||||
require.NoError(t, a.RPC("ConfigEntry.Get", &args, &out))
|
||||
require.NotNil(t, out.Entry)
|
||||
got := out.Entry.(*structs.TerminatingGatewayConfigEntry)
|
||||
expect := []structs.LinkedService{
|
||||
{
|
||||
Name: "web",
|
||||
CAFile: "/etc/web/ca.crt",
|
||||
CertFile: "/etc/web/client.crt",
|
||||
KeyFile: "/etc/web/tls.key",
|
||||
},
|
||||
{
|
||||
Name: "api",
|
||||
},
|
||||
}
|
||||
require.Equal(t, expect, got.Services)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_Apply_ProxyDefaultsMeshGateway(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package state
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
memdb "github.com/hashicorp/go-memdb"
|
||||
|
@ -326,6 +325,7 @@ func (s *Store) validateProposedConfigEntryInGraph(
|
|||
case structs.ServiceSplitter:
|
||||
case structs.ServiceResolver:
|
||||
case structs.IngressGateway:
|
||||
case structs.TerminatingGateway:
|
||||
default:
|
||||
return fmt.Errorf("unhandled kind %q during validation of %q", kind, name)
|
||||
}
|
||||
|
|
|
@ -15,12 +15,13 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
ServiceDefaults string = "service-defaults"
|
||||
ProxyDefaults string = "proxy-defaults"
|
||||
ServiceRouter string = "service-router"
|
||||
ServiceSplitter string = "service-splitter"
|
||||
ServiceResolver string = "service-resolver"
|
||||
IngressGateway string = "ingress-gateway"
|
||||
ServiceDefaults string = "service-defaults"
|
||||
ProxyDefaults string = "proxy-defaults"
|
||||
ServiceRouter string = "service-router"
|
||||
ServiceSplitter string = "service-splitter"
|
||||
ServiceResolver string = "service-resolver"
|
||||
IngressGateway string = "ingress-gateway"
|
||||
TerminatingGateway string = "terminating-gateway"
|
||||
|
||||
ProxyConfigGlobal string = "global"
|
||||
|
||||
|
@ -386,6 +387,15 @@ func ConfigEntryDecodeRulesForKind(kind string) (skipWhenPatching []string, tran
|
|||
}, map[string]string{
|
||||
"service_subset": "servicesubset",
|
||||
}, nil
|
||||
case TerminatingGateway:
|
||||
return []string{
|
||||
"services",
|
||||
"Services",
|
||||
}, map[string]string{
|
||||
"ca_file": "cafile",
|
||||
"cert_file": "certfile",
|
||||
"key_file": "keyfile",
|
||||
}, nil
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("kind %q should be explicitly handled here", kind)
|
||||
}
|
||||
|
@ -478,6 +488,8 @@ func MakeConfigEntry(kind, name string) (ConfigEntry, error) {
|
|||
return &ServiceResolverConfigEntry{Name: name}, nil
|
||||
case IngressGateway:
|
||||
return &IngressGatewayConfigEntry{Name: name}, nil
|
||||
case TerminatingGateway:
|
||||
return &TerminatingGatewayConfigEntry{Name: name}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
||||
}
|
||||
|
@ -489,7 +501,7 @@ func ValidateConfigEntryKind(kind string) bool {
|
|||
return true
|
||||
case ServiceRouter, ServiceSplitter, ServiceResolver:
|
||||
return true
|
||||
case IngressGateway:
|
||||
case IngressGateway, TerminatingGateway:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
|
|
@ -159,3 +159,121 @@ func (e *IngressGatewayConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
|
|||
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
// TerminatingGatewayConfigEntry manages the configuration for a terminating service
|
||||
// with the given name.
|
||||
type TerminatingGatewayConfigEntry struct {
|
||||
Kind string
|
||||
Name string
|
||||
Services []LinkedService
|
||||
|
||||
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
RaftIndex
|
||||
}
|
||||
|
||||
// A LinkedService is a service represented by a terminating gateway
|
||||
type LinkedService struct {
|
||||
// Name is the name of the service, as defined in Consul's catalog
|
||||
Name string `json:",omitempty"`
|
||||
|
||||
// CAFile is the optional path to a CA certificate to use for TLS connections
|
||||
// from the gateway to the linked service
|
||||
CAFile string `json:",omitempty"`
|
||||
|
||||
// CertFile is the optional path to a client certificate to use for TLS connections
|
||||
// from the gateway to the linked service
|
||||
CertFile string `json:",omitempty"`
|
||||
|
||||
// KeyFile is the optional path to a private key to use for TLS connections
|
||||
// from the gateway to the linked service
|
||||
KeyFile string `json:",omitempty"`
|
||||
|
||||
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
}
|
||||
|
||||
func (e *TerminatingGatewayConfigEntry) GetKind() string {
|
||||
return TerminatingGateway
|
||||
}
|
||||
|
||||
func (e *TerminatingGatewayConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *TerminatingGatewayConfigEntry) Normalize() error {
|
||||
if e == nil {
|
||||
return fmt.Errorf("config entry is nil")
|
||||
}
|
||||
|
||||
e.Kind = TerminatingGateway
|
||||
|
||||
for i := range e.Services {
|
||||
e.Services[i].EnterpriseMeta.Normalize()
|
||||
}
|
||||
e.EnterpriseMeta.Normalize()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *TerminatingGatewayConfigEntry) Validate() error {
|
||||
seen := make(map[ServiceID]bool)
|
||||
|
||||
for _, svc := range e.Services {
|
||||
if svc.Name == "" {
|
||||
return fmt.Errorf("Service name cannot be blank.")
|
||||
}
|
||||
|
||||
ns := svc.NamespaceOrDefault()
|
||||
if ns == WildcardSpecifier {
|
||||
return fmt.Errorf("Wildcard namespace is not supported for terminating gateway services")
|
||||
}
|
||||
|
||||
// Check for duplicates within the entry
|
||||
cid := NewServiceID(svc.Name, &svc.EnterpriseMeta)
|
||||
if ok := seen[cid]; ok {
|
||||
return fmt.Errorf("Service %q was specified more than once within a namespace", cid.String())
|
||||
}
|
||||
seen[cid] = true
|
||||
|
||||
// If any TLS config flag was specified, all must be
|
||||
if (svc.CAFile != "" || svc.CertFile != "" || svc.KeyFile != "") &&
|
||||
!(svc.CAFile != "" && svc.CertFile != "" && svc.KeyFile != "") {
|
||||
|
||||
return fmt.Errorf("Service %q must have a CertFile, CAFile, and KeyFile specified for TLS origination", svc.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *TerminatingGatewayConfigEntry) CanRead(authz acl.Authorizer) bool {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
|
||||
return authz.OperatorRead(&authzContext) == acl.Allow
|
||||
}
|
||||
|
||||
func (e *TerminatingGatewayConfigEntry) CanWrite(authz acl.Authorizer) bool {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
|
||||
return authz.OperatorWrite(&authzContext) == acl.Allow
|
||||
}
|
||||
|
||||
func (e *TerminatingGatewayConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
|
||||
return &e.RaftIndex
|
||||
}
|
||||
|
||||
func (e *TerminatingGatewayConfigEntry) GetEnterpriseMeta() *EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
|
|
@ -185,3 +185,161 @@ func TestIngressConfigEntry_Validate(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminatingConfigEntry_Validate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
entry TerminatingGatewayConfigEntry
|
||||
expectErr string
|
||||
}{
|
||||
{
|
||||
name: "service conflict",
|
||||
entry: TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "foo",
|
||||
},
|
||||
{
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: "specified more than once",
|
||||
},
|
||||
{
|
||||
name: "blank service name",
|
||||
entry: TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: "Service name cannot be blank.",
|
||||
},
|
||||
{
|
||||
name: "not all TLS options provided-1",
|
||||
entry: TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "web",
|
||||
CAFile: "ca.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
||||
},
|
||||
{
|
||||
name: "not all TLS options provided-2",
|
||||
entry: TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "web",
|
||||
CertFile: "client.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
||||
},
|
||||
{
|
||||
name: "not all TLS options provided-3",
|
||||
entry: TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "web",
|
||||
KeyFile: "tls.key",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
||||
},
|
||||
{
|
||||
name: "not all TLS options provided-4",
|
||||
entry: TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "web",
|
||||
CAFile: "ca.crt",
|
||||
KeyFile: "tls.key",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
||||
},
|
||||
{
|
||||
name: "not all TLS options provided-5",
|
||||
entry: TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "web",
|
||||
CAFile: "ca.crt",
|
||||
CertFile: "client.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
||||
},
|
||||
{
|
||||
name: "not all TLS options provided-6",
|
||||
entry: TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "web",
|
||||
KeyFile: "tls.key",
|
||||
CertFile: "client.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: "must have a CertFile, CAFile, and KeyFile",
|
||||
},
|
||||
{
|
||||
name: "all TLS options provided",
|
||||
entry: TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "web",
|
||||
CAFile: "ca.crt",
|
||||
CertFile: "client.crt",
|
||||
KeyFile: "tls.key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range cases {
|
||||
// We explicitly copy the variable for the range statement so that can run
|
||||
// tests in parallel.
|
||||
tc := test
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := tc.entry.Validate()
|
||||
if tc.expectErr != "" {
|
||||
require.Error(t, err)
|
||||
requireContainsLower(t, err.Error(), tc.expectErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -647,6 +647,63 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "terminating-gateway: kitchen sink",
|
||||
snake: `
|
||||
kind = "terminating-gateway"
|
||||
name = "terminating-gw-west"
|
||||
services = [
|
||||
{
|
||||
name = "payments",
|
||||
ca_file = "/etc/payments/ca.pem",
|
||||
cert_file = "/etc/payments/cert.pem",
|
||||
key_file = "/etc/payments/tls.key",
|
||||
},
|
||||
{
|
||||
name = "*",
|
||||
ca_file = "/etc/all/ca.pem",
|
||||
cert_file = "/etc/all/cert.pem",
|
||||
key_file = "/etc/all/tls.key",
|
||||
},
|
||||
]
|
||||
`,
|
||||
camel: `
|
||||
Kind = "terminating-gateway"
|
||||
Name = "terminating-gw-west"
|
||||
Services = [
|
||||
{
|
||||
Name = "payments",
|
||||
CAFile = "/etc/payments/ca.pem",
|
||||
CertFile = "/etc/payments/cert.pem",
|
||||
KeyFile = "/etc/payments/tls.key",
|
||||
},
|
||||
{
|
||||
Name = "*",
|
||||
CAFile = "/etc/all/ca.pem",
|
||||
CertFile = "/etc/all/cert.pem",
|
||||
KeyFile = "/etc/all/tls.key",
|
||||
},
|
||||
]
|
||||
`,
|
||||
expect: &TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Name: "payments",
|
||||
CAFile: "/etc/payments/ca.pem",
|
||||
CertFile: "/etc/payments/cert.pem",
|
||||
KeyFile: "/etc/payments/tls.key",
|
||||
},
|
||||
{
|
||||
Name: "*",
|
||||
CAFile: "/etc/all/ca.pem",
|
||||
CertFile: "/etc/all/cert.pem",
|
||||
KeyFile: "/etc/all/tls.key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
|
||||
|
|
|
@ -12,12 +12,13 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
ServiceDefaults string = "service-defaults"
|
||||
ProxyDefaults string = "proxy-defaults"
|
||||
ServiceRouter string = "service-router"
|
||||
ServiceSplitter string = "service-splitter"
|
||||
ServiceResolver string = "service-resolver"
|
||||
IngressGateway string = "ingress-gateway"
|
||||
ServiceDefaults string = "service-defaults"
|
||||
ProxyDefaults string = "proxy-defaults"
|
||||
ServiceRouter string = "service-router"
|
||||
ServiceSplitter string = "service-splitter"
|
||||
ServiceResolver string = "service-resolver"
|
||||
IngressGateway string = "ingress-gateway"
|
||||
TerminatingGateway string = "terminating-gateway"
|
||||
|
||||
ProxyConfigGlobal string = "global"
|
||||
)
|
||||
|
@ -141,11 +142,6 @@ func (p *ProxyConfigEntry) GetModifyIndex() uint64 {
|
|||
return p.ModifyIndex
|
||||
}
|
||||
|
||||
type rawEntryListResponse struct {
|
||||
kind string
|
||||
Entries []map[string]interface{}
|
||||
}
|
||||
|
||||
func makeConfigEntry(kind, name string) (ConfigEntry, error) {
|
||||
switch kind {
|
||||
case ServiceDefaults:
|
||||
|
@ -160,6 +156,8 @@ func makeConfigEntry(kind, name string) (ConfigEntry, error) {
|
|||
return &ServiceResolverConfigEntry{Kind: kind, Name: name}, nil
|
||||
case IngressGateway:
|
||||
return &IngressGatewayConfigEntry{Kind: kind, Name: name}, nil
|
||||
case TerminatingGateway:
|
||||
return &TerminatingGatewayConfigEntry{Kind: kind, Name: name}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
||||
}
|
||||
|
|
|
@ -82,3 +82,67 @@ func (i *IngressGatewayConfigEntry) GetCreateIndex() uint64 {
|
|||
func (i *IngressGatewayConfigEntry) GetModifyIndex() uint64 {
|
||||
return i.ModifyIndex
|
||||
}
|
||||
|
||||
// TerminatingGatewayConfigEntry manages the configuration for a terminating gateway
|
||||
// with the given name.
|
||||
type TerminatingGatewayConfigEntry struct {
|
||||
// Kind of the config entry. This should be set to api.TerminatingGateway.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated terminating gateway
|
||||
// service. This should match the name provided in the service definition.
|
||||
Name string
|
||||
|
||||
// Services is a list of service names represented by the terminating gateway.
|
||||
Services []LinkedService `json:",omitempty"`
|
||||
|
||||
// CreateIndex is the Raft index this entry was created at. This is a
|
||||
// read-only field.
|
||||
CreateIndex uint64
|
||||
|
||||
// ModifyIndex is used for the Check-And-Set operations and can also be fed
|
||||
// back into the WaitIndex of the QueryOptions in order to perform blocking
|
||||
// queries.
|
||||
ModifyIndex uint64
|
||||
|
||||
// Namespace is the namespace the config entry is associated with
|
||||
// Namespacing is a Consul Enterprise feature.
|
||||
Namespace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// A LinkedService is a service represented by a terminating gateway
|
||||
type LinkedService struct {
|
||||
// The namespace the service is registered in
|
||||
Namespace string `json:",omitempty"`
|
||||
|
||||
// Name is the name of the service, as defined in Consul's catalog
|
||||
Name string `json:",omitempty"`
|
||||
|
||||
// CAFile is the optional path to a CA certificate to use for TLS connections
|
||||
// from the gateway to the linked service
|
||||
CAFile string `json:",omitempty"`
|
||||
|
||||
// CertFile is the optional path to a client certificate to use for TLS connections
|
||||
// from the gateway to the linked service
|
||||
CertFile string `json:",omitempty"`
|
||||
|
||||
// KeyFile is the optional path to a private key to use for TLS connections
|
||||
// from the gateway to the linked service
|
||||
KeyFile string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (g *TerminatingGatewayConfigEntry) GetKind() string {
|
||||
return g.Kind
|
||||
}
|
||||
|
||||
func (g *TerminatingGatewayConfigEntry) GetName() string {
|
||||
return g.Name
|
||||
}
|
||||
|
||||
func (g *TerminatingGatewayConfigEntry) GetCreateIndex() uint64 {
|
||||
return g.CreateIndex
|
||||
}
|
||||
|
||||
func (g *TerminatingGatewayConfigEntry) GetModifyIndex() uint64 {
|
||||
return g.ModifyIndex
|
||||
}
|
||||
|
|
|
@ -125,3 +125,117 @@ func TestAPI_ConfigEntries_IngressGateway(t *testing.T) {
|
|||
entry, qm, err = config_entries.Get(IngressGateway, "foo", nil)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestAPI_ConfigEntries_TerminatingGateway(t *testing.T) {
|
||||
t.Parallel()
|
||||
c, s := makeClient(t)
|
||||
defer s.Stop()
|
||||
|
||||
configEntries := c.ConfigEntries()
|
||||
|
||||
terminating1 := &TerminatingGatewayConfigEntry{
|
||||
Kind: TerminatingGateway,
|
||||
Name: "foo",
|
||||
}
|
||||
|
||||
terminating2 := &TerminatingGatewayConfigEntry{
|
||||
Kind: TerminatingGateway,
|
||||
Name: "bar",
|
||||
}
|
||||
|
||||
// set it
|
||||
_, wm, err := configEntries.Set(terminating1, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, wm)
|
||||
require.NotEqual(t, 0, wm.RequestTime)
|
||||
|
||||
// also set the second one
|
||||
_, wm, err = configEntries.Set(terminating2, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, wm)
|
||||
require.NotEqual(t, 0, wm.RequestTime)
|
||||
|
||||
// get it
|
||||
entry, qm, err := configEntries.Get(TerminatingGateway, "foo", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, qm)
|
||||
require.NotEqual(t, 0, qm.RequestTime)
|
||||
|
||||
// verify it
|
||||
readTerminating, ok := entry.(*TerminatingGatewayConfigEntry)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, terminating1.Kind, readTerminating.Kind)
|
||||
require.Equal(t, terminating1.Name, readTerminating.Name)
|
||||
|
||||
// update it
|
||||
terminating1.Services = []LinkedService{
|
||||
{
|
||||
Name: "web",
|
||||
CAFile: "/etc/web/ca.crt",
|
||||
CertFile: "/etc/web/client.crt",
|
||||
KeyFile: "/etc/web/tls.key",
|
||||
},
|
||||
}
|
||||
|
||||
// CAS fail
|
||||
written, _, err := configEntries.CAS(terminating1, 0, nil)
|
||||
require.NoError(t, err)
|
||||
require.False(t, written)
|
||||
|
||||
// CAS success
|
||||
written, wm, err = configEntries.CAS(terminating1, readTerminating.ModifyIndex, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, wm)
|
||||
require.NotEqual(t, 0, wm.RequestTime)
|
||||
require.True(t, written)
|
||||
|
||||
// update no cas
|
||||
terminating2.Services = []LinkedService{
|
||||
{
|
||||
Name: "*",
|
||||
CAFile: "/etc/certs/ca.crt",
|
||||
CertFile: "/etc/certs/client.crt",
|
||||
KeyFile: "/etc/certs/tls.key",
|
||||
},
|
||||
}
|
||||
_, wm, err = configEntries.Set(terminating2, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, wm)
|
||||
require.NotEqual(t, 0, wm.RequestTime)
|
||||
|
||||
// list them
|
||||
entries, qm, err := configEntries.List(TerminatingGateway, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, qm)
|
||||
require.NotEqual(t, 0, qm.RequestTime)
|
||||
require.Len(t, entries, 2)
|
||||
|
||||
for _, entry = range entries {
|
||||
switch entry.GetName() {
|
||||
case "foo":
|
||||
// this also verifies that the update value was persisted and
|
||||
// the updated values are seen
|
||||
readTerminating, ok = entry.(*TerminatingGatewayConfigEntry)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, terminating1.Kind, readTerminating.Kind)
|
||||
require.Equal(t, terminating1.Name, readTerminating.Name)
|
||||
require.Equal(t, terminating1.Services, readTerminating.Services)
|
||||
case "bar":
|
||||
readTerminating, ok = entry.(*TerminatingGatewayConfigEntry)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, terminating2.Kind, readTerminating.Kind)
|
||||
require.Equal(t, terminating2.Name, readTerminating.Name)
|
||||
require.Equal(t, terminating2.Services, readTerminating.Services)
|
||||
}
|
||||
}
|
||||
|
||||
// delete it
|
||||
wm, err = configEntries.Delete(TerminatingGateway, "foo", nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, wm)
|
||||
require.NotEqual(t, 0, wm.RequestTime)
|
||||
|
||||
// verify deletion
|
||||
entry, qm, err = configEntries.Get(TerminatingGateway, "foo", nil)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
|
|
@ -674,6 +674,50 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "terminating-gateway",
|
||||
body: `
|
||||
{
|
||||
"Kind": "terminating-gateway",
|
||||
"Name": "terminating-west",
|
||||
"Services": [
|
||||
{
|
||||
"Namespace": "foo",
|
||||
"Name": "web",
|
||||
"CAFile": "/etc/ca.pem",
|
||||
"CertFile": "/etc/cert.pem",
|
||||
"KeyFile": "/etc/tls.key"
|
||||
},
|
||||
{
|
||||
"Name": "api"
|
||||
},
|
||||
{
|
||||
"Namespace": "bar",
|
||||
"Name": "*"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
expect: &TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-west",
|
||||
Services: []LinkedService{
|
||||
{
|
||||
Namespace: "foo",
|
||||
Name: "web",
|
||||
CAFile: "/etc/ca.pem",
|
||||
CertFile: "/etc/cert.pem",
|
||||
KeyFile: "/etc/tls.key",
|
||||
},
|
||||
{
|
||||
Name: "api",
|
||||
},
|
||||
{
|
||||
Namespace: "bar",
|
||||
Name: "*",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
|
||||
|
|
|
@ -245,6 +245,121 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "terminating-gateway",
|
||||
snake: `
|
||||
kind = "terminating-gateway"
|
||||
name = "terminating-gw-west"
|
||||
namespace = "default"
|
||||
services = [
|
||||
{
|
||||
name = "billing"
|
||||
namespace = "biz"
|
||||
ca_file = "/etc/ca.crt"
|
||||
cert_file = "/etc/client.crt"
|
||||
key_file = "/etc/tls.key"
|
||||
},
|
||||
{
|
||||
name = "*"
|
||||
namespace = "ops"
|
||||
}
|
||||
]
|
||||
`,
|
||||
camel: `
|
||||
Kind = "terminating-gateway"
|
||||
Name = "terminating-gw-west"
|
||||
Namespace = "default"
|
||||
Services = [
|
||||
{
|
||||
Name = "billing"
|
||||
Namespace = "biz"
|
||||
CAFile = "/etc/ca.crt"
|
||||
CertFile = "/etc/client.crt"
|
||||
KeyFile = "/etc/tls.key"
|
||||
},
|
||||
{
|
||||
Name = "*"
|
||||
Namespace = "ops"
|
||||
}
|
||||
]
|
||||
`,
|
||||
snakeJSON: `
|
||||
{
|
||||
"kind": "terminating-gateway",
|
||||
"name": "terminating-gw-west",
|
||||
"namespace": "default",
|
||||
"services": [
|
||||
{
|
||||
"name": "billing",
|
||||
"namespace": "biz",
|
||||
"ca_file": "/etc/ca.crt",
|
||||
"cert_file": "/etc/client.crt",
|
||||
"key_file": "/etc/tls.key"
|
||||
},
|
||||
{
|
||||
"name": "*",
|
||||
"namespace": "ops"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
camelJSON: `
|
||||
{
|
||||
"Kind": "terminating-gateway",
|
||||
"Name": "terminating-gw-west",
|
||||
"Namespace": "default",
|
||||
"Services": [
|
||||
{
|
||||
"Name": "billing",
|
||||
"Namespace": "biz",
|
||||
"CAFile": "/etc/ca.crt",
|
||||
"CertFile": "/etc/client.crt",
|
||||
"KeyFile": "/etc/tls.key"
|
||||
},
|
||||
{
|
||||
"Name": "*",
|
||||
"Namespace": "ops"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
expect: &api.TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Namespace: "default",
|
||||
Services: []api.LinkedService{
|
||||
{
|
||||
Name: "billing",
|
||||
Namespace: "biz",
|
||||
CAFile: "/etc/ca.crt",
|
||||
CertFile: "/etc/client.crt",
|
||||
KeyFile: "/etc/tls.key",
|
||||
},
|
||||
{
|
||||
Name: "*",
|
||||
Namespace: "ops",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectJSON: &api.TerminatingGatewayConfigEntry{
|
||||
Kind: "terminating-gateway",
|
||||
Name: "terminating-gw-west",
|
||||
Namespace: "default",
|
||||
Services: []api.LinkedService{
|
||||
{
|
||||
Name: "billing",
|
||||
Namespace: "biz",
|
||||
CAFile: "/etc/ca.crt",
|
||||
CertFile: "/etc/client.crt",
|
||||
KeyFile: "/etc/tls.key",
|
||||
},
|
||||
{
|
||||
Name: "*",
|
||||
Namespace: "ops",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "service-defaults",
|
||||
snake: `
|
||||
|
@ -1127,7 +1242,6 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
snake: `
|
||||
kind = "ingress-gateway"
|
||||
name = "ingress-web"
|
||||
|
||||
listeners = [
|
||||
{
|
||||
port = 8080
|
||||
|
|
Loading…
Reference in New Issue