Native API Gateway Config Entries (#15897)
* Stub Config Entries for Consul Native API Gateway (#15644) * Add empty InlineCertificate struct and protobuf * apigateway stubs * Stub HTTPRoute in api pkg * Stub HTTPRoute in structs pkg * Simplify api.APIGatewayConfigEntry to be consistent w/ other entries * Update makeConfigEntry switch, add docstring for HTTPRouteConfigEntry * Add TCPRoute to MakeConfigEntry, return unique Kind * Stub BoundAPIGatewayConfigEntry in agent * Add RaftIndex to APIGatewayConfigEntry stub * Add new config entry kinds to validation allow-list * Add RaftIndex to other added config entry stubs * Update usage metrics assertions to include new cfg entries * Add Meta and acl.EnterpriseMeta to all new ConfigEntry types * Remove unnecessary Services field from added config entry types * Implement GetMeta(), GetEnterpriseMeta() for added config entry types * Add meta field to proto, name consistently w/ existing config entries * Format config_entry.proto * Add initial implementation of CanRead + CanWrite for new config entry types * Add unit tests for decoding of new config entry types * Add unit tests for parsing of new config entry types * Add unit tests for API Gateway config entry ACLs * Return typed PermissionDeniedError on BoundAPIGateway CanWrite * Add unit tests for added config entry ACLs * Add BoundAPIGateway type to AllConfigEntryKinds * Return proper kind from BoundAPIGateway * Add docstrings for new config entry types * Add missing config entry kinds to proto def * Update usagemetrics_oss_test.go * Use utility func for returning PermissionDeniedError * EventPublisher subscriptions for Consul Native API Gateway (#15757) * Create new event topics in subscribe proto * Add tests for PBSubscribe func * Make configs singular, add all configs to PBToStreamSubscribeRequest * Add snapshot methods * Add config_entry_events tests * Add config entry kind to topic for new configs * Add unit tests for snapshot methods * Start adding integration test * Test using the new controller code * Update agent/consul/state/config_entry_events.go * Check value of error * Add controller stubs for API Gateway (#15837) * update initial stub implementation * move files, clean up mutex references * Remove embed, use idiomatic names for constructors * Remove stray file introduced in merge * Add APIGateway validation (#15847) * Add APIGateway validation * Add additional validations * Add cert ref validation * Add protobuf definitions * Fix up field types * Add API structs * Move struct fields around a bit * APIGateway InlineCertificate validation (#15856) * Add APIGateway validation * Add additional validations * Add protobuf definitions * Tabs to spaces * Add API structs * Move struct fields around a bit * Add validation for InlineCertificate * Fix ACL test * APIGateway BoundAPIGateway validation (#15858) * Add APIGateway validation * Add additional validations * Add cert ref validation * Add protobuf definitions * Fix up field types * Add API structs * Move struct fields around a bit * Add validation for BoundAPIGateway * APIGateway TCPRoute validation (#15855) * Add APIGateway validation * Add additional validations * Add cert ref validation * Add protobuf definitions * Fix up field types * Add API structs * Add TCPRoute normalization and validation * Add forgotten Status * Add some more field docs in api package * Fix test * Format imports * Rename snapshot test variable names * Add plumbing for Native API GW Subscriptions (#16003) Co-authored-by: Sarah Alsmiller <sarah.alsmiller@hashicorp.com> Co-authored-by: Nathan Coleman <nathan.coleman@hashicorp.com> Co-authored-by: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Co-authored-by: Andrew Stucki <andrew.stucki@hashicorp.com>
This commit is contained in:
parent
66a88b65f2
commit
f198544270
|
@ -70,7 +70,7 @@ type PermissionDeniedError struct {
|
|||
Accessor string
|
||||
// Resource (e.g. Service)
|
||||
Resource Resource
|
||||
// Access leve (e.g. Read)
|
||||
// Access level (e.g. Read)
|
||||
AccessLevel AccessLevel
|
||||
// e.g. "sidecar-proxy-1"
|
||||
ResourceID ResourceDescriptor
|
||||
|
|
|
@ -64,7 +64,7 @@ LOOP:
|
|||
}
|
||||
}
|
||||
|
||||
// since we only modified each entry once, we should have exactly 200 reconcliation calls
|
||||
// since we only modified each entry once, we should have exactly 200 reconciliation calls
|
||||
require.Len(t, received, 200)
|
||||
for i := 0; i < 200; i++ {
|
||||
require.Contains(t, received, fmt.Sprintf("foo-%d", i))
|
||||
|
@ -271,3 +271,147 @@ func TestBasicController_RunPanicAssertions(t *testing.T) {
|
|||
controller.WithQueueFactory(RunWorkQueue)
|
||||
})
|
||||
}
|
||||
|
||||
func TestConfigEntrySubscriptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := map[string]struct {
|
||||
configEntry func(string) structs.ConfigEntry
|
||||
topic stream.Topic
|
||||
kind string
|
||||
}{
|
||||
"Subscribe to Service Resolver Config Changes": {
|
||||
configEntry: func(name string) structs.ConfigEntry {
|
||||
return &structs.ServiceResolverConfigEntry{
|
||||
Kind: structs.ServiceResolver,
|
||||
Name: name,
|
||||
}
|
||||
},
|
||||
topic: state.EventTopicServiceResolver,
|
||||
kind: structs.ServiceResolver,
|
||||
},
|
||||
"Subscribe to Ingress Gateway Changes": {
|
||||
configEntry: func(name string) structs.ConfigEntry {
|
||||
return &structs.IngressGatewayConfigEntry{
|
||||
Kind: structs.IngressGateway,
|
||||
Name: name,
|
||||
}
|
||||
},
|
||||
topic: state.EventTopicIngressGateway,
|
||||
kind: structs.IngressGateway,
|
||||
},
|
||||
"Subscribe to Service Intentions Changes": {
|
||||
configEntry: func(name string) structs.ConfigEntry {
|
||||
return &structs.ServiceIntentionsConfigEntry{
|
||||
Kind: structs.ServiceIntentions,
|
||||
Name: name,
|
||||
}
|
||||
},
|
||||
topic: state.EventTopicServiceIntentions,
|
||||
kind: structs.ServiceIntentions,
|
||||
},
|
||||
"Subscribe to API Gateway Changes": {
|
||||
configEntry: func(name string) structs.ConfigEntry {
|
||||
return &structs.APIGatewayConfigEntry{
|
||||
Kind: structs.APIGateway,
|
||||
Name: name,
|
||||
}
|
||||
},
|
||||
topic: state.EventTopicAPIGateway,
|
||||
kind: structs.APIGateway,
|
||||
},
|
||||
"Subscribe to Inline Certificate Changes": {
|
||||
configEntry: func(name string) structs.ConfigEntry {
|
||||
return &structs.InlineCertificateConfigEntry{
|
||||
Kind: structs.InlineCertificate,
|
||||
Name: name,
|
||||
}
|
||||
},
|
||||
topic: state.EventTopicInlineCertificate,
|
||||
kind: structs.InlineCertificate,
|
||||
},
|
||||
"Subscribe to HTTP Route Changes": {
|
||||
configEntry: func(name string) structs.ConfigEntry {
|
||||
return &structs.HTTPRouteConfigEntry{
|
||||
Kind: structs.HTTPRoute,
|
||||
Name: name,
|
||||
}
|
||||
},
|
||||
topic: state.EventTopicHTTPRoute,
|
||||
kind: structs.HTTPRoute,
|
||||
},
|
||||
"Subscribe to TCP Route Changes": {
|
||||
configEntry: func(name string) structs.ConfigEntry {
|
||||
return &structs.TCPRouteConfigEntry{
|
||||
Kind: structs.TCPRoute,
|
||||
Name: name,
|
||||
}
|
||||
},
|
||||
topic: state.EventTopicTCPRoute,
|
||||
kind: structs.TCPRoute,
|
||||
},
|
||||
"Subscribe to Bound API Gateway Changes": {
|
||||
configEntry: func(name string) structs.ConfigEntry {
|
||||
return &structs.BoundAPIGatewayConfigEntry{
|
||||
Kind: structs.BoundAPIGateway,
|
||||
Name: name,
|
||||
}
|
||||
},
|
||||
topic: state.EventTopicBoundAPIGateway,
|
||||
kind: structs.BoundAPIGateway,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
reconciler := newTestReconciler(false)
|
||||
|
||||
publisher := stream.NewEventPublisher(1 * time.Millisecond)
|
||||
go publisher.Run(ctx)
|
||||
|
||||
// get the store through the FSM since the publisher handlers get registered through it
|
||||
store := fsm.NewFromDeps(fsm.Deps{
|
||||
Logger: hclog.New(nil),
|
||||
NewStateStore: func() *state.Store {
|
||||
return state.NewStateStoreWithEventPublisher(nil, publisher)
|
||||
},
|
||||
Publisher: publisher,
|
||||
}).State()
|
||||
|
||||
for i := 0; i < 200; i++ {
|
||||
entryIndex := uint64(i + 1)
|
||||
name := fmt.Sprintf("foo-%d", i)
|
||||
require.NoError(t, store.EnsureConfigEntry(entryIndex, tc.configEntry(name)))
|
||||
}
|
||||
|
||||
go New(publisher, reconciler).Subscribe(&stream.SubscribeRequest{
|
||||
Topic: tc.topic,
|
||||
Subject: stream.SubjectWildcard,
|
||||
}).WithWorkers(10).Run(ctx)
|
||||
|
||||
received := []string{}
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case request := <-reconciler.received:
|
||||
require.Equal(t, tc.kind, request.Kind)
|
||||
received = append(received, request.Name)
|
||||
if len(received) == 200 {
|
||||
break LOOP
|
||||
}
|
||||
case <-ctx.Done():
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
|
||||
// since we only modified each entry once, we should have exactly 200 reconciliation calls
|
||||
require.Len(t, received, 200)
|
||||
for i := 0; i < 200; i++ {
|
||||
require.Contains(t, received, fmt.Sprintf("foo-%d", i))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -342,4 +342,39 @@ func (c *FSM) registerStreamSnapshotHandlers() {
|
|||
if err != nil {
|
||||
panic(fmt.Errorf("fatal error encountered registering streaming snapshot handlers: %w", err))
|
||||
}
|
||||
|
||||
err = c.deps.Publisher.RegisterHandler(state.EventTopicAPIGateway, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return c.State().APIGatewaySnapshot(req, buf)
|
||||
}, true)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("fatal error encountered registering streaming snapshot handlers: %w", err))
|
||||
}
|
||||
|
||||
err = c.deps.Publisher.RegisterHandler(state.EventTopicInlineCertificate, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return c.State().InlineCertificateSnapshot(req, buf)
|
||||
}, true)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("fatal error encountered registering streaming snapshot handlers: %w", err))
|
||||
}
|
||||
|
||||
err = c.deps.Publisher.RegisterHandler(state.EventTopicHTTPRoute, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return c.State().HTTPRouteSnapshot(req, buf)
|
||||
}, true)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("fatal error encountered registering streaming snapshot handlers: %w", err))
|
||||
}
|
||||
|
||||
err = c.deps.Publisher.RegisterHandler(state.EventTopicTCPRoute, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return c.State().TCPRouteSnapshot(req, buf)
|
||||
}, true)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("fatal error encountered registering streaming snapshot handlers: %w", err))
|
||||
}
|
||||
|
||||
err = c.deps.Publisher.RegisterHandler(state.EventTopicBoundAPIGateway, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return c.State().BoundAPIGatewaySnapshot(req, buf)
|
||||
}, true)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("fatal error encountered registering streaming snapshot handlers: %w", err))
|
||||
}
|
||||
}
|
||||
|
|
34
agent/consul/gateways/controller_gateways.go
Normal file
34
agent/consul/gateways/controller_gateways.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package gateways
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
|
||||
"github.com/hashicorp/consul/agent/consul/controller"
|
||||
"github.com/hashicorp/consul/agent/consul/fsm"
|
||||
"github.com/hashicorp/consul/agent/consul/state"
|
||||
"github.com/hashicorp/consul/agent/consul/stream"
|
||||
)
|
||||
|
||||
type apiGatewayReconciler struct {
|
||||
fsm *fsm.FSM
|
||||
logger hclog.Logger
|
||||
}
|
||||
|
||||
func (r apiGatewayReconciler) Reconcile(ctx context.Context, req controller.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewAPIGatewayController(fsm *fsm.FSM, publisher state.EventPublisher, logger hclog.Logger) controller.Controller {
|
||||
reconciler := apiGatewayReconciler{
|
||||
fsm: fsm,
|
||||
logger: logger,
|
||||
}
|
||||
return controller.New(publisher, reconciler).Subscribe(
|
||||
&stream.SubscribeRequest{
|
||||
Topic: state.EventTopicAPIGateway,
|
||||
Subject: stream.SubjectWildcard,
|
||||
},
|
||||
)
|
||||
}
|
56
agent/consul/gateways/controller_routes.go
Normal file
56
agent/consul/gateways/controller_routes.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package gateways
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
|
||||
"github.com/hashicorp/consul/agent/consul/controller"
|
||||
"github.com/hashicorp/consul/agent/consul/fsm"
|
||||
"github.com/hashicorp/consul/agent/consul/state"
|
||||
"github.com/hashicorp/consul/agent/consul/stream"
|
||||
)
|
||||
|
||||
type tcpRouteReconciler struct {
|
||||
fsm *fsm.FSM
|
||||
logger hclog.Logger
|
||||
}
|
||||
|
||||
func (r tcpRouteReconciler) Reconcile(ctx context.Context, req controller.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTCPRouteController(fsm *fsm.FSM, publisher state.EventPublisher, logger hclog.Logger) controller.Controller {
|
||||
reconciler := tcpRouteReconciler{
|
||||
fsm: fsm,
|
||||
logger: logger,
|
||||
}
|
||||
return controller.New(publisher, reconciler).Subscribe(
|
||||
&stream.SubscribeRequest{
|
||||
Topic: state.EventTopicTCPRoute,
|
||||
Subject: stream.SubjectWildcard,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
type httpRouteReconciler struct {
|
||||
fsm *fsm.FSM
|
||||
logger hclog.Logger
|
||||
}
|
||||
|
||||
func (r httpRouteReconciler) Reconcile(ctx context.Context, req controller.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewHTTPRouteController(fsm *fsm.FSM, publisher state.EventPublisher, logger hclog.Logger) controller.Controller {
|
||||
reconciler := httpRouteReconciler{
|
||||
fsm: fsm,
|
||||
logger: logger,
|
||||
}
|
||||
return controller.New(publisher, reconciler).Subscribe(
|
||||
&stream.SubscribeRequest{
|
||||
Topic: state.EventTopicHTTPRoute,
|
||||
Subject: stream.SubjectWildcard,
|
||||
},
|
||||
)
|
||||
}
|
|
@ -496,6 +496,11 @@ func validateProposedConfigEntryInGraph(
|
|||
case structs.ServiceIntentions:
|
||||
case structs.MeshConfig:
|
||||
case structs.ExportedServices:
|
||||
case structs.APIGateway: // TODO Consider checkGatewayClash
|
||||
case structs.BoundAPIGateway:
|
||||
case structs.InlineCertificate:
|
||||
case structs.HTTPRoute:
|
||||
case structs.TCPRoute:
|
||||
default:
|
||||
return fmt.Errorf("unhandled kind %q during validation of %q", kindName.Kind, kindName.Name)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,11 @@ var configEntryKindToTopic = map[string]stream.Topic{
|
|||
structs.IngressGateway: EventTopicIngressGateway,
|
||||
structs.ServiceIntentions: EventTopicServiceIntentions,
|
||||
structs.ServiceDefaults: EventTopicServiceDefaults,
|
||||
structs.APIGateway: EventTopicAPIGateway,
|
||||
structs.TCPRoute: EventTopicTCPRoute,
|
||||
structs.HTTPRoute: EventTopicHTTPRoute,
|
||||
structs.InlineCertificate: EventTopicInlineCertificate,
|
||||
structs.BoundAPIGateway: EventTopicBoundAPIGateway,
|
||||
}
|
||||
|
||||
// EventSubjectConfigEntry is a stream.Subject used to route and receive events
|
||||
|
@ -117,6 +122,36 @@ func (s *Store) ServiceDefaultsSnapshot(req stream.SubscribeRequest, buf stream.
|
|||
return s.configEntrySnapshot(structs.ServiceDefaults, req, buf)
|
||||
}
|
||||
|
||||
// APIGatewaySnapshot is a stream.SnapshotFunc that returns a snapshot of
|
||||
// api-gateway config entries.
|
||||
func (s *Store) APIGatewaySnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return s.configEntrySnapshot(structs.APIGateway, req, buf)
|
||||
}
|
||||
|
||||
// TCPRouteSnapshot is a stream.SnapshotFunc that returns a snapshot of
|
||||
// tcp-route config entries.
|
||||
func (s *Store) TCPRouteSnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return s.configEntrySnapshot(structs.TCPRoute, req, buf)
|
||||
}
|
||||
|
||||
// HTTPRouteSnapshot is a stream.SnapshotFunc that retuns a snapshot of
|
||||
// http-route config entries.
|
||||
func (s *Store) HTTPRouteSnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return s.configEntrySnapshot(structs.HTTPRoute, req, buf)
|
||||
}
|
||||
|
||||
// InlineCertificateSnapshot is a stream.SnapshotFunc that returns a snapshot of
|
||||
// inline-certificate config entries.
|
||||
func (s *Store) InlineCertificateSnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return s.configEntrySnapshot(structs.InlineCertificate, req, buf)
|
||||
}
|
||||
|
||||
// BoundAPIGatewaySnapshot is a stream.SnapshotFunc that returns a snapshot of
|
||||
// bound-api-gateway config entries.
|
||||
func (s *Store) BoundAPIGatewaySnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
return s.configEntrySnapshot(structs.BoundAPIGateway, req, buf)
|
||||
}
|
||||
|
||||
func (s *Store) configEntrySnapshot(kind string, req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||
var (
|
||||
idx uint64
|
||||
|
|
|
@ -547,3 +547,348 @@ func TestServiceDefaultsSnapshot(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIGatewaySnapshot(t *testing.T) {
|
||||
const index uint64 = 123
|
||||
|
||||
gw1 := &structs.APIGatewayConfigEntry{
|
||||
Kind: structs.APIGateway,
|
||||
Name: "agw1",
|
||||
}
|
||||
gw2 := &structs.APIGatewayConfigEntry{
|
||||
Kind: structs.APIGateway,
|
||||
Name: "agw2",
|
||||
}
|
||||
|
||||
store := testStateStore(t)
|
||||
require.NoError(t, store.EnsureConfigEntry(index, gw1))
|
||||
require.NoError(t, store.EnsureConfigEntry(index, gw2))
|
||||
|
||||
testCases := map[string]struct {
|
||||
subject stream.Subject
|
||||
events []stream.Event
|
||||
}{
|
||||
"named entry": {
|
||||
subject: EventSubjectConfigEntry{Name: gw1.Name},
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicAPIGateway,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: gw1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"wildcard": {
|
||||
subject: stream.SubjectWildcard,
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicAPIGateway,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: gw1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Topic: EventTopicAPIGateway,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: gw2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range testCases {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
buf := &snapshotAppender{}
|
||||
|
||||
idx, err := store.APIGatewaySnapshot(stream.SubscribeRequest{Subject: tc.subject}, buf)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, index, idx)
|
||||
require.Len(t, buf.events, 1)
|
||||
require.ElementsMatch(t, tc.events, buf.events[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTCPRouteSnapshot(t *testing.T) {
|
||||
const index uint64 = 123
|
||||
|
||||
rt1 := &structs.TCPRouteConfigEntry{
|
||||
Kind: structs.TCPRoute,
|
||||
Name: "tcprt1",
|
||||
}
|
||||
rt2 := &structs.TCPRouteConfigEntry{
|
||||
Kind: structs.TCPRoute,
|
||||
Name: "tcprt2",
|
||||
}
|
||||
|
||||
store := testStateStore(t)
|
||||
require.NoError(t, store.EnsureConfigEntry(index, rt1))
|
||||
require.NoError(t, store.EnsureConfigEntry(index, rt2))
|
||||
|
||||
testCases := map[string]struct {
|
||||
subject stream.Subject
|
||||
events []stream.Event
|
||||
}{
|
||||
"named entry": {
|
||||
subject: EventSubjectConfigEntry{Name: rt1.Name},
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicTCPRoute,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: rt1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"wildcard": {
|
||||
subject: stream.SubjectWildcard,
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicTCPRoute,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: rt1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Topic: EventTopicTCPRoute,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: rt2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range testCases {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
buf := &snapshotAppender{}
|
||||
|
||||
idx, err := store.TCPRouteSnapshot(stream.SubscribeRequest{Subject: tc.subject}, buf)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, index, idx)
|
||||
require.Len(t, buf.events, 1)
|
||||
require.ElementsMatch(t, tc.events, buf.events[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPRouteSnapshot(t *testing.T) {
|
||||
const index uint64 = 123
|
||||
|
||||
rt1 := &structs.HTTPRouteConfigEntry{
|
||||
Kind: structs.HTTPRoute,
|
||||
Name: "httprt1",
|
||||
}
|
||||
gw2 := &structs.HTTPRouteConfigEntry{
|
||||
Kind: structs.HTTPRoute,
|
||||
Name: "httprt2",
|
||||
}
|
||||
|
||||
store := testStateStore(t)
|
||||
require.NoError(t, store.EnsureConfigEntry(index, rt1))
|
||||
require.NoError(t, store.EnsureConfigEntry(index, gw2))
|
||||
|
||||
testCases := map[string]struct {
|
||||
subject stream.Subject
|
||||
events []stream.Event
|
||||
}{
|
||||
"named entry": {
|
||||
subject: EventSubjectConfigEntry{Name: rt1.Name},
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicHTTPRoute,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: rt1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"wildcard": {
|
||||
subject: stream.SubjectWildcard,
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicHTTPRoute,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: rt1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Topic: EventTopicHTTPRoute,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: gw2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range testCases {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
buf := &snapshotAppender{}
|
||||
|
||||
idx, err := store.HTTPRouteSnapshot(stream.SubscribeRequest{Subject: tc.subject}, buf)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, index, idx)
|
||||
require.Len(t, buf.events, 1)
|
||||
require.ElementsMatch(t, tc.events, buf.events[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInlineCertificateSnapshot(t *testing.T) {
|
||||
const index uint64 = 123
|
||||
|
||||
crt1 := &structs.InlineCertificateConfigEntry{
|
||||
Kind: structs.InlineCertificate,
|
||||
Name: "inlinecert1",
|
||||
}
|
||||
crt2 := &structs.InlineCertificateConfigEntry{
|
||||
Kind: structs.InlineCertificate,
|
||||
Name: "inlinecert2",
|
||||
}
|
||||
|
||||
store := testStateStore(t)
|
||||
require.NoError(t, store.EnsureConfigEntry(index, crt1))
|
||||
require.NoError(t, store.EnsureConfigEntry(index, crt2))
|
||||
|
||||
testCases := map[string]struct {
|
||||
subject stream.Subject
|
||||
events []stream.Event
|
||||
}{
|
||||
"named entry": {
|
||||
subject: EventSubjectConfigEntry{Name: crt1.Name},
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicInlineCertificate,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: crt1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"wildcard": {
|
||||
subject: stream.SubjectWildcard,
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicInlineCertificate,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: crt1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Topic: EventTopicInlineCertificate,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: crt2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range testCases {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
buf := &snapshotAppender{}
|
||||
|
||||
idx, err := store.InlineCertificateSnapshot(stream.SubscribeRequest{Subject: tc.subject}, buf)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, index, idx)
|
||||
require.Len(t, buf.events, 1)
|
||||
require.ElementsMatch(t, tc.events, buf.events[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoundAPIGatewaySnapshot(t *testing.T) {
|
||||
const index uint64 = 123
|
||||
|
||||
gw1 := &structs.BoundAPIGatewayConfigEntry{
|
||||
Kind: structs.BoundAPIGateway,
|
||||
Name: "boundapigw1",
|
||||
}
|
||||
gw2 := &structs.BoundAPIGatewayConfigEntry{
|
||||
Kind: structs.BoundAPIGateway,
|
||||
Name: "boundapigw2",
|
||||
}
|
||||
|
||||
store := testStateStore(t)
|
||||
require.NoError(t, store.EnsureConfigEntry(index, gw1))
|
||||
require.NoError(t, store.EnsureConfigEntry(index, gw2))
|
||||
|
||||
testCases := map[string]struct {
|
||||
subject stream.Subject
|
||||
events []stream.Event
|
||||
}{
|
||||
"named entry": {
|
||||
subject: EventSubjectConfigEntry{Name: gw1.Name},
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicBoundAPIGateway,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: gw1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"wildcard": {
|
||||
subject: stream.SubjectWildcard,
|
||||
events: []stream.Event{
|
||||
{
|
||||
Topic: EventTopicBoundAPIGateway,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: gw1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Topic: EventTopicBoundAPIGateway,
|
||||
Index: index,
|
||||
Payload: EventPayloadConfigEntry{
|
||||
Op: pbsubscribe.ConfigEntryUpdate_Upsert,
|
||||
Value: gw2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for desc, tc := range testCases {
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
buf := &snapshotAppender{}
|
||||
|
||||
idx, err := store.BoundAPIGatewaySnapshot(stream.SubscribeRequest{Subject: tc.subject}, buf)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, index, idx)
|
||||
require.Len(t, buf.events, 1)
|
||||
require.ElementsMatch(t, tc.events, buf.events[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"github.com/hashicorp/consul/proto/pbsubscribe"
|
||||
)
|
||||
|
||||
// PBToStreamSubscribeRequest takes a protobuf subscribe request and enterprise
|
||||
// metadata to properly generate the matching stream subscribe request.
|
||||
func PBToStreamSubscribeRequest(req *pbsubscribe.SubscribeRequest, entMeta acl.EnterpriseMeta) (*stream.SubscribeRequest, error) {
|
||||
var subject stream.Subject
|
||||
|
||||
|
@ -17,7 +19,7 @@ func PBToStreamSubscribeRequest(req *pbsubscribe.SubscribeRequest, entMeta acl.E
|
|||
} else {
|
||||
named := req.GetNamedSubject()
|
||||
|
||||
// Support the (deprcated) top-level Key, Partition, Namespace, and PeerName fields.
|
||||
// Support the (deprecated) top-level Key, Partition, Namespace, and PeerName fields.
|
||||
if named == nil {
|
||||
named = &pbsubscribe.NamedSubject{
|
||||
Key: req.Key, // nolint:staticcheck // SA1019 intentional use of deprecated field
|
||||
|
@ -38,7 +40,10 @@ func PBToStreamSubscribeRequest(req *pbsubscribe.SubscribeRequest, entMeta acl.E
|
|||
EnterpriseMeta: entMeta,
|
||||
PeerName: named.PeerName,
|
||||
}
|
||||
case EventTopicMeshConfig, EventTopicServiceResolver, EventTopicIngressGateway, EventTopicServiceIntentions, EventTopicServiceDefaults:
|
||||
case EventTopicMeshConfig, EventTopicServiceResolver, EventTopicIngressGateway,
|
||||
EventTopicServiceIntentions, EventTopicServiceDefaults, EventTopicAPIGateway,
|
||||
EventTopicTCPRoute, EventTopicHTTPRoute, EventTopicInlineCertificate,
|
||||
EventTopicBoundAPIGateway:
|
||||
subject = EventSubjectConfigEntry{
|
||||
Name: named.Key,
|
||||
EnterpriseMeta: &entMeta,
|
||||
|
|
157
agent/consul/state/events_test.go
Normal file
157
agent/consul/state/events_test.go
Normal file
|
@ -0,0 +1,157 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/consul/stream"
|
||||
"github.com/hashicorp/consul/proto/pbsubscribe"
|
||||
)
|
||||
|
||||
const aclToken = "67b04fbc-e35f-494a-ad43-739f1c8b839c"
|
||||
|
||||
func TestPBToStreamSubscribeRequest(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
req *pbsubscribe.SubscribeRequest
|
||||
entMeta acl.EnterpriseMeta
|
||||
expectedSubscribeRequest *stream.SubscribeRequest
|
||||
err error
|
||||
}{
|
||||
"Wildcard subject": {
|
||||
req: &pbsubscribe.SubscribeRequest{
|
||||
Topic: EventTopicServiceList,
|
||||
Subject: &pbsubscribe.SubscribeRequest_WildcardSubject{WildcardSubject: true},
|
||||
Token: aclToken,
|
||||
Index: 1,
|
||||
},
|
||||
entMeta: acl.EnterpriseMeta{},
|
||||
expectedSubscribeRequest: &stream.SubscribeRequest{
|
||||
Topic: EventTopicServiceList,
|
||||
Subject: stream.SubjectWildcard,
|
||||
Token: aclToken,
|
||||
Index: 1,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
"Deprecated top level fields": {
|
||||
req: &pbsubscribe.SubscribeRequest{
|
||||
Topic: EventTopicServiceDefaults,
|
||||
Key: "key",
|
||||
Partition: "partition",
|
||||
Namespace: "consul",
|
||||
PeerName: "peer",
|
||||
},
|
||||
entMeta: acl.EnterpriseMeta{},
|
||||
expectedSubscribeRequest: &stream.SubscribeRequest{
|
||||
Topic: EventTopicServiceDefaults,
|
||||
Subject: EventSubjectConfigEntry{
|
||||
Name: "key",
|
||||
EnterpriseMeta: &acl.EnterpriseMeta{},
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
"Service health": {
|
||||
req: &pbsubscribe.SubscribeRequest{
|
||||
Topic: EventTopicServiceHealth,
|
||||
Subject: &pbsubscribe.SubscribeRequest_NamedSubject{
|
||||
NamedSubject: &pbsubscribe.NamedSubject{
|
||||
Key: "key",
|
||||
Namespace: "consul",
|
||||
Partition: "partition",
|
||||
PeerName: "peer",
|
||||
},
|
||||
},
|
||||
Token: aclToken,
|
||||
Index: 2,
|
||||
},
|
||||
entMeta: acl.EnterpriseMeta{},
|
||||
expectedSubscribeRequest: &stream.SubscribeRequest{
|
||||
Topic: EventTopicServiceHealth,
|
||||
Subject: EventSubjectService{
|
||||
Key: "key",
|
||||
EnterpriseMeta: acl.EnterpriseMeta{},
|
||||
PeerName: "peer",
|
||||
},
|
||||
Token: aclToken,
|
||||
Index: 2,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
"Config": {
|
||||
req: &pbsubscribe.SubscribeRequest{
|
||||
Topic: EventTopicAPIGateway,
|
||||
Subject: &pbsubscribe.SubscribeRequest_NamedSubject{
|
||||
NamedSubject: &pbsubscribe.NamedSubject{
|
||||
Key: "key",
|
||||
Namespace: "consul",
|
||||
Partition: "partition",
|
||||
PeerName: "peer",
|
||||
},
|
||||
},
|
||||
Token: aclToken,
|
||||
Index: 2,
|
||||
},
|
||||
entMeta: acl.EnterpriseMeta{},
|
||||
expectedSubscribeRequest: &stream.SubscribeRequest{
|
||||
Topic: EventTopicAPIGateway,
|
||||
Subject: EventSubjectConfigEntry{
|
||||
Name: "key",
|
||||
EnterpriseMeta: &acl.EnterpriseMeta{},
|
||||
},
|
||||
Token: aclToken,
|
||||
Index: 2,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
"Service list without wildcard returns error": {
|
||||
req: &pbsubscribe.SubscribeRequest{
|
||||
Topic: EventTopicServiceList,
|
||||
Subject: &pbsubscribe.SubscribeRequest_NamedSubject{
|
||||
NamedSubject: &pbsubscribe.NamedSubject{
|
||||
Key: "key",
|
||||
Namespace: "consul",
|
||||
Partition: "partition",
|
||||
PeerName: "peer",
|
||||
},
|
||||
},
|
||||
},
|
||||
entMeta: acl.EnterpriseMeta{},
|
||||
expectedSubscribeRequest: nil,
|
||||
err: fmt.Errorf("topic %s can only be consumed using WildcardSubject", EventTopicServiceList),
|
||||
},
|
||||
"Unrecognized topic returns error": {
|
||||
req: &pbsubscribe.SubscribeRequest{
|
||||
Topic: 99999,
|
||||
Subject: &pbsubscribe.SubscribeRequest_NamedSubject{
|
||||
NamedSubject: &pbsubscribe.NamedSubject{
|
||||
Key: "key",
|
||||
Namespace: "consul",
|
||||
Partition: "partition",
|
||||
PeerName: "peer",
|
||||
},
|
||||
},
|
||||
},
|
||||
entMeta: acl.EnterpriseMeta{},
|
||||
expectedSubscribeRequest: nil,
|
||||
err: fmt.Errorf("cannot construct subject for topic 99999"),
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
actual, err := PBToStreamSubscribeRequest(tc.req, tc.entMeta)
|
||||
|
||||
if tc.err != nil {
|
||||
require.EqualError(t, err, tc.err.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.Equal(t, tc.expectedSubscribeRequest, actual)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -186,6 +186,11 @@ var (
|
|||
EventTopicServiceIntentions = pbsubscribe.Topic_ServiceIntentions
|
||||
EventTopicServiceDefaults = pbsubscribe.Topic_ServiceDefaults
|
||||
EventTopicServiceList = pbsubscribe.Topic_ServiceList
|
||||
EventTopicAPIGateway = pbsubscribe.Topic_APIGateway
|
||||
EventTopicTCPRoute = pbsubscribe.Topic_TCPRoute
|
||||
EventTopicHTTPRoute = pbsubscribe.Topic_HTTPRoute
|
||||
EventTopicInlineCertificate = pbsubscribe.Topic_InlineCertificate
|
||||
EventTopicBoundAPIGateway = pbsubscribe.Topic_BoundAPIGateway
|
||||
)
|
||||
|
||||
func processDBChanges(tx ReadTxn, changes Changes) ([]stream.Event, error) {
|
||||
|
|
|
@ -350,6 +350,86 @@ var baseCases = map[string]testCase{
|
|||
{Name: "kind", Value: "exported-services"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=api-gateway": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "api-gateway"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=api-gateway": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "api-gateway"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=bound-api-gateway": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "bound-api-gateway"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=bound-api-gateway": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "bound-api-gateway"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=inline-certificate": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "inline-certificate"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "inline-certificate"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=http-route": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "http-route"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=http-route": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "http-route"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=tcp-route": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "tcp-route"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=tcp-route": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "tcp-route"},
|
||||
},
|
||||
},
|
||||
},
|
||||
getMembersFunc: func() []serf.Member { return []serf.Member{} },
|
||||
},
|
||||
|
@ -690,6 +770,86 @@ var baseCases = map[string]testCase{
|
|||
{Name: "kind", Value: "exported-services"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=api-gateway": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "api-gateway"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=api-gateway": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "api-gateway"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=bound-api-gateway": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "bound-api-gateway"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=bound-api-gateway": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "bound-api-gateway"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=inline-certificate": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "inline-certificate"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "inline-certificate"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=http-route": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "http-route"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=http-route": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "http-route"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=tcp-route": { // Legacy
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "tcp-route"},
|
||||
},
|
||||
},
|
||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=tcp-route": {
|
||||
Name: "consul.usage.test.state.config_entries",
|
||||
Value: 0,
|
||||
Labels: []metrics.Label{
|
||||
{Name: "datacenter", Value: "dc1"},
|
||||
{Name: "kind", Value: "tcp-route"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -62,6 +62,16 @@ func newConfigEntryRequest(req *structs.ConfigEntryQuery, deps ServerDataSourceD
|
|||
topic = pbsubscribe.Topic_IngressGateway
|
||||
case structs.ServiceDefaults:
|
||||
topic = pbsubscribe.Topic_ServiceDefaults
|
||||
case structs.APIGateway:
|
||||
topic = pbsubscribe.Topic_APIGateway
|
||||
case structs.HTTPRoute:
|
||||
topic = pbsubscribe.Topic_HTTPRoute
|
||||
case structs.TCPRoute:
|
||||
topic = pbsubscribe.Topic_TCPRoute
|
||||
case structs.InlineCertificate:
|
||||
topic = pbsubscribe.Topic_InlineCertificate
|
||||
case structs.BoundAPIGateway:
|
||||
topic = pbsubscribe.Topic_BoundAPIGateway
|
||||
default:
|
||||
return nil, fmt.Errorf("cannot map config entry kind: %s to a topic", req.Kind)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,11 @@ const (
|
|||
ServiceIntentions string = "service-intentions"
|
||||
MeshConfig string = "mesh"
|
||||
ExportedServices string = "exported-services"
|
||||
APIGateway string = "api-gateway"
|
||||
BoundAPIGateway string = "bound-api-gateway"
|
||||
InlineCertificate string = "inline-certificate"
|
||||
HTTPRoute string = "http-route"
|
||||
TCPRoute string = "tcp-route"
|
||||
|
||||
ProxyConfigGlobal string = "global"
|
||||
MeshConfigMesh string = "mesh"
|
||||
|
@ -54,6 +59,11 @@ var AllConfigEntryKinds = []string{
|
|||
ServiceIntentions,
|
||||
MeshConfig,
|
||||
ExportedServices,
|
||||
APIGateway,
|
||||
BoundAPIGateway,
|
||||
HTTPRoute,
|
||||
TCPRoute,
|
||||
InlineCertificate,
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -698,6 +708,16 @@ func MakeConfigEntry(kind, name string) (ConfigEntry, error) {
|
|||
return &MeshConfigEntry{}, nil
|
||||
case ExportedServices:
|
||||
return &ExportedServicesConfigEntry{Name: name}, nil
|
||||
case APIGateway:
|
||||
return &APIGatewayConfigEntry{Name: name}, nil
|
||||
case BoundAPIGateway:
|
||||
return &BoundAPIGatewayConfigEntry{Name: name}, nil
|
||||
case InlineCertificate:
|
||||
return &InlineCertificateConfigEntry{Name: name}, nil
|
||||
case HTTPRoute:
|
||||
return &HTTPRouteConfigEntry{Name: name}, nil
|
||||
case TCPRoute:
|
||||
return &TCPRouteConfigEntry{Name: name}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
||||
}
|
||||
|
|
|
@ -409,6 +409,636 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api-gateway",
|
||||
entry: &APIGatewayConfigEntry{Name: "test"},
|
||||
expectACLs: []testACL{
|
||||
{
|
||||
name: "no-authz",
|
||||
authorizer: newAuthz(t, ``),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "deny"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "deny"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
|
||||
canRead: false,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service read and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service write and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "read"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "write"),
|
||||
canRead: false,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "inline-certificate",
|
||||
entry: &InlineCertificateConfigEntry{Name: "test", Certificate: validCertificate, PrivateKey: validPrivateKey},
|
||||
expectACLs: []testACL{
|
||||
{
|
||||
name: "no-authz",
|
||||
authorizer: newAuthz(t, ``),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service read and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service write and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "http-route",
|
||||
entry: &HTTPRouteConfigEntry{Name: "test"},
|
||||
expectACLs: []testACL{
|
||||
{
|
||||
name: "no-authz",
|
||||
authorizer: newAuthz(t, ``),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service read and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service write and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tcp-route",
|
||||
entry: &TCPRouteConfigEntry{Name: "test"},
|
||||
expectACLs: []testACL{
|
||||
{
|
||||
name: "no-authz",
|
||||
authorizer: newAuthz(t, ``),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service read and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service write and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bound-api-gateway",
|
||||
entry: &BoundAPIGatewayConfigEntry{Name: "test"},
|
||||
expectACLs: []testACL{
|
||||
{
|
||||
name: "no-authz",
|
||||
authorizer: newAuthz(t, ``),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator deny",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "deny"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh deny",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "deny"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator read",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and operator write",
|
||||
authorizer: newServiceAndOperatorACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "read"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh read",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "read"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "service deny and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "deny", "write"),
|
||||
canRead: false,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service read and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "read", "write"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
{
|
||||
name: "service write and mesh write",
|
||||
authorizer: newServiceAndMeshACL(t, "write", "write"),
|
||||
canRead: true,
|
||||
canWrite: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "terminating-gateway",
|
||||
entry: &TerminatingGatewayConfigEntry{Name: "test"},
|
||||
|
|
|
@ -12,6 +12,10 @@ import (
|
|||
"github.com/hashicorp/consul/types"
|
||||
)
|
||||
|
||||
const (
|
||||
wildcardPrefix = "*."
|
||||
)
|
||||
|
||||
// IngressGatewayConfigEntry manages the configuration for an ingress service
|
||||
// with the given name.
|
||||
type IngressGatewayConfigEntry struct {
|
||||
|
@ -371,7 +375,6 @@ func validateHost(tlsEnabled bool, host string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
wildcardPrefix := "*."
|
||||
if _, ok := dns.IsDomainName(host); !ok {
|
||||
return fmt.Errorf("Host %q must be a valid DNS hostname", host)
|
||||
}
|
||||
|
@ -687,3 +690,300 @@ func (g *GatewayService) Clone() *GatewayService {
|
|||
ServiceKind: g.ServiceKind,
|
||||
}
|
||||
}
|
||||
|
||||
// APIGatewayConfigEntry manages the configuration for an API gateway service
|
||||
// with the given name.
|
||||
type APIGatewayConfigEntry struct {
|
||||
// Kind of the config entry. This will be set to structs.APIGateway.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated API gateway
|
||||
// service. This should match the name provided in the service definition.
|
||||
Name string
|
||||
|
||||
// Listeners is the set of listener configuration to which an API Gateway
|
||||
// might bind.
|
||||
Listeners []APIGatewayListener
|
||||
// Status is the asynchronous status which an APIGateway propagates to the user.
|
||||
Status Status
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
RaftIndex
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetKind() string {
|
||||
return APIGateway
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) Normalize() error {
|
||||
for i, listener := range e.Listeners {
|
||||
protocol := strings.ToLower(string(listener.Protocol))
|
||||
listener.Protocol = APIGatewayListenerProtocol(protocol)
|
||||
e.Listeners[i] = listener
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) Validate() error {
|
||||
if err := e.validateListenerNames(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := e.validateMergedListeners(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.validateListeners()
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) validateListenerNames() error {
|
||||
listeners := make(map[string]struct{})
|
||||
for _, listener := range e.Listeners {
|
||||
if _, found := listeners[listener.Name]; found {
|
||||
return fmt.Errorf("found multiple listeners with the name %q", listener.Name)
|
||||
}
|
||||
listeners[listener.Name] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) validateMergedListeners() error {
|
||||
listeners := make(map[int]APIGatewayListener)
|
||||
for _, listener := range e.Listeners {
|
||||
merged, found := listeners[listener.Port]
|
||||
if found && (merged.Hostname != listener.Hostname || merged.Protocol != listener.Protocol) {
|
||||
return fmt.Errorf("listeners %q and %q cannot be merged", merged.Name, listener.Name)
|
||||
}
|
||||
listeners[listener.Port] = listener
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) validateListeners() error {
|
||||
validProtocols := map[APIGatewayListenerProtocol]bool{
|
||||
ListenerProtocolHTTP: true,
|
||||
ListenerProtocolTCP: true,
|
||||
}
|
||||
allowedCertificateKinds := map[string]bool{
|
||||
InlineCertificate: true,
|
||||
}
|
||||
|
||||
for _, listener := range e.Listeners {
|
||||
if !validProtocols[listener.Protocol] {
|
||||
return fmt.Errorf("unsupported listener protocol %q, must be one of 'tcp', or 'http'", listener.Protocol)
|
||||
}
|
||||
if listener.Protocol == ListenerProtocolTCP && listener.Hostname != "" {
|
||||
// TODO: once we have SNI matching we should be able to implement this
|
||||
return fmt.Errorf("hostname specification is not supported when using TCP")
|
||||
}
|
||||
if listener.Port <= 0 || listener.Port > 65535 {
|
||||
return fmt.Errorf("listener port %d not in the range 1-65535", listener.Port)
|
||||
}
|
||||
if strings.ContainsRune(strings.TrimPrefix(listener.Hostname, wildcardPrefix), '*') {
|
||||
return fmt.Errorf("host %q is not valid, a wildcard specifier is only allowed as the left-most label", listener.Hostname)
|
||||
}
|
||||
for _, certificate := range listener.TLS.Certificates {
|
||||
if !allowedCertificateKinds[certificate.Kind] {
|
||||
return fmt.Errorf("unsupported certificate kind: %q, must be 'inline-certificate'", certificate.Kind)
|
||||
}
|
||||
if certificate.Name == "" {
|
||||
return fmt.Errorf("certificate reference must have a name")
|
||||
}
|
||||
}
|
||||
if err := validateTLSConfig(listener.TLS.MinVersion, listener.TLS.MaxVersion, listener.TLS.CipherSuites); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) CanRead(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.ToAllowAuthorizer().ServiceReadAllowed(e.Name, &authzContext)
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) CanWrite(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
return &e.RaftIndex
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
// APIGatewayListenerProtocol is the protocol that an APIGateway listener uses
|
||||
type APIGatewayListenerProtocol string
|
||||
|
||||
const (
|
||||
ListenerProtocolHTTP APIGatewayListenerProtocol = "http"
|
||||
ListenerProtocolTCP APIGatewayListenerProtocol = "tcp"
|
||||
)
|
||||
|
||||
// APIGatewayListener represents an individual listener for an APIGateway
|
||||
type APIGatewayListener struct {
|
||||
// Name is the optional name of the listener in a given gateway. This is
|
||||
// optional, however, it must be unique. Therefore, if a gateway has more
|
||||
// than a single listener, all but one must specify a Name.
|
||||
Name string
|
||||
// Hostname is the host name that a listener should be bound to, if
|
||||
// unspecified, the listener accepts requests for all hostnames.
|
||||
Hostname string
|
||||
// Port is the port at which this listener should bind.
|
||||
Port int
|
||||
// Protocol is the protocol that a listener should use, it must
|
||||
// either be http or tcp
|
||||
Protocol APIGatewayListenerProtocol
|
||||
// TLS is the TLS settings for the listener.
|
||||
TLS APIGatewayTLSConfiguration
|
||||
}
|
||||
|
||||
// APIGatewayTLSConfiguration specifies the configuration of a listener’s
|
||||
// TLS settings.
|
||||
type APIGatewayTLSConfiguration struct {
|
||||
// Certificates is a set of references to certificates
|
||||
// that a gateway listener uses for TLS termination.
|
||||
Certificates []ResourceReference
|
||||
// MaxVersion is the maximum TLS version that the listener
|
||||
// should support.
|
||||
MaxVersion types.TLSVersion
|
||||
// MinVersion is the minimum TLS version that the listener
|
||||
// should support.
|
||||
MinVersion types.TLSVersion
|
||||
// CipherSuites is the cipher suites that the listener should support.
|
||||
CipherSuites []types.TLSCipherSuite
|
||||
}
|
||||
|
||||
// BoundAPIGatewayConfigEntry manages the configuration for a bound API
|
||||
// gateway with the given name. This type is never written from the client.
|
||||
// It is only written by the controller in order to represent an API gateway
|
||||
// and the resources that are bound to it.
|
||||
type BoundAPIGatewayConfigEntry struct {
|
||||
// Kind of the config entry. This will be set to structs.BoundAPIGateway.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated API gateway
|
||||
// service. This should match the name provided in the corresponding API
|
||||
// gateway service definition.
|
||||
Name string
|
||||
|
||||
// Listeners are the valid listeners of an APIGateway with information about
|
||||
// what certificates and routes have successfully bound to it.
|
||||
Listeners []BoundAPIGatewayListener
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
RaftIndex
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) GetKind() string {
|
||||
return BoundAPIGateway
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) Normalize() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) Validate() error {
|
||||
allowedCertificateKinds := map[string]bool{
|
||||
InlineCertificate: true,
|
||||
}
|
||||
allowedRouteKinds := map[string]bool{
|
||||
HTTPRoute: true,
|
||||
TCPRoute: true,
|
||||
}
|
||||
|
||||
// These should already be validated by upstream validation
|
||||
// logic in the gateways/routes, but just in case we validate
|
||||
// here as well.
|
||||
for _, listener := range e.Listeners {
|
||||
for _, certificate := range listener.Certificates {
|
||||
if !allowedCertificateKinds[certificate.Kind] {
|
||||
return fmt.Errorf("unsupported certificate kind: %q, must be 'inline-certificate'", certificate.Kind)
|
||||
}
|
||||
if certificate.Name == "" {
|
||||
return fmt.Errorf("certificate reference must have a name")
|
||||
}
|
||||
}
|
||||
for _, route := range listener.Routes {
|
||||
if !allowedRouteKinds[route.Kind] {
|
||||
return fmt.Errorf("unsupported route kind: %q, must be one of 'http-route', or 'tcp-route'", route.Kind)
|
||||
}
|
||||
if route.Name == "" {
|
||||
return fmt.Errorf("route reference must have a name")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) CanRead(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.ToAllowAuthorizer().ServiceReadAllowed(e.Name, &authzContext)
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) CanWrite(_ acl.Authorizer) error {
|
||||
return acl.PermissionDenied("only writeable by controller")
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
return &e.RaftIndex
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
// BoundAPIGatewayListener is an API gateway listener with information
|
||||
// about the routes and certificates that have successfully bound to it.
|
||||
type BoundAPIGatewayListener struct {
|
||||
Name string
|
||||
Routes []ResourceReference
|
||||
Certificates []ResourceReference
|
||||
}
|
||||
|
|
|
@ -1123,3 +1123,221 @@ func TestGatewayService_Addresses(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIGateway_Listeners(t *testing.T) {
|
||||
cases := map[string]configEntryTestcase{
|
||||
"listener name conflict": {
|
||||
entry: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "api-gw-one",
|
||||
Listeners: []APIGatewayListener{
|
||||
{
|
||||
Port: 80,
|
||||
Name: "foo",
|
||||
},
|
||||
{
|
||||
Port: 80,
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "multiple listeners with the name",
|
||||
},
|
||||
"merged listener protocol conflict": {
|
||||
entry: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "api-gw-two",
|
||||
Listeners: []APIGatewayListener{
|
||||
{
|
||||
Port: 80,
|
||||
Protocol: ListenerProtocolHTTP,
|
||||
},
|
||||
{
|
||||
Name: "foo",
|
||||
Port: 80,
|
||||
Protocol: ListenerProtocolTCP,
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "cannot be merged",
|
||||
},
|
||||
"merged listener hostname conflict": {
|
||||
entry: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "api-gw-three",
|
||||
Listeners: []APIGatewayListener{
|
||||
{
|
||||
Port: 80,
|
||||
Hostname: "host.one",
|
||||
},
|
||||
{
|
||||
Name: "foo",
|
||||
Port: 80,
|
||||
Hostname: "host.two",
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "cannot be merged",
|
||||
},
|
||||
"invalid protocol": {
|
||||
entry: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "api-gw-four",
|
||||
Listeners: []APIGatewayListener{
|
||||
{
|
||||
Port: 80,
|
||||
Hostname: "host.one",
|
||||
Protocol: APIGatewayListenerProtocol("UDP"),
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "unsupported listener protocol",
|
||||
},
|
||||
"hostname in unsupported protocol": {
|
||||
entry: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "api-gw-five",
|
||||
Listeners: []APIGatewayListener{
|
||||
{
|
||||
Port: 80,
|
||||
Hostname: "host.one",
|
||||
Protocol: APIGatewayListenerProtocol("tcp"),
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "hostname specification is not supported",
|
||||
},
|
||||
"invalid port": {
|
||||
entry: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "api-gw-six",
|
||||
Listeners: []APIGatewayListener{
|
||||
{
|
||||
Port: -1,
|
||||
Protocol: APIGatewayListenerProtocol("tcp"),
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "not in the range 1-65535",
|
||||
},
|
||||
"invalid hostname": {
|
||||
entry: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "api-gw-seven",
|
||||
Listeners: []APIGatewayListener{
|
||||
{
|
||||
Port: 80,
|
||||
Hostname: "*.*.host.one",
|
||||
Protocol: APIGatewayListenerProtocol("http"),
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "only allowed as the left-most label",
|
||||
},
|
||||
"unsupported certificate kind": {
|
||||
entry: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "api-gw-eight",
|
||||
Listeners: []APIGatewayListener{
|
||||
{
|
||||
Port: 80,
|
||||
Hostname: "host.one",
|
||||
Protocol: APIGatewayListenerProtocol("http"),
|
||||
TLS: APIGatewayTLSConfiguration{
|
||||
Certificates: []ResourceReference{{
|
||||
Kind: APIGateway,
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "unsupported certificate kind",
|
||||
},
|
||||
"unnamed certificate": {
|
||||
entry: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "api-gw-nine",
|
||||
Listeners: []APIGatewayListener{
|
||||
{
|
||||
Port: 80,
|
||||
Hostname: "host.one",
|
||||
Protocol: APIGatewayListenerProtocol("http"),
|
||||
TLS: APIGatewayTLSConfiguration{
|
||||
Certificates: []ResourceReference{{
|
||||
Kind: InlineCertificate,
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "certificate reference must have a name",
|
||||
},
|
||||
}
|
||||
testConfigEntryNormalizeAndValidate(t, cases)
|
||||
}
|
||||
|
||||
func TestBoundAPIGateway(t *testing.T) {
|
||||
cases := map[string]configEntryTestcase{
|
||||
"invalid certificate, no name": {
|
||||
entry: &BoundAPIGatewayConfigEntry{
|
||||
Kind: BoundAPIGateway,
|
||||
Name: "bound-api-gw-one",
|
||||
Listeners: []BoundAPIGatewayListener{
|
||||
{
|
||||
Name: "one",
|
||||
Certificates: []ResourceReference{{
|
||||
Kind: InlineCertificate,
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "certificate reference must have a name",
|
||||
},
|
||||
"invalid certificate, no kind": {
|
||||
entry: &BoundAPIGatewayConfigEntry{
|
||||
Kind: BoundAPIGateway,
|
||||
Name: "bound-api-gw-two",
|
||||
Listeners: []BoundAPIGatewayListener{
|
||||
{
|
||||
Name: "one",
|
||||
Certificates: []ResourceReference{{
|
||||
Name: "foo",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "unsupported certificate kind",
|
||||
},
|
||||
"invalid route, no name": {
|
||||
entry: &BoundAPIGatewayConfigEntry{
|
||||
Kind: BoundAPIGateway,
|
||||
Name: "bound-api-gw-three",
|
||||
Listeners: []BoundAPIGatewayListener{
|
||||
{
|
||||
Name: "one",
|
||||
Routes: []ResourceReference{{
|
||||
Kind: TCPRoute,
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "route reference must have a name",
|
||||
},
|
||||
"invalid route, no kind": {
|
||||
entry: &BoundAPIGatewayConfigEntry{
|
||||
Kind: BoundAPIGateway,
|
||||
Name: "bound-api-gw-four",
|
||||
Listeners: []BoundAPIGatewayListener{
|
||||
{
|
||||
Name: "one",
|
||||
Routes: []ResourceReference{{
|
||||
Name: "foo",
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "unsupported route kind",
|
||||
},
|
||||
}
|
||||
testConfigEntryNormalizeAndValidate(t, cases)
|
||||
}
|
||||
|
|
101
agent/structs/config_entry_inline_certificate.go
Normal file
101
agent/structs/config_entry_inline_certificate.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
)
|
||||
|
||||
// InlineCertificateConfigEntry manages the configuration for an inline certificate
|
||||
// with the given name.
|
||||
type InlineCertificateConfigEntry struct {
|
||||
// Kind of config entry. This will be set to structs.InlineCertificate.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated inline certificate.
|
||||
Name string
|
||||
|
||||
// Certificate is the public certificate component of an x509 key pair encoded in raw PEM format.
|
||||
Certificate string
|
||||
// PrivateKey is the private key component of an x509 key pair encoded in raw PEM format.
|
||||
PrivateKey string
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
RaftIndex
|
||||
}
|
||||
|
||||
func (e *InlineCertificateConfigEntry) GetKind() string {
|
||||
return InlineCertificate
|
||||
}
|
||||
|
||||
func (e *InlineCertificateConfigEntry) GetName() string {
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *InlineCertificateConfigEntry) Normalize() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *InlineCertificateConfigEntry) Validate() error {
|
||||
privateKeyBlock, _ := pem.Decode([]byte(e.PrivateKey))
|
||||
if privateKeyBlock == nil {
|
||||
return errors.New("failed to parse private key PEM")
|
||||
}
|
||||
|
||||
certificateBlock, _ := pem.Decode([]byte(e.Certificate))
|
||||
if certificateBlock == nil {
|
||||
return errors.New("failed to parse certificate PEM")
|
||||
}
|
||||
|
||||
// make sure we have a valid x509 certificate
|
||||
_, err := x509.ParseCertificate(certificateBlock.Bytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse certificate: %w", err)
|
||||
}
|
||||
|
||||
// validate that the cert was generated with the given private key
|
||||
_, err = tls.X509KeyPair([]byte(e.Certificate), []byte(e.PrivateKey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *InlineCertificateConfigEntry) CanRead(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.ToAllowAuthorizer().MeshReadAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *InlineCertificateConfigEntry) CanWrite(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *InlineCertificateConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *InlineCertificateConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
func (e *InlineCertificateConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
return &e.RaftIndex
|
||||
}
|
106
agent/structs/config_entry_inline_certificate_test.go
Normal file
106
agent/structs/config_entry_inline_certificate_test.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package structs
|
||||
|
||||
import "testing"
|
||||
|
||||
const (
|
||||
// generated via openssl req -x509 -sha256 -days 1825 -newkey rsa:2048 -keyout private.key -out certificate.crt
|
||||
validPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAx95Opa6t4lGEpiTUogEBptqOdam2ch4BHQGhNhX/MrDwwuZQ
|
||||
httBwMfngQ/wd9NmYEPAwj0dumUoAITIq6i2jQlhqTodElkbsd5vWY8R/bxJWQSo
|
||||
NvVE12TlzECxGpJEiHt4W0r8pGffk+rvpljiUyCfnT1kGF3znOSjK1hRMTn6RKWC
|
||||
yYaBvXQiB4SGilfLgJcEpOJKtISIxmZ+S409g9X5VU88/Bmmrz4cMyxce86Kc2ug
|
||||
5/MOv0CjWDJwlrv8njneV2zvraQ61DDwQftrXOvuCbO5IBRHMOBHiHTZ4rtGuhMa
|
||||
Ir21V4vb6n8c4YzXiFvhUYcyX7rltGZzVd+WmQIDAQABAoIBACYvceUzp2MK4gYA
|
||||
GWPOP2uKbBdM0l+hHeNV0WAM+dHMfmMuL4pkT36ucqt0ySOLjw6rQyOZG5nmA6t9
|
||||
sv0g4ae2eCMlyDIeNi1Yavu4Wt6YX4cTXbQKThm83C6W2X9THKbauBbxD621bsDK
|
||||
7PhiGPN60yPue7YwFQAPqqD4YaK+s22HFIzk9gwM/rkvAUNwRv7SyHMiFe4Igc1C
|
||||
Eev7iHWzvj5Heoz6XfF+XNF9DU+TieSUAdjd56VyUb8XL4+uBTOhHwLiXvAmfaMR
|
||||
HvpcxeKnYZusS6NaOxcUHiJnsLNWrxmJj9WEGgQzuLxcLjTe4vVmELVZD8t3QUKj
|
||||
PAxu8tUCgYEA7KIWVn9dfVpokReorFym+J8FzLwSktP9RZYEMonJo00i8aii3K9s
|
||||
u/aSwRWQSCzmON1ZcxZzWhwQF9usz6kGCk//9+4hlVW90GtNK0RD+j7sp4aT2JI8
|
||||
9eLEjTG+xSXa7XWe98QncjjL9lu/yrRncSTxHs13q/XP198nn2aYuQ8CgYEA2Dnt
|
||||
sRBzv0fFEvzzFv7G/5f85mouN38TUYvxNRTjBLCXl9DeKjDkOVZ2b6qlfQnYXIru
|
||||
H+W+v+AZEb6fySXc8FRab7lkgTMrwE+aeI4rkW7asVwtclv01QJ5wMnyT84AgDD/
|
||||
Dgt/RThFaHgtU9TW5GOZveL+l9fVPn7vKFdTJdcCgYEArJ99zjHxwJ1whNAOk1av
|
||||
09UmRPm6TvRo4heTDk8oEoIWCNatoHI0z1YMLuENNSnT9Q280FFDayvnrY/qnD7A
|
||||
kktT/sjwJOG8q8trKzIMqQS4XWm2dxoPcIyyOBJfCbEY6XuRsUuePxwh5qF942EB
|
||||
yS9a2s6nC4Ix0lgPrqAIr48CgYBgS/Q6riwOXSU8nqCYdiEkBYlhCJrKpnJxF9T1
|
||||
ofa0yPzKZP/8ZEfP7VzTwHjxJehQ1qLUW9pG08P2biH1UEKEWdzo8vT6wVJT1F/k
|
||||
HtTycR8+a+Hlk2SHVRHqNUYQGpuIe8mrdJ1as4Pd0d/F/P0zO9Rlh+mAsGPM8HUM
|
||||
T0+9gwKBgHDoerX7NTskg0H0t8O+iSMevdxpEWp34ZYa9gHiftTQGyrRgERCa7Gj
|
||||
nZPAxKb2JoWyfnu3v7G5gZ8fhDFsiOxLbZv6UZJBbUIh1MjJISpXrForDrC2QNLX
|
||||
kHrHfwBFDB3KMudhQknsJzEJKCL/KmFH6o0MvsoaT9yzEl3K+ah/
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
validCertificate = `-----BEGIN CERTIFICATE-----
|
||||
MIICljCCAX4CCQCQMDsYO8FrPjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJV
|
||||
UzAeFw0yMjEyMjAxNzUwMjVaFw0yNzEyMTkxNzUwMjVaMA0xCzAJBgNVBAYTAlVT
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx95Opa6t4lGEpiTUogEB
|
||||
ptqOdam2ch4BHQGhNhX/MrDwwuZQhttBwMfngQ/wd9NmYEPAwj0dumUoAITIq6i2
|
||||
jQlhqTodElkbsd5vWY8R/bxJWQSoNvVE12TlzECxGpJEiHt4W0r8pGffk+rvplji
|
||||
UyCfnT1kGF3znOSjK1hRMTn6RKWCyYaBvXQiB4SGilfLgJcEpOJKtISIxmZ+S409
|
||||
g9X5VU88/Bmmrz4cMyxce86Kc2ug5/MOv0CjWDJwlrv8njneV2zvraQ61DDwQftr
|
||||
XOvuCbO5IBRHMOBHiHTZ4rtGuhMaIr21V4vb6n8c4YzXiFvhUYcyX7rltGZzVd+W
|
||||
mQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfCqoUIdPf/HGSbOorPyZWbyizNtHJ
|
||||
GL7x9cAeIYxpI5Y/WcO1o5v94lvrgm3FNfJoGKbV66+JxOge731FrfMpHplhar1Z
|
||||
RahYIzNLRBTLrwadLAZkApUpZvB8qDK4knsTWFYujNsylCww2A6ajzIMFNU4GkUK
|
||||
NtyHRuD+KYRmjXtyX1yHNqfGN3vOQmwavHq2R8wHYuBSc6LAHHV9vG+j0VsgMELO
|
||||
qwxn8SmLkSKbf2+MsQVzLCXXN5u+D8Yv+4py+oKP4EQ5aFZuDEx+r/G/31rTthww
|
||||
AAJAMaoXmoYVdgXV+CPuBb2M4XCpuzLu3bcA2PXm5ipSyIgntMKwXV7r
|
||||
-----END CERTIFICATE-----`
|
||||
mismatchedCertificate = `-----BEGIN CERTIFICATE-----
|
||||
MIICljCCAX4CCQC49bq8e0QgLDANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJV
|
||||
UzAeFw0yMjEyMjAxNzUyMzJaFw0yNzEyMTkxNzUyMzJaMA0xCzAJBgNVBAYTAlVT
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk7Are9ulVDY0IqaG5Pt/
|
||||
OVuS0kmDhgVUfQBM5JDGRfIsu1ebn68kn5JGCTQ+nC8nU9QXRJS7vG6As5GWm08W
|
||||
FpkOyIbHLjOhWtYCYzQ+0R+sSSoMnczgl8l6wIUIkR3Vpoy6QUsSZbvo4/xDi3Uk
|
||||
1CF+JMTM2oFDLD8PNrNzW/txRyTugK36W1G1ofUhvP6EHsTjmVcZwBcLOKToov6L
|
||||
Ai758MLztl1/X/90DNdZwuHC9fGIgx52Ojz3+XIocXFttr+J8xZglMCtqL4n40bh
|
||||
5b1DE+hC3NHQmA+7Chc99z28baj2cU1woNk/TO+ewqpyvj+WPWwGOQt3U63ZoPaw
|
||||
yQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCMF3JlrDdcSv2KYrxEp1tWB/GglI8a
|
||||
JiSvrf3hePaRz59099bg4DoHzTn0ptOcOPOO9epDPbCJrUqLuPlwvrQRvll6GaW1
|
||||
y3TcbnE1AbwTAjbOTgpLhvuj6IVlyNNLoKbjZqs4A8N8i6UkQ7Y8qg77lwxD3QoH
|
||||
pWLwGZKJifKPa7ObVWmKj727kbU59nA2Hx+Y4qa/MyiPWxJM9Y0JsFGxSBxp4kmQ
|
||||
q4ikzSWaPv/TvtV+d4mO1H44aggdNMCYIQd/5BXQzG40l+ecHnBueJyG312ax/Zp
|
||||
NsYUAKQT864cGlxrnWVgT4sW/tsl9Qen7g9iAdeBAPvLO7cQjAjtc7KZ
|
||||
-----END CERTIFICATE-----`
|
||||
)
|
||||
|
||||
func TestInlineCertificate(t *testing.T) {
|
||||
cases := map[string]configEntryTestcase{
|
||||
"invalid private key": {
|
||||
entry: &InlineCertificateConfigEntry{
|
||||
Kind: InlineCertificate,
|
||||
Name: "cert-one",
|
||||
PrivateKey: "foo",
|
||||
},
|
||||
validateErr: "failed to parse private key PEM",
|
||||
},
|
||||
"invalid certificate": {
|
||||
entry: &InlineCertificateConfigEntry{
|
||||
Kind: InlineCertificate,
|
||||
Name: "cert-two",
|
||||
PrivateKey: validPrivateKey,
|
||||
Certificate: "foo",
|
||||
},
|
||||
validateErr: "failed to parse certificate PEM",
|
||||
},
|
||||
"mismatched certificate": {
|
||||
entry: &InlineCertificateConfigEntry{
|
||||
Kind: InlineCertificate,
|
||||
Name: "cert-three",
|
||||
PrivateKey: validPrivateKey,
|
||||
Certificate: mismatchedCertificate,
|
||||
},
|
||||
validateErr: "private key does not match public key",
|
||||
},
|
||||
"matched certificate": {
|
||||
entry: &InlineCertificateConfigEntry{
|
||||
Kind: InlineCertificate,
|
||||
Name: "cert-four",
|
||||
PrivateKey: validPrivateKey,
|
||||
Certificate: validCertificate,
|
||||
},
|
||||
},
|
||||
}
|
||||
testConfigEntryNormalizeAndValidate(t, cases)
|
||||
}
|
177
agent/structs/config_entry_routes.go
Normal file
177
agent/structs/config_entry_routes.go
Normal file
|
@ -0,0 +1,177 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
)
|
||||
|
||||
// HTTPRouteConfigEntry manages the configuration for a HTTP route
|
||||
// with the given name.
|
||||
type HTTPRouteConfigEntry struct {
|
||||
// Kind of the config entry. This will be set to structs.HTTPRoute.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated set
|
||||
// of resources, which may include routers, splitters, filters, etc.
|
||||
Name string
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
RaftIndex
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetKind() string {
|
||||
return HTTPRoute
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) Normalize() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) CanRead(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.ToAllowAuthorizer().MeshReadAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) CanWrite(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
return &e.RaftIndex
|
||||
}
|
||||
|
||||
// TCPRouteConfigEntry manages the configuration for a TCP route
|
||||
// with the given name.
|
||||
type TCPRouteConfigEntry struct {
|
||||
// Kind of the config entry. This will be set to structs.TCPRoute.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated set
|
||||
// of resources.
|
||||
Name string
|
||||
|
||||
// Parents is a list of gateways that this route should be bound to
|
||||
Parents []ResourceReference
|
||||
// Services is a list of TCP-based services that this should route to.
|
||||
// Currently, this must specify at maximum one service.
|
||||
Services []TCPService
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
// Status is the asynchronous status which a TCPRoute propagates to the user.
|
||||
Status Status
|
||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
RaftIndex
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetKind() string {
|
||||
return TCPRoute
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) Normalize() error {
|
||||
for i, parent := range e.Parents {
|
||||
if parent.Kind == "" {
|
||||
parent.Kind = APIGateway
|
||||
e.Parents[i] = parent
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) Validate() error {
|
||||
validParentKinds := map[string]bool{
|
||||
APIGateway: true,
|
||||
}
|
||||
|
||||
if len(e.Services) > 1 {
|
||||
return fmt.Errorf("tcp-route currently only supports one service")
|
||||
}
|
||||
for _, parent := range e.Parents {
|
||||
if !validParentKinds[parent.Kind] {
|
||||
return fmt.Errorf("unsupported parent kind: %q, must be 'api-gateway'", parent.Kind)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) CanRead(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.ToAllowAuthorizer().MeshReadAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) CanWrite(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
return &e.RaftIndex
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
// TCPService is a service reference for a TCPRoute
|
||||
type TCPService struct {
|
||||
Name string
|
||||
// Weight specifies the proportion of requests forwarded to the referenced service.
|
||||
// This is computed as weight/(sum of all weights in the list of services).
|
||||
Weight int
|
||||
|
||||
acl.EnterpriseMeta
|
||||
}
|
58
agent/structs/config_entry_routes_test.go
Normal file
58
agent/structs/config_entry_routes_test.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTCPRoute(t *testing.T) {
|
||||
cases := map[string]configEntryTestcase{
|
||||
"multiple services": {
|
||||
entry: &TCPRouteConfigEntry{
|
||||
Kind: TCPRoute,
|
||||
Name: "route-one",
|
||||
Services: []TCPService{{
|
||||
Name: "foo",
|
||||
}, {
|
||||
Name: "bar",
|
||||
}},
|
||||
},
|
||||
validateErr: "tcp-route currently only supports one service",
|
||||
},
|
||||
"normalize parent kind": {
|
||||
entry: &TCPRouteConfigEntry{
|
||||
Kind: TCPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Services: []TCPService{{
|
||||
Name: "foo",
|
||||
}},
|
||||
},
|
||||
normalizeOnly: true,
|
||||
check: func(t *testing.T, entry ConfigEntry) {
|
||||
expectedParent := ResourceReference{
|
||||
Kind: APIGateway,
|
||||
Name: "gateway",
|
||||
}
|
||||
route := entry.(*TCPRouteConfigEntry)
|
||||
require.Len(t, route.Parents, 1)
|
||||
require.Equal(t, expectedParent, route.Parents[0])
|
||||
},
|
||||
},
|
||||
"invalid parent kind": {
|
||||
entry: &TCPRouteConfigEntry{
|
||||
Kind: TCPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Kind: "route",
|
||||
Name: "gateway",
|
||||
}},
|
||||
},
|
||||
validateErr: "unsupported parent kind",
|
||||
},
|
||||
}
|
||||
testConfigEntryNormalizeAndValidate(t, cases)
|
||||
}
|
51
agent/structs/config_entry_status.go
Normal file
51
agent/structs/config_entry_status.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
)
|
||||
|
||||
// ResourceReference is a reference to a ConfigEntry
|
||||
// with an optional reference to a subsection of that ConfigEntry
|
||||
// that can be specified as SectionName
|
||||
type ResourceReference struct {
|
||||
// Kind is the kind of ConfigEntry that this resource refers to.
|
||||
Kind string
|
||||
// Name is the identifier for the ConfigEntry this resource refers to.
|
||||
Name string
|
||||
// SectionName is a generic subresource identifier that specifies
|
||||
// a subset of the ConfigEntry to which this reference applies. Usage
|
||||
// of this field should be up to the controller that leverages it. If
|
||||
// unused, this should be blank.
|
||||
SectionName string
|
||||
|
||||
acl.EnterpriseMeta
|
||||
}
|
||||
|
||||
// Status is used for propagating back asynchronously calculated
|
||||
// messages from control loops to a user
|
||||
type Status struct {
|
||||
// Conditions is the set of condition objects associated with
|
||||
// a ConfigEntry status.
|
||||
Conditions []Condition
|
||||
}
|
||||
|
||||
// Condition is used for a single message and state associated
|
||||
// with an object. For example, a ConfigEntry that references
|
||||
// multiple other resources may have different statuses with
|
||||
// respect to each of those resources.
|
||||
type Condition struct {
|
||||
// Status is a value from a bounded set of statuses that an object might have
|
||||
Status string
|
||||
// Reason is a value from a bounded set of reasons for a given status
|
||||
Reason string
|
||||
// Message is a message that gives more detailed information about
|
||||
// why a Condition has a given status and reason
|
||||
Message string
|
||||
// Resource is an optional reference to a resource for which this
|
||||
// condition applies
|
||||
Resource *ResourceReference
|
||||
// LastTransitionTime is the time at which this Condition was created
|
||||
LastTransitionTime *time.Time
|
||||
}
|
|
@ -296,7 +296,7 @@ func TestDecodeConfigEntry_ServiceDefaults(t *testing.T) {
|
|||
}
|
||||
|
||||
// TestDecodeConfigEntry is the 'structs' mirror image of
|
||||
// command/config/write/config_write_test.go:TestParseConfigEntry
|
||||
// command/helpers/helpers_test.go:TestParseConfigEntry
|
||||
func TestDecodeConfigEntry(t *testing.T) {
|
||||
|
||||
for _, tc := range []struct {
|
||||
|
@ -1899,6 +1899,114 @@ func TestDecodeConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api-gateway",
|
||||
snake: `
|
||||
kind = "api-gateway"
|
||||
name = "foo"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "api-gateway"
|
||||
Name = "foo"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
expect: &APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "foo",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "inline-certificate",
|
||||
snake: `
|
||||
kind = "inline-certificate"
|
||||
name = "foo"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "inline-certificate"
|
||||
Name = "foo"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
expect: &InlineCertificateConfigEntry{
|
||||
Kind: "inline-certificate",
|
||||
Name: "foo",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "http-route",
|
||||
snake: `
|
||||
kind = "http-route"
|
||||
name = "foo"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "http-route"
|
||||
Name = "foo"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
expect: &HTTPRouteConfigEntry{
|
||||
Kind: "http-route",
|
||||
Name: "foo",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tcp-route",
|
||||
snake: `
|
||||
kind = "tcp-route"
|
||||
name = "foo"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "tcp-route"
|
||||
Name = "foo"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
expect: &TCPRouteConfigEntry{
|
||||
Kind: "tcp-route",
|
||||
Name: "foo",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exported-services",
|
||||
snake: `
|
||||
|
|
|
@ -26,6 +26,10 @@ const (
|
|||
|
||||
ProxyConfigGlobal string = "global"
|
||||
MeshConfigMesh string = "mesh"
|
||||
APIGateway string = "api-gateway"
|
||||
TCPRoute string = "tcp-route"
|
||||
InlineCertificate string = "inline-certificate"
|
||||
HTTPRoute string = "http-route"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -347,6 +351,14 @@ func makeConfigEntry(kind, name string) (ConfigEntry, error) {
|
|||
return &MeshConfigEntry{}, nil
|
||||
case ExportedServices:
|
||||
return &ExportedServicesConfigEntry{Name: name}, nil
|
||||
case APIGateway:
|
||||
return &APIGatewayConfigEntry{Kind: kind, Name: name}, nil
|
||||
case TCPRoute:
|
||||
return &TCPRouteConfigEntry{Kind: kind, Name: name}, nil
|
||||
case InlineCertificate:
|
||||
return &InlineCertificateConfigEntry{Kind: kind, Name: name}, nil
|
||||
case HTTPRoute:
|
||||
return &HTTPRouteConfigEntry{Kind: kind, Name: name}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid config entry kind: %s", kind)
|
||||
}
|
||||
|
|
|
@ -221,3 +221,82 @@ func (g *TerminatingGatewayConfigEntry) GetNamespace() string { return g.N
|
|||
func (g *TerminatingGatewayConfigEntry) GetMeta() map[string]string { return g.Meta }
|
||||
func (g *TerminatingGatewayConfigEntry) GetCreateIndex() uint64 { return g.CreateIndex }
|
||||
func (g *TerminatingGatewayConfigEntry) GetModifyIndex() uint64 { return g.ModifyIndex }
|
||||
|
||||
// APIGatewayConfigEntry manages the configuration for an API gateway
|
||||
// with the given name.
|
||||
type APIGatewayConfigEntry struct {
|
||||
// Kind of the config entry. This should be set to api.APIGateway.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated api gateway
|
||||
// service. This should match the name provided in the service definition.
|
||||
Name string
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
|
||||
// Listeners is the set of listener configuration to which an API Gateway
|
||||
// might bind.
|
||||
Listeners []APIGatewayListener
|
||||
// Status is the asynchronous status which an APIGateway propagates to the user.
|
||||
Status ConfigEntryStatus
|
||||
|
||||
// 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
|
||||
|
||||
// Partition is the partition the config entry is associated with.
|
||||
// Partitioning is a Consul Enterprise feature.
|
||||
Partition string `json:",omitempty"`
|
||||
|
||||
// Namespace is the namespace the config entry is associated with.
|
||||
// Namespacing is a Consul Enterprise feature.
|
||||
Namespace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (g *APIGatewayConfigEntry) GetKind() string { return g.Kind }
|
||||
func (g *APIGatewayConfigEntry) GetName() string { return g.Name }
|
||||
func (g *APIGatewayConfigEntry) GetPartition() string { return g.Partition }
|
||||
func (g *APIGatewayConfigEntry) GetNamespace() string { return g.Namespace }
|
||||
func (g *APIGatewayConfigEntry) GetMeta() map[string]string { return g.Meta }
|
||||
func (g *APIGatewayConfigEntry) GetCreateIndex() uint64 { return g.CreateIndex }
|
||||
func (g *APIGatewayConfigEntry) GetModifyIndex() uint64 { return g.ModifyIndex }
|
||||
|
||||
// APIGatewayListener represents an individual listener for an APIGateway
|
||||
type APIGatewayListener struct {
|
||||
// Name is the optional name of the listener in a given gateway. This is
|
||||
// optional, however, it must be unique. Therefore, if a gateway has more
|
||||
// than a single listener, all but one must specify a Name.
|
||||
Name string
|
||||
// Hostname is the host name that a listener should be bound to, if
|
||||
// unspecified, the listener accepts requests for all hostnames.
|
||||
Hostname string
|
||||
// Port is the port at which this listener should bind.
|
||||
Port int
|
||||
// Protocol is the protocol that a listener should use, it must
|
||||
// either be "http" or "tcp"
|
||||
Protocol string
|
||||
// TLS is the TLS settings for the listener.
|
||||
TLS APIGatewayTLSConfiguration
|
||||
}
|
||||
|
||||
// APIGatewayTLSConfiguration specifies the configuration of a listener’s
|
||||
// TLS settings.
|
||||
type APIGatewayTLSConfiguration struct {
|
||||
// Certificates is a set of references to certificates
|
||||
// that a gateway listener uses for TLS termination.
|
||||
Certificates []ResourceReference
|
||||
// MaxVersion is the maximum TLS version that the listener
|
||||
// should support.
|
||||
MaxVersion string `json:",omitempty" alias:"tls_max_version"`
|
||||
// MinVersion is the minimum TLS version that the listener
|
||||
// should support.
|
||||
MinVersion string `json:",omitempty" alias:"tls_min_version"`
|
||||
// Define a subset of cipher suites to restrict
|
||||
// Only applicable to connections negotiated via TLS 1.2 or earlier
|
||||
CipherSuites []string `json:",omitempty" alias:"cipher_suites"`
|
||||
}
|
||||
|
|
75
api/config_entry_inline_certificate.go
Normal file
75
api/config_entry_inline_certificate.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package api
|
||||
|
||||
// InlineCertificateConfigEntry -- TODO stub
|
||||
type InlineCertificateConfigEntry struct {
|
||||
// Kind of the config entry. This should be set to api.InlineCertificate.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated tcp-route
|
||||
// service. This should match the name provided in the service definition.
|
||||
Name string
|
||||
|
||||
// Certificate is the public certificate component of an x509 key pair encoded in raw PEM format.
|
||||
Certificate string
|
||||
// PrivateKey is the private key component of an x509 key pair encoded in raw PEM format.
|
||||
PrivateKey string
|
||||
|
||||
Meta map[string]string `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
|
||||
|
||||
// Partition is the partition the config entry is associated with.
|
||||
// Partitioning is a Consul Enterprise feature.
|
||||
Partition string `json:",omitempty"`
|
||||
|
||||
// Namespace is the namespace the config entry is associated with.
|
||||
// Namespacing is a Consul Enterprise feature.
|
||||
Namespace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (a *InlineCertificateConfigEntry) GetKind() string {
|
||||
return InlineCertificate
|
||||
}
|
||||
|
||||
func (a *InlineCertificateConfigEntry) GetName() string {
|
||||
if a != nil {
|
||||
return ""
|
||||
}
|
||||
return a.Name
|
||||
}
|
||||
|
||||
func (a *InlineCertificateConfigEntry) GetPartition() string {
|
||||
if a != nil {
|
||||
return ""
|
||||
}
|
||||
return a.Partition
|
||||
}
|
||||
|
||||
func (a *InlineCertificateConfigEntry) GetNamespace() string {
|
||||
if a != nil {
|
||||
return ""
|
||||
}
|
||||
return a.GetNamespace()
|
||||
}
|
||||
|
||||
func (a *InlineCertificateConfigEntry) GetMeta() map[string]string {
|
||||
if a != nil {
|
||||
return nil
|
||||
}
|
||||
return a.GetMeta()
|
||||
}
|
||||
|
||||
func (a *InlineCertificateConfigEntry) GetCreateIndex() uint64 {
|
||||
return a.CreateIndex
|
||||
}
|
||||
|
||||
func (a *InlineCertificateConfigEntry) GetModifyIndex() uint64 {
|
||||
return a.ModifyIndex
|
||||
}
|
132
api/config_entry_routes.go
Normal file
132
api/config_entry_routes.go
Normal file
|
@ -0,0 +1,132 @@
|
|||
package api
|
||||
|
||||
// TCPRouteConfigEntry -- TODO stub
|
||||
type TCPRouteConfigEntry struct {
|
||||
// Kind of the config entry. This should be set to api.TCPRoute.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated tcp-route
|
||||
// service. This should match the name provided in the service definition.
|
||||
Name string
|
||||
|
||||
// Parents is a list of gateways that this route should be bound to.
|
||||
Parents []ResourceReference
|
||||
// Services is a list of TCP-based services that this should route to.
|
||||
// Currently, this must specify at maximum one service.
|
||||
Services []TCPService
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
|
||||
// Status is the asynchronous status which a TCPRoute propagates to the user.
|
||||
Status ConfigEntryStatus
|
||||
|
||||
// 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
|
||||
|
||||
// Partition is the partition the config entry is associated with.
|
||||
// Partitioning is a Consul Enterprise feature.
|
||||
Partition string `json:",omitempty"`
|
||||
|
||||
// Namespace is the namespace the config entry is associated with.
|
||||
// Namespacing is a Consul Enterprise feature.
|
||||
Namespace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (a *TCPRouteConfigEntry) GetKind() string {
|
||||
return TCPRoute
|
||||
}
|
||||
|
||||
func (a *TCPRouteConfigEntry) GetName() string {
|
||||
if a != nil {
|
||||
return ""
|
||||
}
|
||||
return a.Name
|
||||
}
|
||||
|
||||
func (a *TCPRouteConfigEntry) GetPartition() string {
|
||||
if a != nil {
|
||||
return ""
|
||||
}
|
||||
return a.Partition
|
||||
}
|
||||
|
||||
func (a *TCPRouteConfigEntry) GetNamespace() string {
|
||||
if a != nil {
|
||||
return ""
|
||||
}
|
||||
return a.GetNamespace()
|
||||
}
|
||||
|
||||
func (a *TCPRouteConfigEntry) GetMeta() map[string]string {
|
||||
if a != nil {
|
||||
return nil
|
||||
}
|
||||
return a.GetMeta()
|
||||
}
|
||||
|
||||
func (a *TCPRouteConfigEntry) GetCreateIndex() uint64 {
|
||||
return a.CreateIndex
|
||||
}
|
||||
|
||||
func (a *TCPRouteConfigEntry) GetModifyIndex() uint64 {
|
||||
return a.ModifyIndex
|
||||
}
|
||||
|
||||
// TCPService is a service reference for a TCPRoute
|
||||
type TCPService struct {
|
||||
Name string
|
||||
// Weight specifies the proportion of requests forwarded to the referenced service.
|
||||
// This is computed as weight/(sum of all weights in the list of services).
|
||||
Weight int
|
||||
|
||||
// Partition is the partition the config entry is associated with.
|
||||
// Partitioning is a Consul Enterprise feature.
|
||||
Partition string `json:",omitempty"`
|
||||
|
||||
// Namespace is the namespace the config entry is associated with.
|
||||
// Namespacing is a Consul Enterprise feature.
|
||||
Namespace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// HTTPRouteConfigEntry manages the configuration for a HTTP route
|
||||
// with the given name.
|
||||
type HTTPRouteConfigEntry struct {
|
||||
// Kind of the config entry. This should be set to api.HTTPRoute.
|
||||
Kind string
|
||||
|
||||
// Name is used to match the config entry with its associated http-route.
|
||||
Name string
|
||||
|
||||
Meta map[string]string `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
|
||||
|
||||
// Partition is the partition the config entry is associated with.
|
||||
// Partitioning is a Consul Enterprise feature.
|
||||
Partition string `json:",omitempty"`
|
||||
|
||||
// Namespace is the namespace the config entry is associated with.
|
||||
// Namespacing is a Consul Enterprise feature.
|
||||
Namespace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (r *HTTPRouteConfigEntry) GetKind() string { return HTTPRoute }
|
||||
func (r *HTTPRouteConfigEntry) GetName() string { return r.Name }
|
||||
func (r *HTTPRouteConfigEntry) GetPartition() string { return r.Partition }
|
||||
func (r *HTTPRouteConfigEntry) GetNamespace() string { return r.Namespace }
|
||||
func (r *HTTPRouteConfigEntry) GetMeta() map[string]string { return r.Meta }
|
||||
func (r *HTTPRouteConfigEntry) GetCreateIndex() uint64 { return r.CreateIndex }
|
||||
func (r *HTTPRouteConfigEntry) GetModifyIndex() uint64 { return r.ModifyIndex }
|
55
api/config_entry_status.go
Normal file
55
api/config_entry_status.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ResourceReference is a reference to a ConfigEntry
|
||||
// with an optional reference to a subsection of that ConfigEntry
|
||||
// that can be specified as SectionName
|
||||
type ResourceReference struct {
|
||||
// Kind is the kind of ConfigEntry that this resource refers to.
|
||||
Kind string
|
||||
// Name is the identifier for the ConfigEntry this resource refers to.
|
||||
Name string
|
||||
// SectionName is a generic subresource identifier that specifies
|
||||
// a subset of the ConfigEntry to which this reference applies. Usage
|
||||
// of this field should be up to the controller that leverages it. If
|
||||
// unused, this should be blank.
|
||||
SectionName string
|
||||
|
||||
// Partition is the partition the config entry is associated with.
|
||||
// Partitioning is a Consul Enterprise feature.
|
||||
Partition string `json:",omitempty"`
|
||||
|
||||
// Namespace is the namespace the config entry is associated with.
|
||||
// Namespacing is a Consul Enterprise feature.
|
||||
Namespace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ConfigEntryStatus is used for propagating back asynchronously calculated
|
||||
// messages from control loops to a user
|
||||
type ConfigEntryStatus struct {
|
||||
// Conditions is the set of condition objects associated with
|
||||
// a ConfigEntry status.
|
||||
Conditions []Condition
|
||||
}
|
||||
|
||||
// Condition is used for a single message and state associated
|
||||
// with an object. For example, a ConfigEntry that references
|
||||
// multiple other resources may have different statuses with
|
||||
// respect to each of those resources.
|
||||
type Condition struct {
|
||||
// Status is a value from a bounded set of statuses that an object might have
|
||||
Status string
|
||||
// Reason is a value from a bounded set of reasons for a given status
|
||||
Reason string
|
||||
// Message is a message that gives more detailed information about
|
||||
// why a Condition has a given status and reason
|
||||
Message string
|
||||
// Resource is an optional reference to a resource for which this
|
||||
// condition applies
|
||||
Resource *ResourceReference
|
||||
// LastTransitionTime is the time at which this Condition was created
|
||||
LastTransitionTime *time.Time
|
||||
}
|
|
@ -3037,6 +3037,190 @@ func TestParseConfigEntry(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api-gateway",
|
||||
snake: `
|
||||
kind = "api-gateway"
|
||||
name = "main"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "api-gateway"
|
||||
Name = "main"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
snakeJSON: `
|
||||
{
|
||||
"kind": "api-gateway",
|
||||
"name": "main",
|
||||
"meta": {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
}
|
||||
}
|
||||
`,
|
||||
camelJSON: `
|
||||
{
|
||||
"Kind": "api-gateway",
|
||||
"Name": "main",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
}
|
||||
}`,
|
||||
expect: &api.APIGatewayConfigEntry{
|
||||
Kind: "api-gateway",
|
||||
Name: "main",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "inline-certificate",
|
||||
snake: `
|
||||
kind = "inline-certificate"
|
||||
name = "main"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "inline-certificate"
|
||||
Name = "main"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
snakeJSON: `
|
||||
{
|
||||
"kind": "inline-certificate",
|
||||
"name": "main",
|
||||
"meta": {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
}
|
||||
}
|
||||
`,
|
||||
camelJSON: `
|
||||
{
|
||||
"Kind": "inline-certificate",
|
||||
"Name": "main",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
}
|
||||
}`,
|
||||
expect: &api.InlineCertificateConfigEntry{
|
||||
Kind: "inline-certificate",
|
||||
Name: "main",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "http-route",
|
||||
snake: `
|
||||
kind = "http-route"
|
||||
name = "main"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "http-route"
|
||||
Name = "main"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
snakeJSON: `
|
||||
{
|
||||
"kind": "http-route",
|
||||
"name": "main",
|
||||
"meta": {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
}
|
||||
}
|
||||
`,
|
||||
camelJSON: `
|
||||
{
|
||||
"Kind": "http-route",
|
||||
"Name": "main",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
}
|
||||
}`,
|
||||
expect: &api.HTTPRouteConfigEntry{
|
||||
Kind: "http-route",
|
||||
Name: "main",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tcp-route",
|
||||
snake: `
|
||||
kind = "tcp-route"
|
||||
name = "main"
|
||||
meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
camel: `
|
||||
Kind = "tcp-route"
|
||||
Name = "main"
|
||||
Meta {
|
||||
"foo" = "bar"
|
||||
"gir" = "zim"
|
||||
}
|
||||
`,
|
||||
snakeJSON: `
|
||||
{
|
||||
"kind": "tcp-route",
|
||||
"name": "main",
|
||||
"meta": {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
}
|
||||
}
|
||||
`,
|
||||
camelJSON: `
|
||||
{
|
||||
"Kind": "tcp-route",
|
||||
"Name": "main",
|
||||
"Meta": {
|
||||
"foo": "bar",
|
||||
"gir": "zim"
|
||||
}
|
||||
}`,
|
||||
expect: &api.TCPRouteConfigEntry{
|
||||
Kind: "tcp-route",
|
||||
Name: "main",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
"gir": "zim",
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
|
||||
|
|
|
@ -4,6 +4,210 @@ package pbconfigentry
|
|||
|
||||
import "github.com/hashicorp/consul/agent/structs"
|
||||
|
||||
func APIGatewayToStructs(s *APIGateway, t *structs.APIGatewayConfigEntry) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
t.Listeners = make([]structs.APIGatewayListener, len(s.Listeners))
|
||||
for i := range s.Listeners {
|
||||
if s.Listeners[i] != nil {
|
||||
APIGatewayListenerToStructs(s.Listeners[i], &t.Listeners[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.Status != nil {
|
||||
StatusToStructs(s.Status, &t.Status)
|
||||
}
|
||||
t.Meta = s.Meta
|
||||
}
|
||||
func APIGatewayFromStructs(t *structs.APIGatewayConfigEntry, s *APIGateway) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
s.Listeners = make([]*APIGatewayListener, len(t.Listeners))
|
||||
for i := range t.Listeners {
|
||||
{
|
||||
var x APIGatewayListener
|
||||
APIGatewayListenerFromStructs(&t.Listeners[i], &x)
|
||||
s.Listeners[i] = &x
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
var x Status
|
||||
StatusFromStructs(&t.Status, &x)
|
||||
s.Status = &x
|
||||
}
|
||||
s.Meta = t.Meta
|
||||
}
|
||||
func APIGatewayListenerToStructs(s *APIGatewayListener, t *structs.APIGatewayListener) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.Name = s.Name
|
||||
t.Hostname = s.Hostname
|
||||
t.Port = int(s.Port)
|
||||
t.Protocol = apiGatewayProtocolToStructs(s.Protocol)
|
||||
if s.TLS != nil {
|
||||
APIGatewayTLSConfigurationToStructs(s.TLS, &t.TLS)
|
||||
}
|
||||
}
|
||||
func APIGatewayListenerFromStructs(t *structs.APIGatewayListener, s *APIGatewayListener) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.Name = t.Name
|
||||
s.Hostname = t.Hostname
|
||||
s.Port = int32(t.Port)
|
||||
s.Protocol = apiGatewayProtocolFromStructs(t.Protocol)
|
||||
{
|
||||
var x APIGatewayTLSConfiguration
|
||||
APIGatewayTLSConfigurationFromStructs(&t.TLS, &x)
|
||||
s.TLS = &x
|
||||
}
|
||||
}
|
||||
func APIGatewayTLSConfigurationToStructs(s *APIGatewayTLSConfiguration, t *structs.APIGatewayTLSConfiguration) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
t.Certificates = make([]structs.ResourceReference, len(s.Certificates))
|
||||
for i := range s.Certificates {
|
||||
if s.Certificates[i] != nil {
|
||||
ResourceReferenceToStructs(s.Certificates[i], &t.Certificates[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
t.MaxVersion = tlsVersionToStructs(s.MaxVersion)
|
||||
t.MinVersion = tlsVersionToStructs(s.MinVersion)
|
||||
t.CipherSuites = cipherSuitesToStructs(s.CipherSuites)
|
||||
}
|
||||
func APIGatewayTLSConfigurationFromStructs(t *structs.APIGatewayTLSConfiguration, s *APIGatewayTLSConfiguration) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
s.Certificates = make([]*ResourceReference, len(t.Certificates))
|
||||
for i := range t.Certificates {
|
||||
{
|
||||
var x ResourceReference
|
||||
ResourceReferenceFromStructs(&t.Certificates[i], &x)
|
||||
s.Certificates[i] = &x
|
||||
}
|
||||
}
|
||||
}
|
||||
s.MaxVersion = tlsVersionFromStructs(t.MaxVersion)
|
||||
s.MinVersion = tlsVersionFromStructs(t.MinVersion)
|
||||
s.CipherSuites = cipherSuitesFromStructs(t.CipherSuites)
|
||||
}
|
||||
func BoundAPIGatewayToStructs(s *BoundAPIGateway, t *structs.BoundAPIGatewayConfigEntry) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
t.Listeners = make([]structs.BoundAPIGatewayListener, len(s.Listeners))
|
||||
for i := range s.Listeners {
|
||||
if s.Listeners[i] != nil {
|
||||
BoundAPIGatewayListenerToStructs(s.Listeners[i], &t.Listeners[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Meta = s.Meta
|
||||
}
|
||||
func BoundAPIGatewayFromStructs(t *structs.BoundAPIGatewayConfigEntry, s *BoundAPIGateway) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
s.Listeners = make([]*BoundAPIGatewayListener, len(t.Listeners))
|
||||
for i := range t.Listeners {
|
||||
{
|
||||
var x BoundAPIGatewayListener
|
||||
BoundAPIGatewayListenerFromStructs(&t.Listeners[i], &x)
|
||||
s.Listeners[i] = &x
|
||||
}
|
||||
}
|
||||
}
|
||||
s.Meta = t.Meta
|
||||
}
|
||||
func BoundAPIGatewayListenerToStructs(s *BoundAPIGatewayListener, t *structs.BoundAPIGatewayListener) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.Name = s.Name
|
||||
{
|
||||
t.Routes = make([]structs.ResourceReference, len(s.Routes))
|
||||
for i := range s.Routes {
|
||||
if s.Routes[i] != nil {
|
||||
ResourceReferenceToStructs(s.Routes[i], &t.Routes[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
t.Certificates = make([]structs.ResourceReference, len(s.Certificates))
|
||||
for i := range s.Certificates {
|
||||
if s.Certificates[i] != nil {
|
||||
ResourceReferenceToStructs(s.Certificates[i], &t.Certificates[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func BoundAPIGatewayListenerFromStructs(t *structs.BoundAPIGatewayListener, s *BoundAPIGatewayListener) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.Name = t.Name
|
||||
{
|
||||
s.Routes = make([]*ResourceReference, len(t.Routes))
|
||||
for i := range t.Routes {
|
||||
{
|
||||
var x ResourceReference
|
||||
ResourceReferenceFromStructs(&t.Routes[i], &x)
|
||||
s.Routes[i] = &x
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
s.Certificates = make([]*ResourceReference, len(t.Certificates))
|
||||
for i := range t.Certificates {
|
||||
{
|
||||
var x ResourceReference
|
||||
ResourceReferenceFromStructs(&t.Certificates[i], &x)
|
||||
s.Certificates[i] = &x
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func ConditionToStructs(s *Condition, t *structs.Condition) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.Status = s.Status
|
||||
t.Reason = s.Reason
|
||||
t.Message = s.Message
|
||||
if s.Resource != nil {
|
||||
var x structs.ResourceReference
|
||||
ResourceReferenceToStructs(s.Resource, &x)
|
||||
t.Resource = &x
|
||||
}
|
||||
t.LastTransitionTime = timeToStructs(s.LastTransitionTime)
|
||||
}
|
||||
func ConditionFromStructs(t *structs.Condition, s *Condition) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.Status = t.Status
|
||||
s.Reason = t.Reason
|
||||
s.Message = t.Message
|
||||
if t.Resource != nil {
|
||||
var x ResourceReference
|
||||
ResourceReferenceFromStructs(t.Resource, &x)
|
||||
s.Resource = &x
|
||||
}
|
||||
s.LastTransitionTime = timeFromStructs(t.LastTransitionTime)
|
||||
}
|
||||
func CookieConfigToStructs(s *CookieConfig, t *structs.CookieConfig) {
|
||||
if s == nil {
|
||||
return
|
||||
|
@ -178,6 +382,18 @@ func HTTPHeaderModifiersFromStructs(t *structs.HTTPHeaderModifiers, s *HTTPHeade
|
|||
s.Set = t.Set
|
||||
s.Remove = t.Remove
|
||||
}
|
||||
func HTTPRouteToStructs(s *HTTPRoute, t *structs.HTTPRouteConfigEntry) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.Meta = s.Meta
|
||||
}
|
||||
func HTTPRouteFromStructs(t *structs.HTTPRouteConfigEntry, s *HTTPRoute) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.Meta = t.Meta
|
||||
}
|
||||
func HashPolicyToStructs(s *HashPolicy, t *structs.HashPolicy) {
|
||||
if s == nil {
|
||||
return
|
||||
|
@ -386,6 +602,22 @@ func IngressServiceConfigFromStructs(t *structs.IngressServiceConfig, s *Ingress
|
|||
s.PassiveHealthCheck = &x
|
||||
}
|
||||
}
|
||||
func InlineCertificateToStructs(s *InlineCertificate, t *structs.InlineCertificateConfigEntry) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.Certificate = s.Certificate
|
||||
t.PrivateKey = s.PrivateKey
|
||||
t.Meta = s.Meta
|
||||
}
|
||||
func InlineCertificateFromStructs(t *structs.InlineCertificateConfigEntry, s *InlineCertificate) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.Certificate = t.Certificate
|
||||
s.PrivateKey = t.PrivateKey
|
||||
s.Meta = t.Meta
|
||||
}
|
||||
func IntentionHTTPHeaderPermissionToStructs(s *IntentionHTTPHeaderPermission, t *structs.IntentionHTTPHeaderPermission) {
|
||||
if s == nil {
|
||||
return
|
||||
|
@ -678,6 +910,24 @@ func PeeringMeshConfigFromStructs(t *structs.PeeringMeshConfig, s *PeeringMeshCo
|
|||
}
|
||||
s.PeerThroughMeshGateways = t.PeerThroughMeshGateways
|
||||
}
|
||||
func ResourceReferenceToStructs(s *ResourceReference, t *structs.ResourceReference) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.Kind = s.Kind
|
||||
t.Name = s.Name
|
||||
t.SectionName = s.SectionName
|
||||
t.EnterpriseMeta = enterpriseMetaToStructs(s.EnterpriseMeta)
|
||||
}
|
||||
func ResourceReferenceFromStructs(t *structs.ResourceReference, s *ResourceReference) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.Kind = t.Kind
|
||||
s.Name = t.Name
|
||||
s.SectionName = t.SectionName
|
||||
s.EnterpriseMeta = enterpriseMetaFromStructs(t.EnterpriseMeta)
|
||||
}
|
||||
func RingHashConfigToStructs(s *RingHashConfig, t *structs.RingHashConfig) {
|
||||
if s == nil {
|
||||
return
|
||||
|
@ -1038,6 +1288,106 @@ func SourceIntentionFromStructs(t *structs.SourceIntention, s *SourceIntention)
|
|||
s.EnterpriseMeta = enterpriseMetaFromStructs(t.EnterpriseMeta)
|
||||
s.Peer = t.Peer
|
||||
}
|
||||
func StatusToStructs(s *Status, t *structs.Status) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
t.Conditions = make([]structs.Condition, len(s.Conditions))
|
||||
for i := range s.Conditions {
|
||||
if s.Conditions[i] != nil {
|
||||
ConditionToStructs(s.Conditions[i], &t.Conditions[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func StatusFromStructs(t *structs.Status, s *Status) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
s.Conditions = make([]*Condition, len(t.Conditions))
|
||||
for i := range t.Conditions {
|
||||
{
|
||||
var x Condition
|
||||
ConditionFromStructs(&t.Conditions[i], &x)
|
||||
s.Conditions[i] = &x
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func TCPRouteToStructs(s *TCPRoute, t *structs.TCPRouteConfigEntry) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
t.Parents = make([]structs.ResourceReference, len(s.Parents))
|
||||
for i := range s.Parents {
|
||||
if s.Parents[i] != nil {
|
||||
ResourceReferenceToStructs(s.Parents[i], &t.Parents[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
t.Services = make([]structs.TCPService, len(s.Services))
|
||||
for i := range s.Services {
|
||||
if s.Services[i] != nil {
|
||||
TCPServiceToStructs(s.Services[i], &t.Services[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Meta = s.Meta
|
||||
if s.Status != nil {
|
||||
StatusToStructs(s.Status, &t.Status)
|
||||
}
|
||||
}
|
||||
func TCPRouteFromStructs(t *structs.TCPRouteConfigEntry, s *TCPRoute) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
{
|
||||
s.Parents = make([]*ResourceReference, len(t.Parents))
|
||||
for i := range t.Parents {
|
||||
{
|
||||
var x ResourceReference
|
||||
ResourceReferenceFromStructs(&t.Parents[i], &x)
|
||||
s.Parents[i] = &x
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
s.Services = make([]*TCPService, len(t.Services))
|
||||
for i := range t.Services {
|
||||
{
|
||||
var x TCPService
|
||||
TCPServiceFromStructs(&t.Services[i], &x)
|
||||
s.Services[i] = &x
|
||||
}
|
||||
}
|
||||
}
|
||||
s.Meta = t.Meta
|
||||
{
|
||||
var x Status
|
||||
StatusFromStructs(&t.Status, &x)
|
||||
s.Status = &x
|
||||
}
|
||||
}
|
||||
func TCPServiceToStructs(s *TCPService, t *structs.TCPService) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.Name = s.Name
|
||||
t.Weight = int(s.Weight)
|
||||
t.EnterpriseMeta = enterpriseMetaToStructs(s.EnterpriseMeta)
|
||||
}
|
||||
func TCPServiceFromStructs(t *structs.TCPService, s *TCPService) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.Name = t.Name
|
||||
s.Weight = int32(t.Weight)
|
||||
s.EnterpriseMeta = enterpriseMetaFromStructs(t.EnterpriseMeta)
|
||||
}
|
||||
func TransparentProxyConfigToStructs(s *TransparentProxyConfig, t *structs.TransparentProxyConfig) {
|
||||
if s == nil {
|
||||
return
|
||||
|
|
|
@ -314,3 +314,25 @@ func EnvoyExtensionsFromStructs(args []structs.EnvoyExtension) []*EnvoyExtension
|
|||
|
||||
return o
|
||||
}
|
||||
|
||||
func apiGatewayProtocolFromStructs(a structs.APIGatewayListenerProtocol) APIGatewayListenerProtocol {
|
||||
switch a {
|
||||
case structs.ListenerProtocolHTTP:
|
||||
return APIGatewayListenerProtocol_ListenerProtocolHTTP
|
||||
case structs.ListenerProtocolTCP:
|
||||
return APIGatewayListenerProtocol_ListenerProtocolTCP
|
||||
default:
|
||||
return APIGatewayListenerProtocol_ListenerProtocolHTTP
|
||||
}
|
||||
}
|
||||
|
||||
func apiGatewayProtocolToStructs(a APIGatewayListenerProtocol) structs.APIGatewayListenerProtocol {
|
||||
switch a {
|
||||
case APIGatewayListenerProtocol_ListenerProtocolHTTP:
|
||||
return structs.ListenerProtocolHTTP
|
||||
case APIGatewayListenerProtocol_ListenerProtocolTCP:
|
||||
return structs.ListenerProtocolTCP
|
||||
default:
|
||||
return structs.ListenerProtocolHTTP
|
||||
}
|
||||
}
|
||||
|
|
|
@ -416,3 +416,123 @@ func (msg *DestinationConfig) MarshalBinary() ([]byte, error) {
|
|||
func (msg *DestinationConfig) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *APIGateway) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *APIGateway) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *Status) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *Status) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *Condition) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *Condition) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *APIGatewayListener) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *APIGatewayListener) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *APIGatewayTLSConfiguration) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *APIGatewayTLSConfiguration) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *ResourceReference) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *ResourceReference) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *BoundAPIGateway) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *BoundAPIGateway) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *BoundAPIGatewayListener) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *BoundAPIGatewayListener) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *InlineCertificate) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *InlineCertificate) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *HTTPRoute) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *HTTPRoute) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *TCPRoute) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *TCPRoute) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *TCPService) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *TCPService) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,6 +14,11 @@ enum Kind {
|
|||
KindIngressGateway = 3;
|
||||
KindServiceIntentions = 4;
|
||||
KindServiceDefaults = 5;
|
||||
KindInlineCertificate = 6;
|
||||
KindAPIGateway = 7;
|
||||
KindBoundAPIGateway = 8;
|
||||
KindHTTPRoute = 9;
|
||||
KindTCPRoute = 10;
|
||||
}
|
||||
|
||||
message ConfigEntry {
|
||||
|
@ -575,3 +580,156 @@ message DestinationConfig {
|
|||
// mog: func-to=int func-from=int32
|
||||
int32 Port = 2;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.APIGatewayConfigEntry
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
// ignore-fields=Kind,Name,RaftIndex,EnterpriseMeta
|
||||
message APIGateway {
|
||||
map<string, string> Meta = 1;
|
||||
repeated APIGatewayListener Listeners = 2;
|
||||
Status Status = 3;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.Status
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message Status {
|
||||
repeated Condition Conditions = 1;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.Condition
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message Condition {
|
||||
string Status = 1;
|
||||
string Reason = 2;
|
||||
string Message = 3;
|
||||
ResourceReference Resource = 4;
|
||||
// mog: func-to=timeToStructs func-from=timeFromStructs
|
||||
google.protobuf.Timestamp LastTransitionTime = 5;
|
||||
}
|
||||
|
||||
enum APIGatewayListenerProtocol {
|
||||
ListenerProtocolHTTP = 0;
|
||||
ListenerProtocolTCP = 1;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.APIGatewayListener
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message APIGatewayListener {
|
||||
string Name = 1;
|
||||
string Hostname = 2;
|
||||
// mog: func-to=int func-from=int32
|
||||
int32 Port = 3;
|
||||
// mog: func-to=apiGatewayProtocolToStructs func-from=apiGatewayProtocolFromStructs
|
||||
APIGatewayListenerProtocol Protocol = 4;
|
||||
APIGatewayTLSConfiguration TLS = 5;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.APIGatewayTLSConfiguration
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message APIGatewayTLSConfiguration {
|
||||
repeated ResourceReference Certificates = 1;
|
||||
// mog: func-from=tlsVersionFromStructs func-to=tlsVersionToStructs
|
||||
string MinVersion = 2;
|
||||
// mog: func-from=tlsVersionFromStructs func-to=tlsVersionToStructs
|
||||
string MaxVersion = 3;
|
||||
// mog: func-from=cipherSuitesFromStructs func-to=cipherSuitesToStructs
|
||||
repeated string CipherSuites = 4;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.ResourceReference
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message ResourceReference {
|
||||
string Kind = 1;
|
||||
string Name = 2;
|
||||
string SectionName = 3;
|
||||
// mog: func-to=enterpriseMetaToStructs func-from=enterpriseMetaFromStructs
|
||||
common.EnterpriseMeta EnterpriseMeta = 4;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.BoundAPIGatewayConfigEntry
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
// ignore-fields=Kind,Name,RaftIndex,EnterpriseMeta
|
||||
message BoundAPIGateway {
|
||||
map<string, string> Meta = 1;
|
||||
repeated BoundAPIGatewayListener Listeners = 2;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.BoundAPIGatewayListener
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message BoundAPIGatewayListener {
|
||||
string Name = 1;
|
||||
repeated ResourceReference Certificates = 2;
|
||||
repeated ResourceReference Routes = 3;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.InlineCertificateConfigEntry
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
// ignore-fields=Kind,Name,RaftIndex,EnterpriseMeta
|
||||
message InlineCertificate {
|
||||
map<string, string> Meta = 1;
|
||||
string Certificate = 2;
|
||||
string PrivateKey = 3;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.HTTPRouteConfigEntry
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
// ignore-fields=Kind,Name,RaftIndex,EnterpriseMeta
|
||||
message HTTPRoute {
|
||||
map<string, string> Meta = 1;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.TCPRouteConfigEntry
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
// ignore-fields=Kind,Name,RaftIndex,EnterpriseMeta
|
||||
message TCPRoute {
|
||||
map<string, string> Meta = 1;
|
||||
repeated ResourceReference Parents = 2;
|
||||
repeated TCPService Services = 3;
|
||||
Status Status = 4;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.TCPService
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message TCPService {
|
||||
string Name = 1;
|
||||
// mog: func-to=int func-from=int32
|
||||
int32 Weight = 2;
|
||||
// mog: func-to=enterpriseMetaToStructs func-from=enterpriseMetaFromStructs
|
||||
common.EnterpriseMeta EnterpriseMeta = 4;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,16 @@ const (
|
|||
Topic_ServiceList Topic = 7
|
||||
// ServiceDefaults topic contains events for changes to service-defaults.
|
||||
Topic_ServiceDefaults Topic = 8
|
||||
// APIGateway topic contains events for changes to api-gateways.
|
||||
Topic_APIGateway Topic = 9
|
||||
// TCPRoute topic contains events for changes to tcp-routes.
|
||||
Topic_TCPRoute Topic = 10
|
||||
// HTTPRoute topic contains events for changes to http-routes.
|
||||
Topic_HTTPRoute Topic = 11
|
||||
// InlineCertificate topic contains events for changes to inline-certificates.
|
||||
Topic_InlineCertificate Topic = 12
|
||||
// BoundAPIGateway topic contains events for changes to bound-api-gateways.
|
||||
Topic_BoundAPIGateway Topic = 13
|
||||
)
|
||||
|
||||
// Enum value maps for Topic.
|
||||
|
@ -73,6 +83,11 @@ var (
|
|||
6: "ServiceIntentions",
|
||||
7: "ServiceList",
|
||||
8: "ServiceDefaults",
|
||||
9: "APIGateway",
|
||||
10: "TCPRoute",
|
||||
11: "HTTPRoute",
|
||||
12: "InlineCertificate",
|
||||
13: "BoundAPIGateway",
|
||||
}
|
||||
Topic_value = map[string]int32{
|
||||
"Unknown": 0,
|
||||
|
@ -84,6 +99,11 @@ var (
|
|||
"ServiceIntentions": 6,
|
||||
"ServiceList": 7,
|
||||
"ServiceDefaults": 8,
|
||||
"APIGateway": 9,
|
||||
"TCPRoute": 10,
|
||||
"HTTPRoute": 11,
|
||||
"InlineCertificate": 12,
|
||||
"BoundAPIGateway": 13,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -965,8 +985,8 @@ var file_proto_pbsubscribe_subscribe_proto_rawDesc = []byte{
|
|||
0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x52, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x2a, 0xb7,
|
||||
0x01, 0x0a, 0x05, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x2a, 0x90,
|
||||
0x02, 0x0a, 0x05, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e,
|
||||
0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||
0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
|
||||
|
@ -977,26 +997,31 @@ var file_proto_pbsubscribe_subscribe_proto_rawDesc = []byte{
|
|||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x10, 0x06, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65,
|
||||
0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x10, 0x08, 0x2a, 0x29, 0x0a, 0x09, 0x43, 0x61, 0x74, 0x61,
|
||||
0x6c, 0x6f, 0x67, 0x4f, 0x70, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65,
|
||||
0x72, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65,
|
||||
0x72, 0x10, 0x01, 0x32, 0x5f, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e,
|
||||
0x67, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44,
|
||||
0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x75,
|
||||
0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
|
||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63,
|
||||
0x72, 0x69, 0x62, 0x65, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x06, 0xe2, 0x86, 0x04, 0x02,
|
||||
0x08, 0x02, 0x30, 0x01, 0x42, 0x92, 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x75, 0x62,
|
||||
0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x42, 0x0e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
|
||||
0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63,
|
||||
0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x73, 0x75,
|
||||
0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xa2, 0x02, 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x09,
|
||||
0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xca, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73,
|
||||
0x63, 0x72, 0x69, 0x62, 0x65, 0xe2, 0x02, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
|
||||
0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09,
|
||||
0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x50, 0x49, 0x47,
|
||||
0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x10, 0x09, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x43, 0x50, 0x52,
|
||||
0x6f, 0x75, 0x74, 0x65, 0x10, 0x0a, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x54, 0x54, 0x50, 0x52, 0x6f,
|
||||
0x75, 0x74, 0x65, 0x10, 0x0b, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43,
|
||||
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x10, 0x0c, 0x12, 0x13, 0x0a, 0x0f,
|
||||
0x42, 0x6f, 0x75, 0x6e, 0x64, 0x41, 0x50, 0x49, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x10,
|
||||
0x0d, 0x2a, 0x29, 0x0a, 0x09, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x4f, 0x70, 0x12, 0x0c,
|
||||
0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a,
|
||||
0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x10, 0x01, 0x32, 0x5f, 0x0a, 0x17,
|
||||
0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63,
|
||||
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63,
|
||||
0x72, 0x69, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
|
||||
0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x10, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2e, 0x45, 0x76,
|
||||
0x65, 0x6e, 0x74, 0x22, 0x06, 0xe2, 0x86, 0x04, 0x02, 0x08, 0x02, 0x30, 0x01, 0x42, 0x92, 0x01,
|
||||
0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x42,
|
||||
0x0e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50,
|
||||
0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61,
|
||||
0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
|
||||
0xa2, 0x02, 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
|
||||
0x62, 0x65, 0xca, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xe2, 0x02,
|
||||
0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
|
||||
0x62, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -78,6 +78,21 @@ enum Topic {
|
|||
|
||||
// ServiceDefaults topic contains events for changes to service-defaults.
|
||||
ServiceDefaults = 8;
|
||||
|
||||
// APIGateway topic contains events for changes to api-gateways.
|
||||
APIGateway = 9;
|
||||
|
||||
// TCPRoute topic contains events for changes to tcp-routes.
|
||||
TCPRoute = 10;
|
||||
|
||||
// HTTPRoute topic contains events for changes to http-routes.
|
||||
HTTPRoute = 11;
|
||||
|
||||
// InlineCertificate topic contains events for changes to inline-certificates.
|
||||
InlineCertificate = 12;
|
||||
|
||||
// BoundAPIGateway topic contains events for changes to bound-api-gateways.
|
||||
BoundAPIGateway = 13;
|
||||
}
|
||||
|
||||
message NamedSubject {
|
||||
|
|
|
@ -1334,7 +1334,6 @@
|
|||
"title": "Error Messages",
|
||||
"path": "api-gateway/usage/errors"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue