From e24b3d9b3fec0fa4e439f2234c9d92fb50783740 Mon Sep 17 00:00:00 2001 From: Ronald Date: Fri, 15 Sep 2023 10:20:44 -0400 Subject: [PATCH 01/28] fix templated policy cli output (#18821) --- command/acl/templatedpolicy/formatter.go | 4 ++-- .../ce/dns-templated-policy.pretty-meta.golden | 4 ++-- .../ce/dns-templated-policy.pretty.golden | 4 ++-- .../ce/node-templated-policy.pretty-meta.golden | 2 +- .../ce/node-templated-policy.pretty.golden | 2 +- .../ce/service-templated-policy.pretty-meta.golden | 2 +- .../ce/service-templated-policy.pretty.golden | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/command/acl/templatedpolicy/formatter.go b/command/acl/templatedpolicy/formatter.go index a3b18fddf7ae..7945f168f863 100644 --- a/command/acl/templatedpolicy/formatter.go +++ b/command/acl/templatedpolicy/formatter.go @@ -64,7 +64,7 @@ type prettyFormatter struct { func (f *prettyFormatter) FormatTemplatedPolicy(templatedPolicy api.ACLTemplatedPolicyResponse) (string, error) { var buffer bytes.Buffer - buffer.WriteString(fmt.Sprintf("Name: %s\n", templatedPolicy.TemplateName)) + buffer.WriteString(fmt.Sprintf("Name: %s\n", templatedPolicy.TemplateName)) buffer.WriteString("Input variables:") switch templatedPolicy.TemplateName { @@ -77,7 +77,7 @@ func (f *prettyFormatter) FormatTemplatedPolicy(templatedPolicy api.ACLTemplated buffer.WriteString("Example usage:\n") buffer.WriteString(fmt.Sprintf("%sconsul acl token create -templated-policy builtin/node -var name:node-1\n", WhitespaceIndent)) case api.ACLTemplatedPolicyDNSName: - buffer.WriteString(" None\n") + buffer.WriteString(" None\n") buffer.WriteString("Example usage:\n") buffer.WriteString(fmt.Sprintf("%sconsul acl token create -templated-policy builtin/dns\n", WhitespaceIndent)) default: diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/dns-templated-policy.pretty-meta.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/dns-templated-policy.pretty-meta.golden index 4b63841632ce..a30a0c535547 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/dns-templated-policy.pretty-meta.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/dns-templated-policy.pretty-meta.golden @@ -1,5 +1,5 @@ -Name: builtin/dns -Input variables: None +Name: builtin/dns +Input variables: None Example usage: consul acl token create -templated-policy builtin/dns Raw Template: diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/dns-templated-policy.pretty.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/dns-templated-policy.pretty.golden index 012987d03995..f52cfdfe1dfc 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/dns-templated-policy.pretty.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/dns-templated-policy.pretty.golden @@ -1,4 +1,4 @@ -Name: builtin/dns -Input variables: None +Name: builtin/dns +Input variables: None Example usage: consul acl token create -templated-policy builtin/dns diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty-meta.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty-meta.golden index 4547a282d74d..ff42bd711f6d 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty-meta.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty-meta.golden @@ -1,4 +1,4 @@ -Name: builtin/node +Name: builtin/node Input variables: Name: String - Required - The node name. Example usage: diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty.golden index be80ef625c8a..a923087028c0 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty.golden @@ -1,4 +1,4 @@ -Name: builtin/node +Name: builtin/node Input variables: Name: String - Required - The node name. Example usage: diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty-meta.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty-meta.golden index d551c2857d15..49b347efcb7c 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty-meta.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty-meta.golden @@ -1,4 +1,4 @@ -Name: builtin/service +Name: builtin/service Input variables: Name: String - Required - The name of the service. Example usage: diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty.golden index ab10d27b2555..bc3ffcfd25e0 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty.golden @@ -1,4 +1,4 @@ -Name: builtin/service +Name: builtin/service Input variables: Name: String - Required - The name of the service. Example usage: From d3dad14030dd8f63f25e9279368440963d6be538 Mon Sep 17 00:00:00 2001 From: Semir Patel Date: Fri, 15 Sep 2023 09:34:18 -0500 Subject: [PATCH 02/28] resource: default peername to "local" for now (#18822) --- agent/grpc-external/services/resource/server.go | 2 +- .../services/resource/write_status.go | 2 +- .../services/resource/write_test.go | 16 ++++++++++++++++ internal/resource/tenancy.go | 11 ++++++++--- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/agent/grpc-external/services/resource/server.go b/agent/grpc-external/services/resource/server.go index 52d993ad4991..3ae839e69b4b 100644 --- a/agent/grpc-external/services/resource/server.go +++ b/agent/grpc-external/services/resource/server.go @@ -147,7 +147,7 @@ func validateId(id *pbresource.ID, errorPrefix string) error { id.Tenancy = &pbresource.Tenancy{ Partition: "", Namespace: "", - // TODO(spatel): Remove when peerTenancy introduced. + // TODO(spatel): NET-5475 - Remove as part of peer_name moving to PeerTenancy PeerName: "local", } } diff --git a/agent/grpc-external/services/resource/write_status.go b/agent/grpc-external/services/resource/write_status.go index 0c0395a267ba..673f193da0b0 100644 --- a/agent/grpc-external/services/resource/write_status.go +++ b/agent/grpc-external/services/resource/write_status.go @@ -173,7 +173,7 @@ func (s *Server) validateWriteStatusRequest(req *pbresource.WriteStatusRequest) req.Id.Tenancy = &pbresource.Tenancy{ Partition: "", Namespace: "", - // TODO(spatel): Remove when peerTenancy introduced. + // TODO(spatel): NET-5475 - Remove as part of peer_name moving to PeerTenancy PeerName: "local", } } diff --git a/agent/grpc-external/services/resource/write_test.go b/agent/grpc-external/services/resource/write_test.go index 755f8ef2ccbc..49a526e63d2b 100644 --- a/agent/grpc-external/services/resource/write_test.go +++ b/agent/grpc-external/services/resource/write_test.go @@ -256,6 +256,14 @@ func TestWrite_Create_Success(t *testing.T) { }, expectedTenancy: resource.DefaultNamespacedTenancy(), }, + // TODO(spatel): NET-5475 - Remove as part of peer_name moving to PeerTenancy + "namespaced resource defaults peername to local when empty": { + modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource { + artist.Id.Tenancy.PeerName = "" + return artist + }, + expectedTenancy: resource.DefaultNamespacedTenancy(), + }, "partitioned resource provides nonempty partition": { modFn: func(_, recordLabel *pbresource.Resource) *pbresource.Resource { return recordLabel @@ -283,6 +291,14 @@ func TestWrite_Create_Success(t *testing.T) { }, expectedTenancy: resource.DefaultPartitionedTenancy(), }, + // TODO(spatel): NET-5475 - Remove as part of peer_name moving to PeerTenancy + "partitioned resource defaults peername to local when empty": { + modFn: func(_, recordLabel *pbresource.Resource) *pbresource.Resource { + recordLabel.Id.Tenancy.PeerName = "" + return recordLabel + }, + expectedTenancy: resource.DefaultPartitionedTenancy(), + }, // TODO(spatel): Add cluster scope tests when we have an actual cluster scoped resource (e.g. partition) } for desc, tc := range testCases { diff --git a/internal/resource/tenancy.go b/internal/resource/tenancy.go index 35ea87eabbea..2a377ca2f58a 100644 --- a/internal/resource/tenancy.go +++ b/internal/resource/tenancy.go @@ -52,12 +52,17 @@ func Normalize(tenancy *pbresource.Tenancy) { } tenancy.Partition = strings.ToLower(tenancy.Partition) tenancy.Namespace = strings.ToLower(tenancy.Namespace) + + // TODO(spatel): NET-5475 - Remove as part of peer_name moving to PeerTenancy + if tenancy.PeerName == "" { + tenancy.PeerName = "local" + } } // DefaultClusteredTenancy returns the default tenancy for a cluster scoped resource. func DefaultClusteredTenancy() *pbresource.Tenancy { return &pbresource.Tenancy{ - // TODO(spatel): Remove as part of "peer is not part of tenancy" ADR + // TODO(spatel): NET-5475 - Remove as part of peer_name moving to PeerTenancy PeerName: "local", } } @@ -66,7 +71,7 @@ func DefaultClusteredTenancy() *pbresource.Tenancy { func DefaultPartitionedTenancy() *pbresource.Tenancy { return &pbresource.Tenancy{ Partition: DefaultPartitionName, - // TODO(spatel): Remove as part of "peer is not part of tenancy" ADR + // TODO(spatel): NET-5475 - Remove as part of peer_name moving to PeerTenancy PeerName: "local", } } @@ -76,7 +81,7 @@ func DefaultNamespacedTenancy() *pbresource.Tenancy { return &pbresource.Tenancy{ Partition: DefaultPartitionName, Namespace: DefaultNamespaceName, - // TODO(spatel): Remove as part of "peer is not part of tenancy" ADR + // TODO(spatel): NET-5475 - Remove as part of peer_name moving to PeerTenancy PeerName: "local", } } From 21fdbbabbcf6d4a8fc14e681118cb5f7f5feb3f2 Mon Sep 17 00:00:00 2001 From: Eric Haberkorn Date: Fri, 15 Sep 2023 12:31:22 -0400 Subject: [PATCH 03/28] Wire up traffic permissions (#18812) Wire up traffic permissions --- agent/xds/proxystateconverter/listeners.go | 4 +- agent/xds/rbac_test.go | 108 +++--- agent/xdsv2/rbac_resources.go | 14 +- .../sidecarproxycache/identities_cache.go | 38 ++ .../identities_cache_test.go | 59 +++ .../mesh/internal/controllers/register.go | 5 +- .../sidecarproxy/builder/local_app.go | 170 ++++++++- .../builder/local_app_multiport_test.go | 2 +- .../sidecarproxy/builder/local_app_test.go | 216 ++++++++++- ...kload-addresses-with-specific-ports.golden | 15 +- ...le-workload-addresses-without-ports.golden | 3 +- ...ngle-workload-address-without-ports.golden | 3 +- ...kload-addresses-with-specific-ports.golden | 6 +- ...le-workload-addresses-without-ports.golden | 6 +- ...ngle-workload-address-without-ports.golden | 6 +- .../controllers/sidecarproxy/controller.go | 33 +- .../sidecarproxy/controller_test.go | 66 +++- .../sidecarproxy/fetcher/data_fetcher.go | 20 +- .../sidecarproxy/fetcher/data_fetcher_test.go | 49 ++- .../mappers/sidecarproxymapper/mapper.go | 3 + .../traffic_permissions_mapper.go | 35 ++ .../traffic_permissions_mapper_test.go | 63 ++++ .../sidecarproxymapper/unified_mapper_test.go | 2 +- internal/mesh/internal/types/decoded.go | 26 +- .../v1alpha1/traffic_permissions_addon.go | 14 + .../pbcatalog/v1alpha1/workload_addon.go | 13 + .../v1alpha1/pbproxystate/listener.pb.go | 183 +++++---- .../v1alpha1/pbproxystate/listener.proto | 4 +- .../pbproxystate/traffic_permissions.go | 20 - .../traffic_permissions.pb.binary.go | 32 +- .../pbproxystate/traffic_permissions.pb.go | 356 ++++++------------ .../pbproxystate/traffic_permissions.proto | 22 +- 32 files changed, 1085 insertions(+), 511 deletions(-) create mode 100644 internal/mesh/internal/cache/sidecarproxycache/identities_cache.go create mode 100644 internal/mesh/internal/cache/sidecarproxycache/identities_cache_test.go create mode 100644 internal/mesh/internal/mappers/sidecarproxymapper/traffic_permissions_mapper.go create mode 100644 internal/mesh/internal/mappers/sidecarproxymapper/traffic_permissions_mapper_test.go create mode 100644 proto-public/pbauth/v1alpha1/traffic_permissions_addon.go delete mode 100644 proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.go diff --git a/agent/xds/proxystateconverter/listeners.go b/agent/xds/proxystateconverter/listeners.go index 0d74a709c193..3b9c5e513969 100644 --- a/agent/xds/proxystateconverter/listeners.go +++ b/agent/xds/proxystateconverter/listeners.go @@ -1051,7 +1051,7 @@ func (s *Converter) makeInboundListener(cfgSnap *proxycfg.ConfigSnapshot, name s l4Dest.MaxInboundConnections = uint64(cfg.MaxInboundConnections) } - l4Dest.TrafficPermissions = &pbproxystate.L4TrafficPermissions{} + l4Dest.TrafficPermissions = &pbproxystate.TrafficPermissions{} } l.Routers = append(l.Routers, localAppRouter) @@ -1576,7 +1576,7 @@ func (g *Converter) makeL7Destination(opts destinationOpts) (*pbproxystate.L7Des // access and that every filter chain uses our TLS certs. if len(opts.httpAuthzFilters) > 0 { // TODO(proxystate) support intentions in the future - dest.TrafficPermissions = &pbproxystate.L7TrafficPermissions{} + dest.TrafficPermissions = &pbproxystate.TrafficPermissions{} //cfg.HttpFilters = append(opts.httpAuthzFilters, cfg.HttpFilters...) } diff --git a/agent/xds/rbac_test.go b/agent/xds/rbac_test.go index 2170f266494a..2e979fb44052 100644 --- a/agent/xds/rbac_test.go +++ b/agent/xds/rbac_test.go @@ -556,25 +556,25 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) { } ) - makeL4Spiffe := func(name string, entMeta *acl.EnterpriseMeta) string { + makeSpiffe := func(name string, entMeta *acl.EnterpriseMeta) *pbproxystate.Spiffe { em := *acl.DefaultEnterpriseMeta() if entMeta != nil { em = *entMeta } - spiffe := makeSpiffePattern(rbacService{ + regex := makeSpiffePattern(rbacService{ ServiceName: structs.ServiceName{ Name: name, EnterpriseMeta: em, }, TrustDomain: testTrustDomain, }) - return spiffe + return &pbproxystate.Spiffe{Regex: regex} } tests := map[string]struct { intentionDefaultAllow bool v1Intentions structs.SimplifiedIntentions - v2L4TrafficPermissions *pbproxystate.L4TrafficPermissions + v2L4TrafficPermissions *pbproxystate.TrafficPermissions }{ "default-deny-mixed-precedence": { intentionDefaultAllow: false, @@ -583,12 +583,12 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) { testIntention(t, "*", "api", structs.IntentionActionDeny), testIntention(t, "web", "*", structs.IntentionActionDeny), ), - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{ - AllowPermissions: []*pbproxystate.L4Permission{ + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{ + AllowPermissions: []*pbproxystate.Permission{ { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("web", nil), + Spiffe: makeSpiffe("web", nil), }, }, }, @@ -600,12 +600,12 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) { v1Intentions: sorted( testSourceIntention("*", structs.IntentionActionAllow), ), - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{ - AllowPermissions: []*pbproxystate.L4Permission{ + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{ + AllowPermissions: []*pbproxystate.Permission{ { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("*", nil), + Spiffe: makeSpiffe("*", nil), }, }, }, @@ -623,12 +623,12 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) { v1Intentions: sorted( testSourceIntention("web", structs.IntentionActionAllow), ), - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{ - AllowPermissions: []*pbproxystate.L4Permission{ + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{ + AllowPermissions: []*pbproxystate.Permission{ { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("web", nil), + Spiffe: makeSpiffe("web", nil), }, }, }, @@ -647,13 +647,13 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) { testSourceIntention("web", structs.IntentionActionDeny), testSourceIntention("*", structs.IntentionActionAllow), ), - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{ - AllowPermissions: []*pbproxystate.L4Permission{ + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{ + AllowPermissions: []*pbproxystate.Permission{ { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("*", nil), - ExcludeSpiffeRegexes: []string{makeL4Spiffe("web", nil)}, + Spiffe: makeSpiffe("*", nil), + ExcludeSpiffes: []*pbproxystate.Spiffe{makeSpiffe("web", nil)}, }, }, }, @@ -669,22 +669,22 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) { testSourceIntention("cron", structs.IntentionActionAllow), testSourceIntention("*", structs.IntentionActionAllow), ), - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{ - AllowPermissions: []*pbproxystate.L4Permission{ + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{ + AllowPermissions: []*pbproxystate.Permission{ { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("cron", nil), + Spiffe: makeSpiffe("cron", nil), }, { - SpiffeRegex: makeL4Spiffe("web", nil), + Spiffe: makeSpiffe("web", nil), }, { - SpiffeRegex: makeL4Spiffe("*", nil), - ExcludeSpiffeRegexes: []string{ - makeL4Spiffe("web", nil), - makeL4Spiffe("unsafe", nil), - makeL4Spiffe("cron", nil), + Spiffe: makeSpiffe("*", nil), + ExcludeSpiffes: []*pbproxystate.Spiffe{ + makeSpiffe("web", nil), + makeSpiffe("unsafe", nil), + makeSpiffe("cron", nil), }, }, }, @@ -694,37 +694,37 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) { }, "v2-kitchen-sink": { intentionDefaultAllow: false, - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{ - AllowPermissions: []*pbproxystate.L4Permission{ + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{ + AllowPermissions: []*pbproxystate.Permission{ { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("api", nil), + Spiffe: makeSpiffe("api", nil), }, { - SpiffeRegex: makeL4Spiffe("*", nil), - ExcludeSpiffeRegexes: []string{ - makeL4Spiffe("unsafe", nil), + Spiffe: makeSpiffe("*", nil), + ExcludeSpiffes: []*pbproxystate.Spiffe{ + makeSpiffe("unsafe", nil), }, }, }, }, { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("web", nil), + Spiffe: makeSpiffe("web", nil), }, }, }, }, - DenyPermissions: []*pbproxystate.L4Permission{ + DenyPermissions: []*pbproxystate.Permission{ { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("db", nil), + Spiffe: makeSpiffe("db", nil), }, { - SpiffeRegex: makeL4Spiffe("cron", nil), + Spiffe: makeSpiffe("cron", nil), }, }, }, @@ -733,20 +733,20 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) { }, "v2-default-deny": { intentionDefaultAllow: false, - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{}, + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{}, }, "v2-default-allow": { intentionDefaultAllow: true, - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{}, + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{}, }, "v2-default-allow-one-allow": { intentionDefaultAllow: true, - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{ - AllowPermissions: []*pbproxystate.L4Permission{ + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{ + AllowPermissions: []*pbproxystate.Permission{ { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("web", nil), + Spiffe: makeSpiffe("web", nil), }, }, }, @@ -756,12 +756,12 @@ func TestMakeRBACNetworkAndHTTPFilters(t *testing.T) { // In v2, having a single permission turns on default deny. "v2-default-allow-one-deny": { intentionDefaultAllow: true, - v2L4TrafficPermissions: &pbproxystate.L4TrafficPermissions{ - DenyPermissions: []*pbproxystate.L4Permission{ + v2L4TrafficPermissions: &pbproxystate.TrafficPermissions{ + DenyPermissions: []*pbproxystate.Permission{ { - Principals: []*pbproxystate.L4Principal{ + Principals: []*pbproxystate.Principal{ { - SpiffeRegex: makeL4Spiffe("web", nil), + Spiffe: makeSpiffe("web", nil), }, }, }, diff --git a/agent/xdsv2/rbac_resources.go b/agent/xdsv2/rbac_resources.go index ee7e906691cc..6cc43224d7cb 100644 --- a/agent/xdsv2/rbac_resources.go +++ b/agent/xdsv2/rbac_resources.go @@ -20,7 +20,7 @@ const ( baseL4PermissionKey = "consul-intentions-layer4" ) -func MakeL4RBAC(defaultAllow bool, trafficPermissions *pbproxystate.L4TrafficPermissions) ([]*envoy_listener_v3.Filter, error) { +func MakeL4RBAC(defaultAllow bool, trafficPermissions *pbproxystate.TrafficPermissions) ([]*envoy_listener_v3.Filter, error) { var filters []*envoy_listener_v3.Filter if trafficPermissions == nil { @@ -60,7 +60,7 @@ func MakeL4RBAC(defaultAllow bool, trafficPermissions *pbproxystate.L4TrafficPer // includeAllowFilter determines if an Envoy RBAC allow filter will be included in the filter chain. // We include this filter with default deny or whenever any permissions are configured. -func includeAllowFilter(defaultAllow bool, trafficPermissions *pbproxystate.L4TrafficPermissions) bool { +func includeAllowFilter(defaultAllow bool, trafficPermissions *pbproxystate.TrafficPermissions) bool { hasPermissions := len(trafficPermissions.DenyPermissions)+len(trafficPermissions.AllowPermissions) > 0 return !defaultAllow || hasPermissions } @@ -73,7 +73,7 @@ func makeRBACFilter(rbac *envoy_rbac_v3.RBAC) (*envoy_listener_v3.Filter, error) return makeEnvoyFilter("envoy.filters.network.rbac", cfg) } -func makeRBACPolicies(l4Permissions []*pbproxystate.L4Permission) map[string]*envoy_rbac_v3.Policy { +func makeRBACPolicies(l4Permissions []*pbproxystate.Permission) map[string]*envoy_rbac_v3.Policy { policyLabel := func(i int) string { if len(l4Permissions) == 1 { return baseL4PermissionKey @@ -90,11 +90,11 @@ func makeRBACPolicies(l4Permissions []*pbproxystate.L4Permission) map[string]*en return policies } -func makeRBACPolicy(p *pbproxystate.L4Permission) *envoy_rbac_v3.Policy { +func makeRBACPolicy(p *pbproxystate.Permission) *envoy_rbac_v3.Policy { var principals []*envoy_rbac_v3.Principal - for _, l4Principal := range p.Principals { - principals = append(principals, toEnvoyPrincipal(l4Principal.ToL7Principal())) + for _, p := range p.Principals { + principals = append(principals, toEnvoyPrincipal(p)) } return &envoy_rbac_v3.Policy{ @@ -103,7 +103,7 @@ func makeRBACPolicy(p *pbproxystate.L4Permission) *envoy_rbac_v3.Policy { } } -func toEnvoyPrincipal(p *pbproxystate.L7Principal) *envoy_rbac_v3.Principal { +func toEnvoyPrincipal(p *pbproxystate.Principal) *envoy_rbac_v3.Principal { includePrincipal := principal(p.Spiffe) if len(p.ExcludeSpiffes) == 0 { diff --git a/internal/mesh/internal/cache/sidecarproxycache/identities_cache.go b/internal/mesh/internal/cache/sidecarproxycache/identities_cache.go new file mode 100644 index 000000000000..a7d9ba692c95 --- /dev/null +++ b/internal/mesh/internal/cache/sidecarproxycache/identities_cache.go @@ -0,0 +1,38 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package sidecarproxycache + +import ( + auth "github.com/hashicorp/consul/internal/auth" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/mappers/bimapper" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +// IdentitiesCache tracks mappings between workload identities and proxy IDs +// that a configuration applies to. It is the responsibility of the controller to +// keep this cache up-to-date. +type IdentitiesCache struct { + mapper *bimapper.Mapper +} + +func NewIdentitiesCache() *IdentitiesCache { + return &IdentitiesCache{ + mapper: bimapper.New(types.ProxyStateTemplateType, auth.WorkloadIdentityType), + } +} + +func (c *IdentitiesCache) ProxyIDsByWorkloadIdentity(id *pbresource.ID) []*pbresource.ID { + return c.mapper.ItemIDsForLink(id) +} + +func (c *IdentitiesCache) TrackPair(identityID *pbresource.ID, proxyID *pbresource.ID) { + c.mapper.TrackItem(proxyID, []resource.ReferenceOrID{identityID}) +} + +// UntrackProxyID removes tracking for the given proxy state template ID. +func (c *IdentitiesCache) UntrackProxyID(proxyID *pbresource.ID) { + c.mapper.UntrackItem(proxyID) +} diff --git a/internal/mesh/internal/cache/sidecarproxycache/identities_cache_test.go b/internal/mesh/internal/cache/sidecarproxycache/identities_cache_test.go new file mode 100644 index 000000000000..f3b5e3067afb --- /dev/null +++ b/internal/mesh/internal/cache/sidecarproxycache/identities_cache_test.go @@ -0,0 +1,59 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package sidecarproxycache + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/auth" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +func TestIdentitiesCache(t *testing.T) { + cache := NewIdentitiesCache() + + identityID1 := resourcetest.Resource(auth.WorkloadIdentityType, "workload-identity-1"). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + identityID2 := resourcetest.Resource(auth.WorkloadIdentityType, "workload-identity-2"). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + + proxyID1 := resourcetest.Resource(types.ProxyStateTemplateType, "service-workload-1"). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + proxyID2 := resourcetest.Resource(types.ProxyStateTemplateType, "service-workload-2"). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + + // Empty cache + require.Nil(t, cache.ProxyIDsByWorkloadIdentity(identityID1)) + require.Nil(t, cache.ProxyIDsByWorkloadIdentity(identityID2)) + + // Insert value and fetch it. + cache.TrackPair(identityID1, proxyID1) + require.Equal(t, []*pbresource.ID{proxyID1}, cache.ProxyIDsByWorkloadIdentity(identityID1)) + require.Nil(t, cache.ProxyIDsByWorkloadIdentity(identityID2)) + + // Insert another value referencing the same identity. + cache.TrackPair(identityID1, proxyID2) + require.ElementsMatch(t, []*pbresource.ID{proxyID1, proxyID2}, cache.ProxyIDsByWorkloadIdentity(identityID1)) + require.Nil(t, cache.ProxyIDsByWorkloadIdentity(identityID2)) + + // Now proxy 1 uses identity 2 + cache.TrackPair(identityID2, proxyID1) + require.Equal(t, []*pbresource.ID{proxyID1}, cache.ProxyIDsByWorkloadIdentity(identityID2)) + require.Equal(t, []*pbresource.ID{proxyID2}, cache.ProxyIDsByWorkloadIdentity(identityID1)) + + // Untrack proxy 2 + cache.UntrackProxyID(proxyID2) + require.Equal(t, []*pbresource.ID{proxyID1}, cache.ProxyIDsByWorkloadIdentity(identityID2)) + require.Nil(t, cache.ProxyIDsByWorkloadIdentity(identityID1)) + + // Untrack proxy 1 + cache.UntrackProxyID(proxyID1) + require.Nil(t, cache.ProxyIDsByWorkloadIdentity(identityID2)) + require.Nil(t, cache.ProxyIDsByWorkloadIdentity(identityID1)) +} diff --git a/internal/mesh/internal/controllers/register.go b/internal/mesh/internal/controllers/register.go index c98f02b963dd..cc62da83dec0 100644 --- a/internal/mesh/internal/controllers/register.go +++ b/internal/mesh/internal/controllers/register.go @@ -40,10 +40,11 @@ func Register(mgr *controller.Manager, deps Dependencies) { destinationsCache = sidecarproxycache.NewDestinationsCache() proxyCfgCache = sidecarproxycache.NewProxyConfigurationCache() computedRoutesCache = sidecarproxycache.NewComputedRoutesCache() - m = sidecarproxymapper.New(destinationsCache, proxyCfgCache, computedRoutesCache) + identitiesCache = sidecarproxycache.NewIdentitiesCache() + m = sidecarproxymapper.New(destinationsCache, proxyCfgCache, computedRoutesCache, identitiesCache) ) mgr.Register( - sidecarproxy.Controller(destinationsCache, proxyCfgCache, computedRoutesCache, m, deps.TrustDomainFetcher, deps.LocalDatacenter), + sidecarproxy.Controller(destinationsCache, proxyCfgCache, computedRoutesCache, identitiesCache, m, deps.TrustDomainFetcher, deps.LocalDatacenter), ) mgr.Register(routes.Controller()) diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go index 409f50363816..2a34e366dc2f 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go @@ -6,16 +6,21 @@ package builder import ( "fmt" + "github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/envoyextensions/xdscommon" + pbauth "github.com/hashicorp/consul/proto-public/pbauth/v1alpha1" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1/pbproxystate" + "github.com/hashicorp/consul/proto-public/pbresource" ) -func (b *Builder) BuildLocalApp(workload *pbcatalog.Workload) *Builder { +func (b *Builder) BuildLocalApp(workload *pbcatalog.Workload, ctp *pbauth.ComputedTrafficPermissions) *Builder { // Add the public listener. lb := b.addInboundListener(xdscommon.PublicListenerName, workload) lb.buildListener() + trafficPermissions := buildTrafficPermissions(b.trustDomain, workload, ctp) + // Go through workload ports and add the routers, clusters, endpoints, and TLS. // Note that the order of ports is non-deterministic here but the xds generation // code should make sure to send it in the same order to Envoy to avoid unnecessary @@ -24,7 +29,7 @@ func (b *Builder) BuildLocalApp(workload *pbcatalog.Workload) *Builder { clusterName := fmt.Sprintf("%s:%s", xdscommon.LocalAppClusterName, portName) if port.Protocol != pbcatalog.Protocol_PROTOCOL_MESH { - lb.addInboundRouter(clusterName, port, portName). + lb.addInboundRouter(clusterName, port, portName, trafficPermissions[portName]). addInboundTLS() b.addLocalAppCluster(clusterName). @@ -35,6 +40,162 @@ func (b *Builder) BuildLocalApp(workload *pbcatalog.Workload) *Builder { return b } +func buildTrafficPermissions(trustDomain string, workload *pbcatalog.Workload, computed *pbauth.ComputedTrafficPermissions) map[string]*pbproxystate.TrafficPermissions { + portsWithProtocol := workload.GetPortsByProtocol() + + out := make(map[string]*pbproxystate.TrafficPermissions) + portToProtocol := make(map[string]pbcatalog.Protocol) + var allPorts []string + for protocol, ports := range portsWithProtocol { + if protocol == pbcatalog.Protocol_PROTOCOL_MESH { + continue + } + + for _, p := range ports { + allPorts = append(allPorts, p) + portToProtocol[p] = protocol + out[p] = &pbproxystate.TrafficPermissions{} + } + } + + if computed == nil { + return out + } + + for _, p := range computed.DenyPermissions { + drsByPort := destinationRulesByPort(allPorts, p.DestinationRules) + principals := makePrincipals(trustDomain, p) + for port := range drsByPort { + out[port].DenyPermissions = append(out[port].DenyPermissions, &pbproxystate.Permission{ + Principals: principals, + }) + } + } + + for _, p := range computed.AllowPermissions { + drsByPort := destinationRulesByPort(allPorts, p.DestinationRules) + principals := makePrincipals(trustDomain, p) + for port := range drsByPort { + out[port].AllowPermissions = append(out[port].AllowPermissions, &pbproxystate.Permission{ + Principals: principals, + }) + } + } + + return out +} + +// TODO this is a placeholder until we add them to the IR. +type DestinationRule struct{} + +func destinationRulesByPort(allPorts []string, destinationRules []*pbauth.DestinationRule) map[string][]DestinationRule { + out := make(map[string][]DestinationRule) + + if len(destinationRules) == 0 { + for _, p := range allPorts { + out[p] = nil + } + + return out + } + + for _, destinationRule := range destinationRules { + ports, dr := convertDestinationRule(allPorts, destinationRule) + for _, p := range ports { + out[p] = append(out[p], dr) + } + } + + return out +} + +func convertDestinationRule(allPorts []string, dr *pbauth.DestinationRule) ([]string, DestinationRule) { + ports := make(map[string]struct{}) + if len(dr.PortNames) > 0 { + for _, p := range dr.PortNames { + ports[p] = struct{}{} + } + } else { + for _, p := range allPorts { + ports[p] = struct{}{} + } + } + + for _, exclude := range dr.Exclude { + for _, p := range exclude.PortNames { + delete(ports, p) + } + } + + var out []string + for p := range ports { + out = append(out, p) + } + + return out, DestinationRule{} +} + +func makePrincipals(trustDomain string, perm *pbauth.Permission) []*pbproxystate.Principal { + var principals []*pbproxystate.Principal + for _, s := range perm.Sources { + principals = append(principals, makePrincipal(trustDomain, s)) + } + + return principals +} + +func makePrincipal(trustDomain string, s *pbauth.Source) *pbproxystate.Principal { + excludes := make([]*pbproxystate.Spiffe, 0, len(s.Exclude)) + for _, es := range s.Exclude { + excludes = append(excludes, sourceToSpiffe(trustDomain, es)) + } + + return &pbproxystate.Principal{ + Spiffe: sourceToSpiffe(trustDomain, s), + ExcludeSpiffes: excludes, + } +} + +const ( + anyPath = `[^/]+` +) + +func sourceToSpiffe(trustDomain string, s pbauth.SourceToSpiffe) *pbproxystate.Spiffe { + var ( + name = s.GetIdentityName() + ns = s.GetNamespace() + ap = s.GetPartition() + ) + + if ns == "" && name != "" { + panic(fmt.Sprintf("not possible to have a wildcarded namespace %q but an exact identity %q", ns, name)) + } + + if ap == "" { + panic("not possible to have a wildcarded source partition") + } + + if ns == "" { + ns = anyPath + } + if name == "" { + name = anyPath + } + + spiffeMatcher := connect.SpiffeIDFromIdentityRef(trustDomain, &pbresource.Reference{ + Name: name, + Tenancy: &pbresource.Tenancy{ + Partition: ap, + Namespace: ns, + PeerName: s.GetPeer(), + }, + }) + + return &pbproxystate.Spiffe{ + Regex: fmt.Sprintf(`^%s$`, spiffeMatcher), + } +} + func (b *Builder) addInboundListener(name string, workload *pbcatalog.Workload) *ListenerBuilder { listener := &pbproxystate.Listener{ Name: name, @@ -77,7 +238,7 @@ func (b *Builder) addInboundListener(name string, workload *pbcatalog.Workload) return b.NewListenerBuilder(listener) } -func (l *ListenerBuilder) addInboundRouter(clusterName string, port *pbcatalog.WorkloadPort, portName string) *ListenerBuilder { +func (l *ListenerBuilder) addInboundRouter(clusterName string, port *pbcatalog.WorkloadPort, portName string, tp *pbproxystate.TrafficPermissions) *ListenerBuilder { if l.listener == nil { return l } @@ -91,7 +252,8 @@ func (l *ListenerBuilder) addInboundRouter(clusterName string, port *pbcatalog.W Name: clusterName, }, }, - StatPrefix: l.listener.Name, + StatPrefix: l.listener.Name, + TrafficPermissions: tp, }, }, Match: &pbproxystate.Match{ diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go index 56377033606c..816e01236a2d 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go @@ -76,7 +76,7 @@ func TestBuildLocalApp_Multiport(t *testing.T) { for name, c := range cases { t.Run(name, func(t *testing.T) { proxyTmpl := New(testProxyStateTemplateID(), testIdentityRef(), "foo.consul", "dc1", nil). - BuildLocalApp(c.workload). + BuildLocalApp(c.workload, nil). Build() // sort routers because of test flakes where order was flip flopping. diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_test.go b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_test.go index 658142647b51..66985c74b4e8 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_test.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_test.go @@ -12,13 +12,17 @@ import ( "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/internal/resource/resourcetest" "github.com/hashicorp/consul/internal/testing/golden" + pbauth "github.com/hashicorp/consul/proto-public/pbauth/v1alpha1" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" + pbproxystate "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1/pbproxystate" "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" ) func TestBuildLocalApp(t *testing.T) { cases := map[string]struct { workload *pbcatalog.Workload + ctp *pbauth.ComputedTrafficPermissions }{ "source/l4-single-workload-address-without-ports": { workload: &pbcatalog.Workload{ @@ -66,13 +70,26 @@ func TestBuildLocalApp(t *testing.T) { "port2": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, }, }, + ctp: &pbauth.ComputedTrafficPermissions{ + AllowPermissions: []*pbauth.Permission{ + { + Sources: []*pbauth.Source{ + { + IdentityName: "foo", + Namespace: "default", + Partition: "default", + }, + }, + }, + }, + }, }, } for name, c := range cases { t.Run(name, func(t *testing.T) { proxyTmpl := New(testProxyStateTemplateID(), testIdentityRef(), "foo.consul", "dc1", nil). - BuildLocalApp(c.workload). + BuildLocalApp(c.workload, c.ctp). Build() actual := protoToJSON(t, proxyTmpl) expected := golden.Get(t, actual, name+".golden") @@ -98,3 +115,200 @@ func testIdentityRef() *pbresource.Reference { }, } } + +func TestBuildL4TrafficPermissions(t *testing.T) { + testTrustDomain := "test.consul" + + cases := map[string]struct { + workloadPorts map[string]*pbcatalog.WorkloadPort + ctp *pbauth.ComputedTrafficPermissions + expected map[string]*pbproxystate.TrafficPermissions + }{ + "empty": { + workloadPorts: map[string]*pbcatalog.WorkloadPort{ + "p1": { + Protocol: pbcatalog.Protocol_PROTOCOL_TCP, + }, + "p2": { + Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, + }, + "p3": {}, + "mesh": { + Protocol: pbcatalog.Protocol_PROTOCOL_MESH, + }, + }, + expected: map[string]*pbproxystate.TrafficPermissions{ + "p1": {}, + "p2": {}, + "p3": {}, + }, + }, + "kitchen sink": { + workloadPorts: map[string]*pbcatalog.WorkloadPort{ + "p1": { + Protocol: pbcatalog.Protocol_PROTOCOL_TCP, + }, + "p2": { + Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, + }, + }, + ctp: &pbauth.ComputedTrafficPermissions{ + AllowPermissions: []*pbauth.Permission{ + { + Sources: []*pbauth.Source{ + { + IdentityName: "foo", + Partition: "default", + Namespace: "default", + }, + { + IdentityName: "", + Partition: "default", + Namespace: "default", + Exclude: []*pbauth.ExcludeSource{ + { + IdentityName: "bar", + Namespace: "default", + Partition: "default", + }, + }, + }, + }, + DestinationRules: []*pbauth.DestinationRule{ + // This should be p2. + { + Exclude: []*pbauth.ExcludePermissionRule{ + { + PortNames: []string{"p1"}, + }, + }, + }, + }, + }, + { + Sources: []*pbauth.Source{ + { + IdentityName: "baz", + Partition: "default", + Namespace: "default", + }, + }, + DestinationRules: []*pbauth.DestinationRule{ + { + PortNames: []string{"p1"}, + }, + }, + }, + }, + DenyPermissions: []*pbauth.Permission{ + { + Sources: []*pbauth.Source{ + { + IdentityName: "qux", + Partition: "default", + Namespace: "default", + }, + }, + }, + { + Sources: []*pbauth.Source{ + { + IdentityName: "", + Namespace: "default", + Partition: "default", + Exclude: []*pbauth.ExcludeSource{ + { + IdentityName: "quux", + Partition: "default", + Namespace: "default", + }, + }, + }, + }, + }, + }, + }, + expected: map[string]*pbproxystate.TrafficPermissions{ + "p1": { + DenyPermissions: []*pbproxystate.Permission{ + { + Principals: []*pbproxystate.Principal{ + { + Spiffe: &pbproxystate.Spiffe{Regex: "^spiffe://test.consul/ap/default/ns/default/identity/qux$"}, + }, + }, + }, + { + Principals: []*pbproxystate.Principal{ + { + Spiffe: &pbproxystate.Spiffe{Regex: `^spiffe://test.consul/ap/default/ns/default/identity/%5B%5E/%5D+$`}, + ExcludeSpiffes: []*pbproxystate.Spiffe{ + {Regex: "^spiffe://test.consul/ap/default/ns/default/identity/quux$"}, + }, + }, + }, + }, + }, + AllowPermissions: []*pbproxystate.Permission{ + { + Principals: []*pbproxystate.Principal{ + { + Spiffe: &pbproxystate.Spiffe{Regex: "^spiffe://test.consul/ap/default/ns/default/identity/baz$"}, + }, + }, + }, + }, + }, + "p2": { + DenyPermissions: []*pbproxystate.Permission{ + { + Principals: []*pbproxystate.Principal{ + { + Spiffe: &pbproxystate.Spiffe{Regex: "^spiffe://test.consul/ap/default/ns/default/identity/qux$"}, + }, + }, + }, + { + Principals: []*pbproxystate.Principal{ + { + Spiffe: &pbproxystate.Spiffe{Regex: `^spiffe://test.consul/ap/default/ns/default/identity/%5B%5E/%5D+$`}, + ExcludeSpiffes: []*pbproxystate.Spiffe{ + {Regex: "^spiffe://test.consul/ap/default/ns/default/identity/quux$"}, + }, + }, + }, + }, + }, + AllowPermissions: []*pbproxystate.Permission{ + { + Principals: []*pbproxystate.Principal{ + { + Spiffe: &pbproxystate.Spiffe{Regex: "^spiffe://test.consul/ap/default/ns/default/identity/foo$"}, + }, + { + Spiffe: &pbproxystate.Spiffe{Regex: `^spiffe://test.consul/ap/default/ns/default/identity/%5B%5E/%5D+$`}, + ExcludeSpiffes: []*pbproxystate.Spiffe{ + {Regex: "^spiffe://test.consul/ap/default/ns/default/identity/bar$"}, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + workload := &pbcatalog.Workload{ + Ports: tc.workloadPorts, + } + permissions := buildTrafficPermissions(testTrustDomain, workload, tc.ctp) + require.Equal(t, len(tc.expected), len(permissions)) + for k, v := range tc.expected { + prototest.AssertDeepEqual(t, v, permissions[k]) + } + }) + } +} diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-multiple-workload-addresses-with-specific-ports.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-multiple-workload-addresses-with-specific-ports.golden index 9d331630a5ea..d9320a445ac8 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-multiple-workload-addresses-with-specific-ports.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-multiple-workload-addresses-with-specific-ports.golden @@ -51,7 +51,20 @@ "cluster": { "name": "local_app:port1" }, - "statPrefix": "public_listener" + "statPrefix": "public_listener", + "trafficPermissions": { + "allowPermissions": [ + { + "principals": [ + { + "spiffe": { + "regex": "^spiffe://foo.consul/ap/default/ns/default/identity/foo$" + } + } + ] + } + ] + } }, "match": { "alpnProtocols": [ diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-multiple-workload-addresses-without-ports.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-multiple-workload-addresses-without-ports.golden index 6269f9b3b9c2..f9136e89d314 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-multiple-workload-addresses-without-ports.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-multiple-workload-addresses-without-ports.golden @@ -51,7 +51,8 @@ "cluster": { "name": "local_app:port1" }, - "statPrefix": "public_listener" + "statPrefix": "public_listener", + "trafficPermissions": {} }, "match": { "alpnProtocols": [ diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-single-workload-address-without-ports.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-single-workload-address-without-ports.golden index 6269f9b3b9c2..f9136e89d314 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-single-workload-address-without-ports.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/l4-single-workload-address-without-ports.golden @@ -51,7 +51,8 @@ "cluster": { "name": "local_app:port1" }, - "statPrefix": "public_listener" + "statPrefix": "public_listener", + "trafficPermissions": {} }, "match": { "alpnProtocols": [ diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-multiple-workload-addresses-with-specific-ports.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-multiple-workload-addresses-with-specific-ports.golden index cd5e8c12522b..f7240ea25605 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-multiple-workload-addresses-with-specific-ports.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-multiple-workload-addresses-with-specific-ports.golden @@ -66,7 +66,8 @@ "cluster": { "name": "local_app:admin-port" }, - "statPrefix": "public_listener" + "statPrefix": "public_listener", + "trafficPermissions": {} }, "match": { "alpnProtocols": [ @@ -89,7 +90,8 @@ "cluster": { "name": "local_app:api-port" }, - "statPrefix": "public_listener" + "statPrefix": "public_listener", + "trafficPermissions": {} }, "match": { "alpnProtocols": [ diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-multiple-workload-addresses-without-ports.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-multiple-workload-addresses-without-ports.golden index 170f72cb3562..e66f1e13d352 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-multiple-workload-addresses-without-ports.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-multiple-workload-addresses-without-ports.golden @@ -66,7 +66,8 @@ "cluster": { "name": "local_app:admin-port" }, - "statPrefix": "public_listener" + "statPrefix": "public_listener", + "trafficPermissions": {} }, "match": { "alpnProtocols": [ @@ -89,7 +90,8 @@ "cluster": { "name": "local_app:api-port" }, - "statPrefix": "public_listener" + "statPrefix": "public_listener", + "trafficPermissions": {} }, "match": { "alpnProtocols": [ diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-single-workload-address-without-ports.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-single-workload-address-without-ports.golden index 170f72cb3562..e66f1e13d352 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-single-workload-address-without-ports.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-single-workload-address-without-ports.golden @@ -66,7 +66,8 @@ "cluster": { "name": "local_app:admin-port" }, - "statPrefix": "public_listener" + "statPrefix": "public_listener", + "trafficPermissions": {} }, "match": { "alpnProtocols": [ @@ -89,7 +90,8 @@ "cluster": { "name": "local_app:api-port" }, - "statPrefix": "public_listener" + "statPrefix": "public_listener", + "trafficPermissions": {} }, "match": { "alpnProtocols": [ diff --git a/internal/mesh/internal/controllers/sidecarproxy/controller.go b/internal/mesh/internal/controllers/sidecarproxy/controller.go index 2e3649cbcf89..416d77261066 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/controller.go +++ b/internal/mesh/internal/controllers/sidecarproxy/controller.go @@ -9,6 +9,7 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" + "github.com/hashicorp/consul/internal/auth" "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/mesh/internal/cache/sidecarproxycache" @@ -17,6 +18,7 @@ import ( "github.com/hashicorp/consul/internal/mesh/internal/mappers/sidecarproxymapper" "github.com/hashicorp/consul/internal/mesh/internal/types" "github.com/hashicorp/consul/internal/resource" + pbauth "github.com/hashicorp/consul/proto-public/pbauth/v1alpha1" "github.com/hashicorp/consul/proto-public/pbresource" ) @@ -29,12 +31,13 @@ func Controller( destinationsCache *sidecarproxycache.DestinationsCache, proxyCfgCache *sidecarproxycache.ProxyConfigurationCache, computedRoutesCache *sidecarproxycache.ComputedRoutesCache, + identitiesCache *sidecarproxycache.IdentitiesCache, mapper *sidecarproxymapper.Mapper, trustDomainFetcher TrustDomainFetcher, dc string, ) controller.Controller { - if destinationsCache == nil || proxyCfgCache == nil || computedRoutesCache == nil || mapper == nil || trustDomainFetcher == nil { - panic("destinations cache, proxy configuration cache, computed routes cache, mapper, and trust domain fetcher are required") + if destinationsCache == nil || proxyCfgCache == nil || computedRoutesCache == nil || identitiesCache == nil || mapper == nil || trustDomainFetcher == nil { + panic("destinations cache, proxy configuration cache, computed routes cache, identities cache, mapper, and trust domain fetcher are required") } /* @@ -88,10 +91,12 @@ func Controller( WithWatch(types.UpstreamsType, mapper.MapDestinationsToProxyStateTemplate). WithWatch(types.ProxyConfigurationType, mapper.MapProxyConfigurationToProxyStateTemplate). WithWatch(types.ComputedRoutesType, mapper.MapComputedRoutesToProxyStateTemplate). + WithWatch(auth.ComputedTrafficPermissionsType, mapper.MapComputedTrafficPermissionsToProxyStateTemplate). WithReconciler(&reconciler{ destinationsCache: destinationsCache, proxyCfgCache: proxyCfgCache, computedRoutesCache: computedRoutesCache, + identitiesCache: identitiesCache, getTrustDomain: trustDomainFetcher, dc: dc, }) @@ -101,6 +106,7 @@ type reconciler struct { destinationsCache *sidecarproxycache.DestinationsCache proxyCfgCache *sidecarproxycache.ProxyConfigurationCache computedRoutesCache *sidecarproxycache.ComputedRoutesCache + identitiesCache *sidecarproxycache.IdentitiesCache getTrustDomain TrustDomainFetcher dc string } @@ -116,6 +122,7 @@ func (r *reconciler) Reconcile(ctx context.Context, rt controller.Runtime, req c r.destinationsCache, r.proxyCfgCache, r.computedRoutesCache, + r.identitiesCache, ) // Check if the workload exists. @@ -175,8 +182,20 @@ func (r *reconciler) Reconcile(ctx context.Context, rt controller.Runtime, req c rt.Logger.Error("error fetching proxy and merging proxy configurations", "error", err) return err } + + trafficPermissions, err := dataFetcher.FetchComputedTrafficPermissions(ctx, computedTrafficPermissionsIDFromWorkload(workload)) + if err != nil { + rt.Logger.Error("error fetching computed traffic permissions to compute proxy state template", "error", err) + return err + } + + var ctp *pbauth.ComputedTrafficPermissions + if trafficPermissions != nil { + ctp = trafficPermissions.Data + } + b := builder.New(req.ID, identityRefFromWorkload(workload), trustDomain, r.dc, proxyCfg). - BuildLocalApp(workload.Data) + BuildLocalApp(workload.Data, ctp) // Get all destinationsData. destinationsRefs := r.destinationsCache.DestinationsBySourceProxy(req.ID) @@ -252,3 +271,11 @@ func identityRefFromWorkload(w *types.DecodedWorkload) *pbresource.Reference { Tenancy: w.Resource.Id.Tenancy, } } + +func computedTrafficPermissionsIDFromWorkload(w *types.DecodedWorkload) *pbresource.ID { + return &pbresource.ID{ + Type: auth.ComputedTrafficPermissionsType, + Name: w.Data.Identity, + Tenancy: w.Resource.Id.Tenancy, + } +} diff --git a/internal/mesh/internal/controllers/sidecarproxy/controller_test.go b/internal/mesh/internal/controllers/sidecarproxy/controller_test.go index d972fd8d79f3..902c9f47de6b 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/controller_test.go +++ b/internal/mesh/internal/controllers/sidecarproxy/controller_test.go @@ -13,6 +13,7 @@ import ( svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing" "github.com/hashicorp/consul/envoyextensions/xdscommon" + "github.com/hashicorp/consul/internal/auth" "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/mesh/internal/cache/sidecarproxycache" @@ -23,6 +24,7 @@ import ( "github.com/hashicorp/consul/internal/mesh/internal/types" "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/internal/resource/resourcetest" + pbauth "github.com/hashicorp/consul/proto-public/pbauth/v1alpha1" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1/pbproxystate" @@ -41,13 +43,15 @@ type meshControllerTestSuite struct { ctl *reconciler ctx context.Context - apiWorkloadID *pbresource.ID - apiWorkload *pbcatalog.Workload - apiService *pbresource.Resource - apiServiceData *pbcatalog.Service - apiEndpoints *pbresource.Resource - apiEndpointsData *pbcatalog.ServiceEndpoints - webWorkload *pbresource.Resource + apiWorkloadID *pbresource.ID + apiWorkload *pbcatalog.Workload + computedTrafficPermissions *pbresource.Resource + computedTrafficPermissionsData *pbauth.ComputedTrafficPermissions + apiService *pbresource.Resource + apiServiceData *pbcatalog.Service + apiEndpoints *pbresource.Resource + apiEndpointsData *pbcatalog.ServiceEndpoints + webWorkload *pbresource.Resource dbWorkloadID *pbresource.ID dbWorkload *pbcatalog.Workload @@ -59,7 +63,7 @@ type meshControllerTestSuite struct { } func (suite *meshControllerTestSuite) SetupTest() { - resourceClient := svctest.RunResourceService(suite.T(), types.Register, catalog.RegisterTypes) + resourceClient := svctest.RunResourceService(suite.T(), types.Register, catalog.RegisterTypes, auth.RegisterTypes) suite.client = resourcetest.NewClient(resourceClient) suite.runtime = controller.Runtime{Client: resourceClient, Logger: testutil.Logger(suite.T())} suite.ctx = testutil.TestContext(suite.T()) @@ -67,6 +71,7 @@ func (suite *meshControllerTestSuite) SetupTest() { suite.ctl = &reconciler{ destinationsCache: sidecarproxycache.NewDestinationsCache(), proxyCfgCache: sidecarproxycache.NewProxyConfigurationCache(), + identitiesCache: sidecarproxycache.NewIdentitiesCache(), getTrustDomain: func() (string, error) { return "test.consul", nil }, @@ -142,6 +147,25 @@ func (suite *meshControllerTestSuite) SetupTest() { }, } + suite.computedTrafficPermissionsData = &pbauth.ComputedTrafficPermissions{ + AllowPermissions: []*pbauth.Permission{ + { + Sources: []*pbauth.Source{ + { + IdentityName: "foo", + Namespace: "default", + Partition: "default", + Peer: "local", + }, + }, + }, + }, + } + + suite.computedTrafficPermissions = resourcetest.Resource(auth.ComputedTrafficPermissionsType, suite.apiWorkload.Identity). + WithData(suite.T(), suite.computedTrafficPermissionsData). + Write(suite.T(), resourceClient) + suite.apiService = resourcetest.Resource(catalog.ServiceType, "api-service"). WithData(suite.T(), suite.apiServiceData). Write(suite.T(), suite.client.ResourceServiceClient) @@ -203,7 +227,7 @@ func (suite *meshControllerTestSuite) SetupTest() { } suite.proxyStateTemplate = builder.New(suite.apiWorkloadID, identityRef, "test.consul", "dc1", nil). - BuildLocalApp(suite.apiWorkload). + BuildLocalApp(suite.apiWorkload, suite.computedTrafficPermissionsData). Build() } @@ -328,11 +352,12 @@ func (suite *meshControllerTestSuite) TestController() { destinationsCache = sidecarproxycache.NewDestinationsCache() proxyCfgCache = sidecarproxycache.NewProxyConfigurationCache() computedRoutesCache = sidecarproxycache.NewComputedRoutesCache() - m = sidecarproxymapper.New(destinationsCache, proxyCfgCache, computedRoutesCache) + identitiesCache = sidecarproxycache.NewIdentitiesCache() + m = sidecarproxymapper.New(destinationsCache, proxyCfgCache, computedRoutesCache, identitiesCache) ) trustDomainFetcher := func() (string, error) { return "test.consul", nil } - mgr.Register(Controller(destinationsCache, proxyCfgCache, computedRoutesCache, m, trustDomainFetcher, "dc1")) + mgr.Register(Controller(destinationsCache, proxyCfgCache, computedRoutesCache, identitiesCache, m, trustDomainFetcher, "dc1")) mgr.SetRaftLeader(true) go mgr.Run(suite.ctx) @@ -344,6 +369,7 @@ func (suite *meshControllerTestSuite) TestController() { apiComputedRoutesID = resource.ReplaceType(types.ComputedRoutesType, suite.apiService.Id) dbComputedRoutesID = resource.ReplaceType(types.ComputedRoutesType, suite.dbService.Id) + apiProxyStateTemplate *pbresource.Resource webProxyStateTemplate *pbresource.Resource webDestinations *pbresource.Resource ) @@ -353,6 +379,7 @@ func (suite *meshControllerTestSuite) TestController() { retry.Run(t, func(r *retry.R) { suite.client.RequireResourceExists(r, apiProxyStateTemplateID) webProxyStateTemplate = suite.client.RequireResourceExists(r, webProxyStateTemplateID) + apiProxyStateTemplate = suite.client.RequireResourceExists(r, apiProxyStateTemplateID) }) }) @@ -521,11 +548,28 @@ func (suite *meshControllerTestSuite) TestController() { }).Write(suite.T(), suite.client) webProxyStateTemplate = suite.client.WaitForNewVersion(suite.T(), webProxyStateTemplateID, webProxyStateTemplate.Version) + apiProxyStateTemplate = suite.client.WaitForNewVersion(t, apiProxyStateTemplateID, apiProxyStateTemplate.Version) requireImplicitDestinationsFound(t, "api", webProxyStateTemplate) requireImplicitDestinationsFound(t, "db", webProxyStateTemplate) }) + testutil.RunStep(suite.T(), "computed traffic permissions force regeneration", func(t *testing.T) { + suite.runtime.Logger.Trace("deleting computed traffic permissions") + _, err := suite.client.Delete(suite.ctx, &pbresource.DeleteRequest{Id: suite.computedTrafficPermissions.Id}) + require.NoError(t, err) + suite.client.WaitForDeletion(t, suite.computedTrafficPermissions.Id) + + apiProxyStateTemplate = suite.client.WaitForNewVersion(t, apiProxyStateTemplateID, apiProxyStateTemplate.Version) + + suite.runtime.Logger.Trace("creating computed traffic permissions") + resourcetest.Resource(auth.ComputedTrafficPermissionsType, suite.apiWorkload.Identity). + WithData(t, suite.computedTrafficPermissionsData). + Write(t, suite.client) + + suite.client.WaitForNewVersion(t, apiProxyStateTemplateID, apiProxyStateTemplate.Version) + }) + testutil.RunStep(suite.T(), "add an HTTPRoute with a simple split on the tcp port", func(t *testing.T) { // NOTE: because at this point we have tproxy in all-to-all mode, we will get an // implicit upstream on 'db' diff --git a/internal/mesh/internal/controllers/sidecarproxy/fetcher/data_fetcher.go b/internal/mesh/internal/controllers/sidecarproxy/fetcher/data_fetcher.go index a61c74a2dbb7..4bf5df4827b9 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/fetcher/data_fetcher.go +++ b/internal/mesh/internal/controllers/sidecarproxy/fetcher/data_fetcher.go @@ -12,6 +12,7 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" + "github.com/hashicorp/consul/internal/auth" "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/mesh/internal/cache/sidecarproxycache" ctrlStatus "github.com/hashicorp/consul/internal/mesh/internal/controllers/sidecarproxy/status" @@ -19,6 +20,7 @@ import ( intermediateTypes "github.com/hashicorp/consul/internal/mesh/internal/types/intermediate" "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/internal/storage" + pbauth "github.com/hashicorp/consul/proto-public/pbauth/v1alpha1" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" "github.com/hashicorp/consul/proto-public/pbresource" @@ -29,6 +31,7 @@ type Fetcher struct { DestinationsCache *sidecarproxycache.DestinationsCache ProxyCfgCache *sidecarproxycache.ProxyConfigurationCache ComputedRoutesCache *sidecarproxycache.ComputedRoutesCache + IdentitiesCache *sidecarproxycache.IdentitiesCache } func New( @@ -36,28 +39,39 @@ func New( dCache *sidecarproxycache.DestinationsCache, pcfgCache *sidecarproxycache.ProxyConfigurationCache, computedRoutesCache *sidecarproxycache.ComputedRoutesCache, + iCache *sidecarproxycache.IdentitiesCache, ) *Fetcher { return &Fetcher{ Client: client, DestinationsCache: dCache, ProxyCfgCache: pcfgCache, ComputedRoutesCache: computedRoutesCache, + IdentitiesCache: iCache, } } func (f *Fetcher) FetchWorkload(ctx context.Context, id *pbresource.ID) (*types.DecodedWorkload, error) { + proxyID := resource.ReplaceType(types.ProxyStateTemplateType, id) dec, err := resource.GetDecodedResource[*pbcatalog.Workload](ctx, f.Client, id) if err != nil { return nil, err } else if dec == nil { // We also need to make sure to delete the associated proxy from cache. // We are ignoring errors from cache here as this deletion is best effort. - proxyID := resource.ReplaceType(types.ProxyStateTemplateType, id) f.DestinationsCache.DeleteSourceProxy(proxyID) f.ProxyCfgCache.UntrackProxyID(proxyID) + f.IdentitiesCache.UntrackProxyID(proxyID) return nil, nil } + identityID := &pbresource.ID{ + Name: dec.Data.Identity, + Tenancy: dec.Resource.Id.Tenancy, + Type: auth.WorkloadIdentityType, + } + + f.IdentitiesCache.TrackPair(identityID, proxyID) + return dec, err } @@ -65,6 +79,10 @@ func (f *Fetcher) FetchProxyStateTemplate(ctx context.Context, id *pbresource.ID return resource.GetDecodedResource[*pbmesh.ProxyStateTemplate](ctx, f.Client, id) } +func (f *Fetcher) FetchComputedTrafficPermissions(ctx context.Context, id *pbresource.ID) (*types.DecodedComputedTrafficPermissions, error) { + return resource.GetDecodedResource[*pbauth.ComputedTrafficPermissions](ctx, f.Client, id) +} + func (f *Fetcher) FetchServiceEndpoints(ctx context.Context, id *pbresource.ID) (*types.DecodedServiceEndpoints, error) { return resource.GetDecodedResource[*pbcatalog.ServiceEndpoints](ctx, f.Client, id) } diff --git a/internal/mesh/internal/controllers/sidecarproxy/fetcher/data_fetcher_test.go b/internal/mesh/internal/controllers/sidecarproxy/fetcher/data_fetcher_test.go index adbf431e148b..fb73b93c2a3a 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/fetcher/data_fetcher_test.go +++ b/internal/mesh/internal/controllers/sidecarproxy/fetcher/data_fetcher_test.go @@ -13,6 +13,7 @@ import ( "google.golang.org/grpc/status" svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing" + "github.com/hashicorp/consul/internal/auth" "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/mesh/internal/cache/sidecarproxycache" @@ -238,16 +239,28 @@ func (suite *dataFetcherSuite) SetupTest() { } func (suite *dataFetcherSuite) TestFetcher_FetchWorkload_WorkloadNotFound() { - // Test that when workload is not found, we remove it from cache. - - proxyID := resourcetest.Resource(types.ProxyStateTemplateType, "service-workload-abc").ID() + proxyID := resourcetest.Resource(types.ProxyStateTemplateType, "service-workload-abc"). + WithTenancy(resource.DefaultNamespacedTenancy()). + ID() + identityID := resourcetest.Resource(auth.WorkloadIdentityType, "workload-identity-abc").ID() // Create cache and pre-populate it. var ( destCache = sidecarproxycache.NewDestinationsCache() proxyCfgCache = sidecarproxycache.NewProxyConfigurationCache() computedRoutesCache = sidecarproxycache.NewComputedRoutesCache() + identitiesCache = sidecarproxycache.NewIdentitiesCache() ) + + f := Fetcher{ + DestinationsCache: destCache, + ProxyCfgCache: proxyCfgCache, + ComputedRoutesCache: computedRoutesCache, + IdentitiesCache: identitiesCache, + Client: suite.client, + } + + // Prepopulate the cache. dest1 := intermediate.CombinedDestinationRef{ ServiceRef: resourcetest.Resource(catalog.ServiceType, "test-service-1").ReferenceNoSection(), Port: "tcp", @@ -264,21 +277,45 @@ func (suite *dataFetcherSuite) TestFetcher_FetchWorkload_WorkloadNotFound() { resource.NewReferenceKey(proxyID): {}, }, } + destCache.WriteDestination(dest1) destCache.WriteDestination(dest2) suite.syncDestinations(dest1, dest2) + workload := resourcetest.Resource(catalog.WorkloadType, "service-workload-abc"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(suite.T(), &pbcatalog.Workload{ + Identity: identityID.Name, + Ports: map[string]*pbcatalog.WorkloadPort{ + "foo": {Port: 8080, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP}, + }, + Addresses: []*pbcatalog.WorkloadAddress{ + { + Host: "10.0.0.1", + Ports: []string{"foo"}, + }, + }, + }).Write(suite.T(), suite.client) + + // Track the workload's identity + _, err := f.FetchWorkload(context.Background(), workload.Id) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), destCache.DestinationsBySourceProxy(proxyID)) + require.Nil(suite.T(), proxyCfgCache.ProxyConfigurationsByProxyID(proxyID)) + require.Nil(suite.T(), proxyCfgCache.ProxyConfigurationsByProxyID(proxyID)) + require.Equal(suite.T(), []*pbresource.ID{proxyID}, identitiesCache.ProxyIDsByWorkloadIdentity(identityID)) + proxyCfgID := resourcetest.Resource(types.ProxyConfigurationType, "proxy-config").ID() proxyCfgCache.TrackProxyConfiguration(proxyCfgID, []resource.ReferenceOrID{proxyID}) - f := New(suite.client, destCache, proxyCfgCache, computedRoutesCache) - - _, err := f.FetchWorkload(context.Background(), proxyID) + _, err = f.FetchWorkload(context.Background(), proxyID) require.NoError(suite.T(), err) // Check that cache is updated to remove proxy id. require.Nil(suite.T(), destCache.DestinationsBySourceProxy(proxyID)) require.Nil(suite.T(), proxyCfgCache.ProxyConfigurationsByProxyID(proxyID)) + require.Nil(suite.T(), proxyCfgCache.ProxyConfigurationsByProxyID(proxyID)) + require.Nil(suite.T(), identitiesCache.ProxyIDsByWorkloadIdentity(identityID)) } func (suite *dataFetcherSuite) TestFetcher_NotFound() { diff --git a/internal/mesh/internal/mappers/sidecarproxymapper/mapper.go b/internal/mesh/internal/mappers/sidecarproxymapper/mapper.go index 1faf74ebb94a..23b762ff59cd 100644 --- a/internal/mesh/internal/mappers/sidecarproxymapper/mapper.go +++ b/internal/mesh/internal/mappers/sidecarproxymapper/mapper.go @@ -19,17 +19,20 @@ type Mapper struct { destinationsCache *sidecarproxycache.DestinationsCache proxyCfgCache *sidecarproxycache.ProxyConfigurationCache computedRoutesCache *sidecarproxycache.ComputedRoutesCache + identitiesCache *sidecarproxycache.IdentitiesCache } func New( destinationsCache *sidecarproxycache.DestinationsCache, proxyCfgCache *sidecarproxycache.ProxyConfigurationCache, computedRoutesCache *sidecarproxycache.ComputedRoutesCache, + identitiesCache *sidecarproxycache.IdentitiesCache, ) *Mapper { return &Mapper{ destinationsCache: destinationsCache, proxyCfgCache: proxyCfgCache, computedRoutesCache: computedRoutesCache, + identitiesCache: identitiesCache, } } diff --git a/internal/mesh/internal/mappers/sidecarproxymapper/traffic_permissions_mapper.go b/internal/mesh/internal/mappers/sidecarproxymapper/traffic_permissions_mapper.go new file mode 100644 index 000000000000..5f1010811eab --- /dev/null +++ b/internal/mesh/internal/mappers/sidecarproxymapper/traffic_permissions_mapper.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package sidecarproxymapper + +import ( + "context" + + "github.com/hashicorp/consul/internal/auth" + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + pbauth "github.com/hashicorp/consul/proto-public/pbauth/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" +) + +func (m *Mapper) MapComputedTrafficPermissionsToProxyStateTemplate(ctx context.Context, rt controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) { + var ctp pbauth.ComputedTrafficPermissions + err := res.Data.UnmarshalTo(&ctp) + if err != nil { + return nil, err + } + + pid := resource.ReplaceType(auth.WorkloadIdentityType, res.Id) + ids := m.identitiesCache.ProxyIDsByWorkloadIdentity(pid) + + requests := make([]controller.Request, 0, len(ids)) + for _, id := range ids { + requests = append(requests, controller.Request{ + ID: resource.ReplaceType(types.ProxyStateTemplateType, id)}, + ) + } + + return requests, nil +} diff --git a/internal/mesh/internal/mappers/sidecarproxymapper/traffic_permissions_mapper_test.go b/internal/mesh/internal/mappers/sidecarproxymapper/traffic_permissions_mapper_test.go new file mode 100644 index 000000000000..e5e87e6f1179 --- /dev/null +++ b/internal/mesh/internal/mappers/sidecarproxymapper/traffic_permissions_mapper_test.go @@ -0,0 +1,63 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package sidecarproxymapper + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing" + "github.com/hashicorp/consul/internal/auth" + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/controller" + "github.com/hashicorp/consul/internal/mesh/internal/cache/sidecarproxycache" + "github.com/hashicorp/consul/internal/mesh/internal/types" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" + pbauth "github.com/hashicorp/consul/proto-public/pbauth/v1alpha1" + "github.com/hashicorp/consul/proto/private/prototest" +) + +func TestMapComputedTrafficPermissionsToProxyStateTemplate(t *testing.T) { + client := svctest.RunResourceService(t, types.Register, catalog.RegisterTypes) + ctp := resourcetest.Resource(auth.ComputedTrafficPermissionsType, "workload-identity-1"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(t, &pbauth.ComputedTrafficPermissions{}). + Build() + + i := sidecarproxycache.NewIdentitiesCache() + mapper := &Mapper{identitiesCache: i} + + // Empty results when the cache isn't populated. + requests, err := mapper.MapComputedTrafficPermissionsToProxyStateTemplate(context.Background(), controller.Runtime{Client: client}, ctp) + require.NoError(t, err) + require.Len(t, requests, 0) + + identityID1 := resourcetest.Resource(auth.WorkloadIdentityType, "workload-identity-1"). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + + proxyID1 := resourcetest.Resource(types.ProxyStateTemplateType, "service-workload-1"). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + proxyID2 := resourcetest.Resource(types.ProxyStateTemplateType, "service-workload-2"). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + + i.TrackPair(identityID1, proxyID1) + + // Empty results when the cache isn't populated. + requests, err = mapper.MapComputedTrafficPermissionsToProxyStateTemplate(context.Background(), controller.Runtime{Client: client}, ctp) + require.NoError(t, err) + prototest.AssertElementsMatch(t, []controller.Request{{ID: proxyID1}}, requests) + + i.TrackPair(identityID1, proxyID2) + + // Empty results when the cache isn't populated. + requests, err = mapper.MapComputedTrafficPermissionsToProxyStateTemplate(context.Background(), controller.Runtime{Client: client}, ctp) + require.NoError(t, err) + prototest.AssertElementsMatch(t, []controller.Request{ + {ID: proxyID1}, + {ID: proxyID2}, + }, requests) +} diff --git a/internal/mesh/internal/mappers/sidecarproxymapper/unified_mapper_test.go b/internal/mesh/internal/mappers/sidecarproxymapper/unified_mapper_test.go index e0a37a635815..b0b1cd465d54 100644 --- a/internal/mesh/internal/mappers/sidecarproxymapper/unified_mapper_test.go +++ b/internal/mesh/internal/mappers/sidecarproxymapper/unified_mapper_test.go @@ -29,7 +29,7 @@ func TestUnified_AllMappingsToProxyStateTemplate(t *testing.T) { destCache = sidecarproxycache.NewDestinationsCache() // proxyCfgCache = sidecarproxycache.NewProxyConfigurationCache() routesCache = sidecarproxycache.NewComputedRoutesCache() - mapper = New(destCache, nil, routesCache) + mapper = New(destCache, nil, routesCache, nil) client = svctest.RunResourceService(t, types.Register, catalog.RegisterTypes) ) diff --git a/internal/mesh/internal/types/decoded.go b/internal/mesh/internal/types/decoded.go index 50fb4d04b496..2e89fef7b5e6 100644 --- a/internal/mesh/internal/types/decoded.go +++ b/internal/mesh/internal/types/decoded.go @@ -5,21 +5,23 @@ package types import ( "github.com/hashicorp/consul/internal/resource" + pbauth "github.com/hashicorp/consul/proto-public/pbauth/v1alpha1" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" ) type ( - DecodedHTTPRoute = resource.DecodedResource[*pbmesh.HTTPRoute] - DecodedGRPCRoute = resource.DecodedResource[*pbmesh.GRPCRoute] - DecodedTCPRoute = resource.DecodedResource[*pbmesh.TCPRoute] - DecodedDestinationPolicy = resource.DecodedResource[*pbmesh.DestinationPolicy] - DecodedComputedRoutes = resource.DecodedResource[*pbmesh.ComputedRoutes] - DecodedFailoverPolicy = resource.DecodedResource[*pbcatalog.FailoverPolicy] - DecodedService = resource.DecodedResource[*pbcatalog.Service] - DecodedServiceEndpoints = resource.DecodedResource[*pbcatalog.ServiceEndpoints] - DecodedWorkload = resource.DecodedResource[*pbcatalog.Workload] - DecodedProxyConfiguration = resource.DecodedResource[*pbmesh.ProxyConfiguration] - DecodedDestinations = resource.DecodedResource[*pbmesh.Upstreams] - DecodedProxyStateTemplate = resource.DecodedResource[*pbmesh.ProxyStateTemplate] + DecodedHTTPRoute = resource.DecodedResource[*pbmesh.HTTPRoute] + DecodedGRPCRoute = resource.DecodedResource[*pbmesh.GRPCRoute] + DecodedTCPRoute = resource.DecodedResource[*pbmesh.TCPRoute] + DecodedDestinationPolicy = resource.DecodedResource[*pbmesh.DestinationPolicy] + DecodedComputedRoutes = resource.DecodedResource[*pbmesh.ComputedRoutes] + DecodedComputedTrafficPermissions = resource.DecodedResource[*pbauth.ComputedTrafficPermissions] + DecodedFailoverPolicy = resource.DecodedResource[*pbcatalog.FailoverPolicy] + DecodedService = resource.DecodedResource[*pbcatalog.Service] + DecodedServiceEndpoints = resource.DecodedResource[*pbcatalog.ServiceEndpoints] + DecodedWorkload = resource.DecodedResource[*pbcatalog.Workload] + DecodedProxyConfiguration = resource.DecodedResource[*pbmesh.ProxyConfiguration] + DecodedDestinations = resource.DecodedResource[*pbmesh.Upstreams] + DecodedProxyStateTemplate = resource.DecodedResource[*pbmesh.ProxyStateTemplate] ) diff --git a/proto-public/pbauth/v1alpha1/traffic_permissions_addon.go b/proto-public/pbauth/v1alpha1/traffic_permissions_addon.go new file mode 100644 index 000000000000..402cc83ee023 --- /dev/null +++ b/proto-public/pbauth/v1alpha1/traffic_permissions_addon.go @@ -0,0 +1,14 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package authv1alpha1 + +type SourceToSpiffe interface { + GetIdentityName() string + GetPartition() string + GetNamespace() string + GetPeer() string +} + +var _ SourceToSpiffe = (*Source)(nil) +var _ SourceToSpiffe = (*ExcludeSource)(nil) diff --git a/proto-public/pbcatalog/v1alpha1/workload_addon.go b/proto-public/pbcatalog/v1alpha1/workload_addon.go index e26bca3af722..ac62b2cb0c81 100644 --- a/proto-public/pbcatalog/v1alpha1/workload_addon.go +++ b/proto-public/pbcatalog/v1alpha1/workload_addon.go @@ -38,3 +38,16 @@ func (w *Workload) GetNonExternalAddressesForPort(portName string) []*WorkloadAd return addresses } + +func (w *Workload) GetPortsByProtocol() map[Protocol][]string { + if w == nil { + return nil + } + + out := make(map[Protocol][]string, len(w.Ports)) + for name, port := range w.Ports { + out[port.GetProtocol()] = append(out[port.GetProtocol()], name) + } + + return out +} diff --git a/proto-public/pbmesh/v1alpha1/pbproxystate/listener.pb.go b/proto-public/pbmesh/v1alpha1/pbproxystate/listener.pb.go index b50d47d74600..9ee5bc6167d8 100644 --- a/proto-public/pbmesh/v1alpha1/pbproxystate/listener.pb.go +++ b/proto-public/pbmesh/v1alpha1/pbproxystate/listener.pb.go @@ -645,7 +645,7 @@ type L4Destination struct { // stat_prefix is for compatibility with v1 xds configuration, so it is generated in exactly the same way. StatPrefix string `protobuf:"bytes,3,opt,name=stat_prefix,json=statPrefix,proto3" json:"stat_prefix,omitempty"` // traffic_permissions is a list of traffic permissions for this destination. - TrafficPermissions *L4TrafficPermissions `protobuf:"bytes,4,opt,name=traffic_permissions,json=trafficPermissions,proto3" json:"traffic_permissions,omitempty"` + TrafficPermissions *TrafficPermissions `protobuf:"bytes,4,opt,name=traffic_permissions,json=trafficPermissions,proto3" json:"traffic_permissions,omitempty"` // max_inbound_connections specifies how many connections this destination can accept. MaxInboundConnections uint64 `protobuf:"varint,5,opt,name=max_inbound_connections,json=maxInboundConnections,proto3" json:"max_inbound_connections,omitempty"` } @@ -710,7 +710,7 @@ func (x *L4Destination) GetStatPrefix() string { return "" } -func (x *L4Destination) GetTrafficPermissions() *L4TrafficPermissions { +func (x *L4Destination) GetTrafficPermissions() *TrafficPermissions { if x != nil { return x.TrafficPermissions } @@ -752,7 +752,7 @@ type L7Destination struct { // protocol for the destination. Protocol L7Protocol `protobuf:"varint,3,opt,name=protocol,proto3,enum=hashicorp.consul.mesh.v1alpha1.pbproxystate.L7Protocol" json:"protocol,omitempty"` // traffic_permissions is a list of intentions for this destination. - TrafficPermissions *L7TrafficPermissions `protobuf:"bytes,4,opt,name=traffic_permissions,json=trafficPermissions,proto3" json:"traffic_permissions,omitempty"` + TrafficPermissions *TrafficPermissions `protobuf:"bytes,4,opt,name=traffic_permissions,json=trafficPermissions,proto3" json:"traffic_permissions,omitempty"` // include_xfcc specifies whether to add xfcc header. IncludeXfcc bool `protobuf:"varint,5,opt,name=include_xfcc,json=includeXfcc,proto3" json:"include_xfcc,omitempty"` // static_route specifies whether this is a static route that is inlined in the listener filter. This is required to @@ -815,7 +815,7 @@ func (x *L7Destination) GetProtocol() L7Protocol { return L7Protocol_L7_PROTOCOL_HTTP } -func (x *L7Destination) GetTrafficPermissions() *L7TrafficPermissions { +func (x *L7Destination) GetTrafficPermissions() *TrafficPermissions { if x != nil { return x.TrafficPermissions } @@ -1021,7 +1021,7 @@ var file_pbmesh_v1alpha1_pbproxystate_listener_proto_rawDesc = []byte{ 0x6c, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, - 0x65, 0x6e, 0x22, 0xbc, 0x03, 0x0a, 0x0d, 0x4c, 0x34, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x65, 0x6e, 0x22, 0xba, 0x03, 0x0a, 0x0d, 0x4c, 0x34, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5b, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, @@ -1037,92 +1037,92 @@ var file_pbmesh_v1alpha1_pbproxystate_listener_proto_rawDesc = []byte{ 0x70, 0x48, 0x00, 0x52, 0x10, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, - 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x72, 0x0a, 0x13, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, + 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x70, 0x0a, 0x13, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x2e, 0x4c, 0x34, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x61, - 0x78, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x6d, 0x61, 0x78, - 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x42, 0x0d, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0x8b, 0x03, 0x0a, 0x0d, 0x4c, 0x37, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x5f, - 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, - 0x61, 0x74, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x53, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x37, 0x2e, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, - 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4c, 0x37, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x72, 0x0a, - 0x13, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, - 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4c, 0x37, 0x54, 0x72, 0x61, 0x66, 0x66, - 0x69, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x74, - 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x78, 0x66, 0x63, - 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x58, 0x66, 0x63, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x5f, 0x69, - 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x49, 0x6e, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0x31, 0x0a, 0x0e, 0x53, 0x4e, 0x49, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x50, 0x72, 0x65, 0x66, - 0x69, 0x78, 0x2a, 0x55, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x19, 0x0a, 0x15, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x49, - 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x10, - 0x01, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, - 0x55, 0x54, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x2a, 0x54, 0x0a, 0x12, 0x42, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x1f, 0x0a, 0x1b, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, - 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, - 0x12, 0x1d, 0x0a, 0x19, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, - 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x45, 0x58, 0x41, 0x43, 0x54, 0x10, 0x01, 0x2a, - 0x71, 0x0a, 0x0a, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, - 0x16, 0x43, 0x41, 0x50, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x54, 0x52, 0x41, 0x4e, - 0x53, 0x50, 0x41, 0x52, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x43, 0x41, 0x50, - 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x4c, 0x37, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, - 0x43, 0x4f, 0x4c, 0x5f, 0x49, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, - 0x12, 0x20, 0x0a, 0x1c, 0x43, 0x41, 0x50, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x4c, - 0x34, 0x5f, 0x54, 0x4c, 0x53, 0x5f, 0x49, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, - 0x10, 0x02, 0x2a, 0x4f, 0x0a, 0x0a, 0x4c, 0x37, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x37, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, - 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x37, 0x5f, 0x50, 0x52, 0x4f, - 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x32, 0x10, 0x01, 0x12, 0x14, 0x0a, - 0x10, 0x4c, 0x37, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x47, 0x52, 0x50, - 0x43, 0x10, 0x02, 0x42, 0xd9, 0x02, 0x0a, 0x2f, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x42, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, - 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x45, 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, 0x2d, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x2f, 0x70, 0x62, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2f, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xa2, - 0x02, 0x05, 0x48, 0x43, 0x4d, 0x56, 0x50, 0xaa, 0x02, 0x2b, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, - 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x73, 0x74, 0x61, 0x74, 0x65, 0xca, 0x02, 0x2b, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, - 0x61, 0x74, 0x65, 0xe2, 0x02, 0x37, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, - 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x5c, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x2f, - 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, - 0x6c, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x3a, 0x3a, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x5f, + 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x49, 0x6e, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x42, 0x0d, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x89, 0x03, 0x0a, 0x0d, 0x4c, 0x37, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x70, 0x72, + 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x53, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4c, 0x37, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x70, 0x0a, 0x13, 0x74, + 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x74, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x78, 0x66, 0x63, 0x63, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x58, 0x66, 0x63, 0x63, + 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x31, 0x0a, 0x0e, 0x53, + 0x4e, 0x49, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x2a, 0x55, + 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x15, 0x44, + 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x01, 0x12, 0x16, 0x0a, + 0x12, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x55, 0x54, 0x42, 0x4f, + 0x55, 0x4e, 0x44, 0x10, 0x02, 0x2a, 0x54, 0x0a, 0x12, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x42, + 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, + 0x4e, 0x53, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, + 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, + 0x4f, 0x4e, 0x53, 0x5f, 0x45, 0x58, 0x41, 0x43, 0x54, 0x10, 0x01, 0x2a, 0x71, 0x0a, 0x0a, 0x43, + 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x41, 0x50, + 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x50, 0x41, 0x52, + 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x43, 0x41, 0x50, 0x41, 0x42, 0x49, 0x4c, + 0x49, 0x54, 0x59, 0x5f, 0x4c, 0x37, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, + 0x49, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, + 0x43, 0x41, 0x50, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x4c, 0x34, 0x5f, 0x54, 0x4c, + 0x53, 0x5f, 0x49, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x2a, 0x4f, + 0x0a, 0x0a, 0x4c, 0x37, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x14, 0x0a, 0x10, + 0x4c, 0x37, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x48, 0x54, 0x54, 0x50, + 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x37, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, + 0x4c, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x32, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x37, 0x5f, + 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x47, 0x52, 0x50, 0x43, 0x10, 0x02, 0x42, + 0xd9, 0x02, 0x0a, 0x2f, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x42, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x45, 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, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, + 0x62, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x70, + 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xa2, 0x02, 0x05, 0x48, 0x43, + 0x4d, 0x56, 0x50, 0xaa, 0x02, 0x2b, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, 0x56, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, + 0x65, 0xca, 0x02, 0x2b, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x5c, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xe2, + 0x02, 0x37, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, + 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x5c, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5c, 0x47, 0x50, + 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x2f, 0x48, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x4d, + 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3a, 0x3a, 0x50, + 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -1157,8 +1157,7 @@ var file_pbmesh_v1alpha1_pbproxystate_listener_proto_goTypes = []interface{}{ (*wrapperspb.UInt32Value)(nil), // 14: google.protobuf.UInt32Value (*DestinationCluster)(nil), // 15: hashicorp.consul.mesh.v1alpha1.pbproxystate.DestinationCluster (*L4WeightedClusterGroup)(nil), // 16: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4WeightedClusterGroup - (*L4TrafficPermissions)(nil), // 17: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4TrafficPermissions - (*L7TrafficPermissions)(nil), // 18: hashicorp.consul.mesh.v1alpha1.pbproxystate.L7TrafficPermissions + (*TrafficPermissions)(nil), // 17: hashicorp.consul.mesh.v1alpha1.pbproxystate.TrafficPermissions } var file_pbmesh_v1alpha1_pbproxystate_listener_proto_depIdxs = []int32{ 0, // 0: hashicorp.consul.mesh.v1alpha1.pbproxystate.Listener.direction:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.Direction @@ -1179,9 +1178,9 @@ var file_pbmesh_v1alpha1_pbproxystate_listener_proto_depIdxs = []int32{ 14, // 15: hashicorp.consul.mesh.v1alpha1.pbproxystate.CidrRange.prefix_len:type_name -> google.protobuf.UInt32Value 15, // 16: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Destination.cluster:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.DestinationCluster 16, // 17: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Destination.weighted_clusters:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.L4WeightedClusterGroup - 17, // 18: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Destination.traffic_permissions:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.L4TrafficPermissions + 17, // 18: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Destination.traffic_permissions:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.TrafficPermissions 3, // 19: hashicorp.consul.mesh.v1alpha1.pbproxystate.L7Destination.protocol:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.L7Protocol - 18, // 20: hashicorp.consul.mesh.v1alpha1.pbproxystate.L7Destination.traffic_permissions:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.L7TrafficPermissions + 17, // 20: hashicorp.consul.mesh.v1alpha1.pbproxystate.L7Destination.traffic_permissions:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.TrafficPermissions 21, // [21:21] is the sub-list for method output_type 21, // [21:21] is the sub-list for method input_type 21, // [21:21] is the sub-list for extension type_name diff --git a/proto-public/pbmesh/v1alpha1/pbproxystate/listener.proto b/proto-public/pbmesh/v1alpha1/pbproxystate/listener.proto index 0582197638f0..e1ee56cafb17 100644 --- a/proto-public/pbmesh/v1alpha1/pbproxystate/listener.proto +++ b/proto-public/pbmesh/v1alpha1/pbproxystate/listener.proto @@ -97,7 +97,7 @@ message L4Destination { // stat_prefix is for compatibility with v1 xds configuration, so it is generated in exactly the same way. string stat_prefix = 3; // traffic_permissions is a list of traffic permissions for this destination. - L4TrafficPermissions traffic_permissions = 4; + TrafficPermissions traffic_permissions = 4; // max_inbound_connections specifies how many connections this destination can accept. uint64 max_inbound_connections = 5; } @@ -110,7 +110,7 @@ message L7Destination { // protocol for the destination. L7Protocol protocol = 3; // traffic_permissions is a list of intentions for this destination. - L7TrafficPermissions traffic_permissions = 4; + TrafficPermissions traffic_permissions = 4; // include_xfcc specifies whether to add xfcc header. bool include_xfcc = 5; // static_route specifies whether this is a static route that is inlined in the listener filter. This is required to diff --git a/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.go b/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.go deleted file mode 100644 index 299a23652267..000000000000 --- a/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package pbproxystate - -func (s *L4Principal) ToL7Principal() *L7Principal { - out := &L7Principal{ - Spiffe: &Spiffe{ - Regex: s.SpiffeRegex, - }, - } - - for _, regex := range s.ExcludeSpiffeRegexes { - out.ExcludeSpiffes = append(out.ExcludeSpiffes, &Spiffe{ - Regex: regex, - }) - } - - return out -} diff --git a/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.pb.binary.go b/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.pb.binary.go index e34686a6cfe1..9c9e70a29f99 100644 --- a/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.pb.binary.go +++ b/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.pb.binary.go @@ -8,52 +8,32 @@ import ( ) // MarshalBinary implements encoding.BinaryMarshaler -func (msg *L7TrafficPermissions) MarshalBinary() ([]byte, error) { +func (msg *TrafficPermissions) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *L7TrafficPermissions) UnmarshalBinary(b []byte) error { +func (msg *TrafficPermissions) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *L4TrafficPermissions) MarshalBinary() ([]byte, error) { +func (msg *Permission) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *L4TrafficPermissions) UnmarshalBinary(b []byte) error { +func (msg *Permission) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } // MarshalBinary implements encoding.BinaryMarshaler -func (msg *L4Permission) MarshalBinary() ([]byte, error) { +func (msg *Principal) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) } // UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *L4Permission) UnmarshalBinary(b []byte) error { - return proto.Unmarshal(b, msg) -} - -// MarshalBinary implements encoding.BinaryMarshaler -func (msg *L4Principal) MarshalBinary() ([]byte, error) { - return proto.Marshal(msg) -} - -// UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *L4Principal) UnmarshalBinary(b []byte) error { - return proto.Unmarshal(b, msg) -} - -// MarshalBinary implements encoding.BinaryMarshaler -func (msg *L7Principal) MarshalBinary() ([]byte, error) { - return proto.Marshal(msg) -} - -// UnmarshalBinary implements encoding.BinaryUnmarshaler -func (msg *L7Principal) UnmarshalBinary(b []byte) error { +func (msg *Principal) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } diff --git a/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.pb.go b/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.pb.go index c2bc8a720958..46ebdc675232 100644 --- a/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.pb.go +++ b/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.pb.go @@ -23,14 +23,17 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type L7TrafficPermissions struct { +type TrafficPermissions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + AllowPermissions []*Permission `protobuf:"bytes,1,rep,name=allow_permissions,json=allowPermissions,proto3" json:"allow_permissions,omitempty"` + DenyPermissions []*Permission `protobuf:"bytes,2,rep,name=deny_permissions,json=denyPermissions,proto3" json:"deny_permissions,omitempty"` } -func (x *L7TrafficPermissions) Reset() { - *x = L7TrafficPermissions{} +func (x *TrafficPermissions) Reset() { + *x = TrafficPermissions{} if protoimpl.UnsafeEnabled { mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -38,13 +41,13 @@ func (x *L7TrafficPermissions) Reset() { } } -func (x *L7TrafficPermissions) String() string { +func (x *TrafficPermissions) String() string { return protoimpl.X.MessageStringOf(x) } -func (*L7TrafficPermissions) ProtoMessage() {} +func (*TrafficPermissions) ProtoMessage() {} -func (x *L7TrafficPermissions) ProtoReflect() protoreflect.Message { +func (x *TrafficPermissions) ProtoReflect() protoreflect.Message { mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -56,91 +59,50 @@ func (x *L7TrafficPermissions) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use L7TrafficPermissions.ProtoReflect.Descriptor instead. -func (*L7TrafficPermissions) Descriptor() ([]byte, []int) { +// Deprecated: Use TrafficPermissions.ProtoReflect.Descriptor instead. +func (*TrafficPermissions) Descriptor() ([]byte, []int) { return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{0} } -type L4TrafficPermissions struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - AllowPermissions []*L4Permission `protobuf:"bytes,1,rep,name=allow_permissions,json=allowPermissions,proto3" json:"allow_permissions,omitempty"` - DenyPermissions []*L4Permission `protobuf:"bytes,2,rep,name=deny_permissions,json=denyPermissions,proto3" json:"deny_permissions,omitempty"` -} - -func (x *L4TrafficPermissions) Reset() { - *x = L4TrafficPermissions{} - if protoimpl.UnsafeEnabled { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *L4TrafficPermissions) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L4TrafficPermissions) ProtoMessage() {} - -func (x *L4TrafficPermissions) ProtoReflect() protoreflect.Message { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L4TrafficPermissions.ProtoReflect.Descriptor instead. -func (*L4TrafficPermissions) Descriptor() ([]byte, []int) { - return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{1} -} - -func (x *L4TrafficPermissions) GetAllowPermissions() []*L4Permission { +func (x *TrafficPermissions) GetAllowPermissions() []*Permission { if x != nil { return x.AllowPermissions } return nil } -func (x *L4TrafficPermissions) GetDenyPermissions() []*L4Permission { +func (x *TrafficPermissions) GetDenyPermissions() []*Permission { if x != nil { return x.DenyPermissions } return nil } -type L4Permission struct { +type Permission struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Principals []*L4Principal `protobuf:"bytes,1,rep,name=principals,proto3" json:"principals,omitempty"` + Principals []*Principal `protobuf:"bytes,1,rep,name=principals,proto3" json:"principals,omitempty"` } -func (x *L4Permission) Reset() { - *x = L4Permission{} +func (x *Permission) Reset() { + *x = Permission{} if protoimpl.UnsafeEnabled { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[2] + mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *L4Permission) String() string { +func (x *Permission) String() string { return protoimpl.X.MessageStringOf(x) } -func (*L4Permission) ProtoMessage() {} +func (*Permission) ProtoMessage() {} -func (x *L4Permission) ProtoReflect() protoreflect.Message { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[2] +func (x *Permission) ProtoReflect() protoreflect.Message { + mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -151,75 +113,19 @@ func (x *L4Permission) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use L4Permission.ProtoReflect.Descriptor instead. -func (*L4Permission) Descriptor() ([]byte, []int) { - return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{2} +// Deprecated: Use Permission.ProtoReflect.Descriptor instead. +func (*Permission) Descriptor() ([]byte, []int) { + return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{1} } -func (x *L4Permission) GetPrincipals() []*L4Principal { +func (x *Permission) GetPrincipals() []*Principal { if x != nil { return x.Principals } return nil } -// L4Principal maps into Source. We first convert this to Source before generating Envoy resources. -type L4Principal struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - SpiffeRegex string `protobuf:"bytes,1,opt,name=spiffe_regex,json=spiffeRegex,proto3" json:"spiffe_regex,omitempty"` - ExcludeSpiffeRegexes []string `protobuf:"bytes,2,rep,name=exclude_spiffe_regexes,json=excludeSpiffeRegexes,proto3" json:"exclude_spiffe_regexes,omitempty"` -} - -func (x *L4Principal) Reset() { - *x = L4Principal{} - if protoimpl.UnsafeEnabled { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *L4Principal) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L4Principal) ProtoMessage() {} - -func (x *L4Principal) ProtoReflect() protoreflect.Message { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L4Principal.ProtoReflect.Descriptor instead. -func (*L4Principal) Descriptor() ([]byte, []int) { - return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{3} -} - -func (x *L4Principal) GetSpiffeRegex() string { - if x != nil { - return x.SpiffeRegex - } - return "" -} - -func (x *L4Principal) GetExcludeSpiffeRegexes() []string { - if x != nil { - return x.ExcludeSpiffeRegexes - } - return nil -} - -type L7Principal struct { +type Principal struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -228,23 +134,23 @@ type L7Principal struct { ExcludeSpiffes []*Spiffe `protobuf:"bytes,2,rep,name=exclude_spiffes,json=excludeSpiffes,proto3" json:"exclude_spiffes,omitempty"` } -func (x *L7Principal) Reset() { - *x = L7Principal{} +func (x *Principal) Reset() { + *x = Principal{} if protoimpl.UnsafeEnabled { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[4] + mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *L7Principal) String() string { +func (x *Principal) String() string { return protoimpl.X.MessageStringOf(x) } -func (*L7Principal) ProtoMessage() {} +func (*Principal) ProtoMessage() {} -func (x *L7Principal) ProtoReflect() protoreflect.Message { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[4] +func (x *Principal) ProtoReflect() protoreflect.Message { + mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -255,19 +161,19 @@ func (x *L7Principal) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use L7Principal.ProtoReflect.Descriptor instead. -func (*L7Principal) Descriptor() ([]byte, []int) { - return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{4} +// Deprecated: Use Principal.ProtoReflect.Descriptor instead. +func (*Principal) Descriptor() ([]byte, []int) { + return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{2} } -func (x *L7Principal) GetSpiffe() *Spiffe { +func (x *Principal) GetSpiffe() *Spiffe { if x != nil { return x.Spiffe } return nil } -func (x *L7Principal) GetExcludeSpiffes() []*Spiffe { +func (x *Principal) GetExcludeSpiffes() []*Spiffe { if x != nil { return x.ExcludeSpiffes } @@ -289,7 +195,7 @@ type Spiffe struct { func (x *Spiffe) Reset() { *x = Spiffe{} if protoimpl.UnsafeEnabled { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[5] + mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -302,7 +208,7 @@ func (x *Spiffe) String() string { func (*Spiffe) ProtoMessage() {} func (x *Spiffe) ProtoReflect() protoreflect.Message { - mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[5] + mi := &file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -315,7 +221,7 @@ func (x *Spiffe) ProtoReflect() protoreflect.Message { // Deprecated: Use Spiffe.ProtoReflect.Descriptor instead. func (*Spiffe) Descriptor() ([]byte, []int) { - return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{5} + return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP(), []int{3} } func (x *Spiffe) GetRegex() string { @@ -341,74 +247,66 @@ var file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDesc = []byte 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x2b, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x4c, 0x37, 0x54, 0x72, 0x61, 0x66, 0x66, - 0x69, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe4, 0x01, - 0x0a, 0x14, 0x4c, 0x34, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x66, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, - 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x39, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, - 0x4c, 0x34, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x64, - 0x0a, 0x10, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4c, 0x34, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x64, 0x65, 0x6e, 0x79, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x68, 0x0a, 0x0c, 0x4c, 0x34, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x58, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, - 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4c, 0x34, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, - 0x61, 0x6c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x22, 0x66, - 0x0a, 0x0b, 0x4c, 0x34, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x21, 0x0a, - 0x0c, 0x73, 0x70, 0x69, 0x66, 0x66, 0x65, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x70, 0x69, 0x66, 0x66, 0x65, 0x52, 0x65, 0x67, 0x65, 0x78, - 0x12, 0x34, 0x0a, 0x16, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x73, 0x70, 0x69, 0x66, - 0x66, 0x65, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x14, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x52, - 0x65, 0x67, 0x65, 0x78, 0x65, 0x73, 0x22, 0xb8, 0x01, 0x0a, 0x0b, 0x4c, 0x37, 0x50, 0x72, 0x69, - 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x4b, 0x0a, 0x06, 0x73, 0x70, 0x69, 0x66, 0x66, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x52, 0x06, 0x73, 0x70, 0x69, - 0x66, 0x66, 0x65, 0x12, 0x5c, 0x0a, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x73, - 0x70, 0x69, 0x66, 0x66, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x68, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xde, 0x01, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, + 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x64, 0x0a, 0x11, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x10, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x62, 0x0a, 0x10, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x70, 0x69, 0x66, 0x66, - 0x65, 0x52, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, - 0x73, 0x22, 0x3d, 0x0a, 0x06, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x72, - 0x65, 0x67, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, - 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x78, 0x66, 0x63, 0x63, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x78, 0x66, 0x63, 0x63, 0x52, 0x65, 0x67, 0x65, 0x78, - 0x42, 0xe3, 0x02, 0x0a, 0x2f, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x42, 0x17, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, 0x72, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, - 0x45, 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, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x6d, 0x65, 0x73, 0x68, - 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xa2, 0x02, 0x05, 0x48, 0x43, 0x4d, 0x56, 0x50, 0xaa, 0x02, - 0x2b, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, - 0x6c, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xca, 0x02, 0x2b, 0x48, - 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, - 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, 0x50, 0x62, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xe2, 0x02, 0x37, 0x48, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, - 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, 0x50, 0x62, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x2f, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x68, 0x3a, 0x3a, - 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3a, 0x3a, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x64, 0x65, 0x6e, 0x79, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x64, 0x0a, 0x0a, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x56, 0x0a, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, + 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, + 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x22, 0xb6, 0x01, 0x0a, + 0x09, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x4b, 0x0a, 0x06, 0x73, 0x70, + 0x69, 0x66, 0x66, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, + 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x52, + 0x06, 0x73, 0x70, 0x69, 0x66, 0x66, 0x65, 0x12, 0x5c, 0x0a, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x5f, 0x73, 0x70, 0x69, 0x66, 0x66, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x33, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x53, + 0x70, 0x69, 0x66, 0x66, 0x65, 0x52, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x70, + 0x69, 0x66, 0x66, 0x65, 0x73, 0x22, 0x3d, 0x0a, 0x06, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x78, 0x66, 0x63, 0x63, 0x5f, 0x72, 0x65, + 0x67, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x78, 0x66, 0x63, 0x63, 0x52, + 0x65, 0x67, 0x65, 0x78, 0x42, 0xe3, 0x02, 0x0a, 0x2f, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, + 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x70, 0x62, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x42, 0x17, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, + 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x45, 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, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, + 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x70, 0x62, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xa2, 0x02, 0x05, 0x48, 0x43, 0x4d, + 0x56, 0x50, 0xaa, 0x02, 0x2b, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, + 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, + 0xca, 0x02, 0x2b, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x5c, 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0xe2, 0x02, + 0x37, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, + 0x50, 0x62, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x2f, 0x48, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x4d, 0x65, + 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3a, 0x3a, 0x50, 0x62, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -423,21 +321,19 @@ func file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescGZIP() [ return file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDescData } -var file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_goTypes = []interface{}{ - (*L7TrafficPermissions)(nil), // 0: hashicorp.consul.mesh.v1alpha1.pbproxystate.L7TrafficPermissions - (*L4TrafficPermissions)(nil), // 1: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4TrafficPermissions - (*L4Permission)(nil), // 2: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Permission - (*L4Principal)(nil), // 3: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Principal - (*L7Principal)(nil), // 4: hashicorp.consul.mesh.v1alpha1.pbproxystate.L7Principal - (*Spiffe)(nil), // 5: hashicorp.consul.mesh.v1alpha1.pbproxystate.Spiffe + (*TrafficPermissions)(nil), // 0: hashicorp.consul.mesh.v1alpha1.pbproxystate.TrafficPermissions + (*Permission)(nil), // 1: hashicorp.consul.mesh.v1alpha1.pbproxystate.Permission + (*Principal)(nil), // 2: hashicorp.consul.mesh.v1alpha1.pbproxystate.Principal + (*Spiffe)(nil), // 3: hashicorp.consul.mesh.v1alpha1.pbproxystate.Spiffe } var file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_depIdxs = []int32{ - 2, // 0: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4TrafficPermissions.allow_permissions:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Permission - 2, // 1: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4TrafficPermissions.deny_permissions:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Permission - 3, // 2: hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Permission.principals:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.L4Principal - 5, // 3: hashicorp.consul.mesh.v1alpha1.pbproxystate.L7Principal.spiffe:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.Spiffe - 5, // 4: hashicorp.consul.mesh.v1alpha1.pbproxystate.L7Principal.exclude_spiffes:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.Spiffe + 1, // 0: hashicorp.consul.mesh.v1alpha1.pbproxystate.TrafficPermissions.allow_permissions:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.Permission + 1, // 1: hashicorp.consul.mesh.v1alpha1.pbproxystate.TrafficPermissions.deny_permissions:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.Permission + 2, // 2: hashicorp.consul.mesh.v1alpha1.pbproxystate.Permission.principals:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.Principal + 3, // 3: hashicorp.consul.mesh.v1alpha1.pbproxystate.Principal.spiffe:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.Spiffe + 3, // 4: hashicorp.consul.mesh.v1alpha1.pbproxystate.Principal.exclude_spiffes:type_name -> hashicorp.consul.mesh.v1alpha1.pbproxystate.Spiffe 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name @@ -452,7 +348,7 @@ func file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_init() { } if !protoimpl.UnsafeEnabled { file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*L7TrafficPermissions); i { + switch v := v.(*TrafficPermissions); i { case 0: return &v.state case 1: @@ -464,7 +360,7 @@ func file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_init() { } } file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*L4TrafficPermissions); i { + switch v := v.(*Permission); i { case 0: return &v.state case 1: @@ -476,7 +372,7 @@ func file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_init() { } } file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*L4Permission); i { + switch v := v.(*Principal); i { case 0: return &v.state case 1: @@ -488,30 +384,6 @@ func file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_init() { } } file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*L4Principal); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*L7Principal); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Spiffe); i { case 0: return &v.state @@ -530,7 +402,7 @@ func file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pbmesh_v1alpha1_pbproxystate_traffic_permissions_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.proto b/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.proto index d1397ceebbd1..1c327e93b88e 100644 --- a/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.proto +++ b/proto-public/pbmesh/v1alpha1/pbproxystate/traffic_permissions.proto @@ -5,28 +5,20 @@ syntax = "proto3"; package hashicorp.consul.mesh.v1alpha1.pbproxystate; -message L7TrafficPermissions {} - -message L4TrafficPermissions { - repeated L4Permission allow_permissions = 1; - repeated L4Permission deny_permissions = 2; +message TrafficPermissions { + repeated Permission allow_permissions = 1; + repeated Permission deny_permissions = 2; } -message L4Permission { - repeated L4Principal principals = 1; +message Permission { + repeated Principal principals = 1; - // We don't need destination rules here because they either apply to L7 features or multi-ports. + // We don't need destination rules here yet because they either apply to L7 features or multi-ports. // In the case of multiple ports, the sidecar proxy controller is responsible for filtering // per-port permissions. } -// L4Principal maps into Source. We first convert this to Source before generating Envoy resources. -message L4Principal { - string spiffe_regex = 1; - repeated string exclude_spiffe_regexes = 2; -} - -message L7Principal { +message Principal { Spiffe spiffe = 1; repeated Spiffe exclude_spiffes = 2; } From 850fbda2e94085991358ad593318365c5ef9df3c Mon Sep 17 00:00:00 2001 From: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:37:11 -0700 Subject: [PATCH 04/28] added consul and envoy version constraints (#18726) * added consul and envoy version constraints * fixed Destination configuraiton and added tproxy requirement * Apply suggestions from code review Co-authored-by: Michael Zalimeni --------- Co-authored-by: Michael Zalimeni --- .../config-entries/service-defaults.mdx | 835 +++++++++--------- 1 file changed, 434 insertions(+), 401 deletions(-) diff --git a/website/content/docs/connect/config-entries/service-defaults.mdx b/website/content/docs/connect/config-entries/service-defaults.mdx index 29cc76d97fd9..62fb2c20a0b7 100644 --- a/website/content/docs/connect/config-entries/service-defaults.mdx +++ b/website/content/docs/connect/config-entries/service-defaults.mdx @@ -6,80 +6,83 @@ description: -> --- # Service Defaults Configuration Reference + This topic describes how to configure service defaults configuration entries. The service defaults configuration entry contains common configuration settings for service mesh services, such as upstreams and gateways. Refer to [Define service defaults](/consul/docs/services/usage/define-services#define-service-defaults) for usage information. ## Configuration model -The following outline shows how to format the service defaults configuration entry. Click on a property name to view details about the configuration. +The following list outlines field hierarchy, language-specific data types, requirements, and any applicable default values in service defaults configuration entries. Click on a property name to view additional details. - [`Kind`](#kind): string | required - [`Name`](#name): string | required -- [`Namespace`](#namespace): string -- [`Partition`](#partition): string -- [`Meta`](#meta): map | no default -- [`Protocol`](#protocol): string | default: `tcp` -- [`BalanceInboundConnections`](#balanceinboundconnections): string | no default -- [`Mode`](#mode): string | no default -- [`UpstreamConfig`](#upstreamconfig): map | no default - - [`Overrides`](#upstreamconfig-overrides): map | no default - - [`Name`](#upstreamconfig-overrides-name): string | no default - - [`Namespace`](#upstreamconfig-overrides-namespace): string | no default - - [`Peer`](#upstreamconfig-overrides-peer): string | no default - - [`Protocol`](#upstreamconfig-overrides-protocol): string | no default - - [`ConnectTimeoutMs`](#upstreamconfig-overrides-connecttimeoutms): int | default: `5000` - - [`MeshGateway`](#upstreamconfig-overrides-meshgateway): map | no default - - [`mode`](#upstreamconfig-overrides-meshgateway): string | no default - - [`BalanceOutboundConnections`](#upstreamconfig-overrides-balanceoutboundconnections): string | no default - - [`Limits`](#upstreamconfig-overrides-limits): map | optional - - [`MaxConnections`](#upstreamconfig-overrides-limits): integer | `0` - - [`MaxPendingRequests`](#upstreamconfig-overrides-limits): integer | `0` - - [`MaxConcurrentRequests`](#upstreamconfig-overrides-limits): integer | `0` - - [`PassiveHealthCheck`](#upstreamconfig-overrides-passivehealthcheck): map | optional +- [`Namespace`](#namespace): string | `default` +- [`Partition`](#partition): string | `default` +- [`Meta`](#meta): map +- [`Protocol`](#protocol): string | `tcp` +- [`BalanceInboundConnections`](#balanceinboundconnections): string +- [`Mode`](#mode): string +- [`UpstreamConfig`](#upstreamconfig): map + - [`Overrides`](#upstreamconfig-overrides): map + - [`Name`](#upstreamconfig-overrides-name): string + - [`Namespace`](#upstreamconfig-overrides-namespace): string + - [`Peer`](#upstreamconfig-overrides-peer): string + - [`Protocol`](#upstreamconfig-overrides-protocol): string + - [`ConnectTimeoutMs`](#upstreamconfig-overrides-connecttimeoutms): int | `5000` + - [`MeshGateway`](#upstreamconfig-overrides-meshgateway): map + - [`mode`](#upstreamconfig-overrides-meshgateway): string + - [`BalanceOutboundConnections`](#upstreamconfig-overrides-balanceoutboundconnections): string + - [`Limits`](#upstreamconfig-overrides-limits): map + - [`MaxConnections`](#upstreamconfig-overrides-limits): number | `0` + - [`MaxPendingRequests`](#upstreamconfig-overrides-limits): number | `0` + - [`MaxConcurrentRequests`](#upstreamconfig-overrides-limits): number | `0` + - [`PassiveHealthCheck`](#upstreamconfig-overrides-passivehealthcheck): map - [`Interval`](#upstreamconfig-overrides-passivehealthcheck): string | `0s` - - [`MaxFailures`](#upstreamconfig-overrides-passivehealthcheck): integer | `0` - - [`EnforcingConsecutive5xx`](#upstreamconfig-overrides-passivehealthcheck): integer | `0` - - [`MaxEjectionPercent`](#upstreamconfig-overrides-passivehealthcheck): integer | `0` + - [`MaxFailures`](#upstreamconfig-overrides-passivehealthcheck): number | `0` + - [`EnforcingConsecutive5xx`](#upstreamconfig-overrides-passivehealthcheck): number | `0` + - [`MaxEjectionPercent`](#upstreamconfig-overrides-passivehealthcheck): number | `0` - [`BaseEjectionTime`](#upstreamconfig-overrides-passivehealthcheck): string | `30s` - - [`Defaults`](#upstreamconfig-defaults): map | no default - - [`Protocol`](#upstreamconfig-defaults-protocol): string | no default - - [`ConnectTimeoutMs`](#upstreamconfig-defaults-connecttimeoutms): int | default: `5000` - - [`MeshGateway`](#upstreamconfig-defaults-meshgateway): map | no default - - [`mode`](#upstreamconfig-defaults-meshgateway): string | no default - - [`BalanceOutboundConnections`](#upstreamconfig-defaults-balanceoutboundconnections): string | no default - - [`Limits`](#upstreamconfig-defaults-limits): map | optional - - [`MaxConnections`](#upstreamconfig-defaults-limits): integer | `0` - - [`MaxPendingRequests`](#upstreamconfig-defaults-limits): integer | `0` - - [`MaxConcurrentRequests`](#upstreamconfig-defaults-limits): integer | `0` - - [`PassiveHealthCheck`](#upstreamconfig-defaults-passivehealthcheck): map | optional + - [`Defaults`](#upstreamconfig-defaults): map + - [`Protocol`](#upstreamconfig-defaults-protocol): string + - [`ConnectTimeoutMs`](#upstreamconfig-defaults-connecttimeoutms): int | `5000` + - [`MeshGateway`](#upstreamconfig-defaults-meshgateway): map + - [`mode`](#upstreamconfig-defaults-meshgateway): string + - [`BalanceOutboundConnections`](#upstreamconfig-defaults-balanceoutboundconnections): string + - [`Limits`](#upstreamconfig-defaults-limits): map + - [`MaxConnections`](#upstreamconfig-defaults-limits): number | `0` + - [`MaxPendingRequests`](#upstreamconfig-defaults-limits): number | `0` + - [`MaxConcurrentRequests`](#upstreamconfig-defaults-limits): number | `0` + - [`PassiveHealthCheck`](#upstreamconfig-defaults-passivehealthcheck): map - [`Interval`](#upstreamconfig-defaults-passivehealthcheck): string | `0s` - - [`MaxFailures`](#upstreamconfig-defaults-passivehealthcheck): integer | `0` - - [`EnforcingConsecutive5xx`](#upstreamconfig-defaults-passivehealthcheck): integer | `100` - - [`MaxEjectionPercent`](#upstreamconfig-defaults-passivehealthcheck): integer | `0` + - [`MaxFailures`](#upstreamconfig-defaults-passivehealthcheck): number | `0` + - [`EnforcingConsecutive5xx`](#upstreamconfig-defaults-passivehealthcheck): number | `100` + - [`MaxEjectionPercent`](#upstreamconfig-defaults-passivehealthcheck): number | `0` - [`BaseEjectionTime`](#upstreamconfig-defaults-passivehealthcheck): string | `30s` -- [`TransparentProxy`](#transparentproxy): map | no default - - [`OutboundListenerPort`](#transparentproxy): integer | `15001` +- [`TransparentProxy`](#transparentproxy): map + - [`OutboundListenerPort`](#transparentproxy): number | `15001` - [`DialedDirectly`](#transparentproxy ): boolean | `false` -- [`MutualTLSMode`](#mutualtlsmode): string | `""` -- [`EnvoyExtensions`](#envoyextensions): list | no default - - [`Name`](#envoyextensions): string | `""` - - [`Required`](#envoyextensions): string | `""` - - [`Arguments`](#envoyextensions): map | `nil` -- [`Destination`](#destination): map | no default - - [`Addresses`](#destination): list | no default +- [`MutualTLSMode`](#mutualtlsmode): string +- [`EnvoyExtensions`](#envoyextensions): list + - [`Name`](#envoyextensions): string + - [`Required`](#envoyextensions): string + - [`Arguments`](#envoyextensions): map + - [`ConsulVersion`](#envoyextensions): string + - [`EnvoyVersion`](#envoyextensions): string +- [`Destination`](#destination): map + - [`Addresses`](#destination): list - [`Port`](#destination): integer | `0` -- [`MaxInboundConnections`](#maxinboundconnections): integer | `0` -- [`LocalConnectTimeoutMs`](#localconnecttimeoutms): integer | `0` -- [`LocalRequestTimeoutMs`](#localrequesttimeoutms): integer | `0` -- [`MeshGateway`](#meshgateway): map | no default - - [`Mode`](#meshgateway): string | no default -- [`ExternalSNI`](#externalsni): string | no default -- [`Expose`](#expose): map | no default +- [`MaxInboundConnections`](#maxinboundconnections): number | `0` +- [`LocalConnectTimeoutMs`](#localconnecttimeoutms): number | `0` +- [`LocalRequestTimeoutMs`](#localrequesttimeoutms): number | `0` +- [`MeshGateway`](#meshgateway): map + - [`Mode`](#meshgateway): string +- [`ExternalSNI`](#externalsni): string +- [`Expose`](#expose): map - [`Checks`](#expose-checks): boolean | `false` - - [`Paths`](#expose-paths): list | no default - - [`Path`](#expose-paths): string | no default + - [`Paths`](#expose-paths): list + - [`Path`](#expose-paths): string - [`LocalPathPort`](#expose-paths): integer | `0` - [`ListenerPort`](#expose-paths): integer | `0` - [`Protocol`](#expose-paths): string | `http` @@ -87,74 +90,76 @@ The following outline shows how to format the service defaults configuration ent -- [`apiVersion`](#apiversion): string | must be set to `consul.hashicorp.com/v1alpha1` -- [`kind`](#kind): string | no default -- [`metadata`](#metadata): map | no default - - [`name`](#name): string | no default - - [`namespace`](#namespace): string | no default | -- [`spec`](#spec): map | no default - - [`protocol`](#protocol): string | default: `tcp` - - [`balanceInboundConnections`](#balanceinboundconnections): string | no default - - [`mode`](#mode): string | no default - - [`upstreamConfig`](#upstreamconfig): map | no default - - [`overrides`](#upstreamconfig-overrides): list | no default - - [`name`](#upstreamconfig-overrides-name): string | no default - - [`namespace`](#upstreamconfig-overrides-namespace): string | no default - - [`peer`](#upstreamconfig-overrides-peer): string | no default - - [`protocol`](#upstreamconfig-overrides-protocol): string | no default - - [`connectTimeoutMs`](#upstreamconfig-overrides-connecttimeoutms): int | default: `5000` - - [`meshGateway`](#upstreamconfig-overrides-meshgateway): map | no default - - [`mode`](#upstreamconfig-overrides-meshgateway): string | no default - - [`balanceOutboundConnections`](#overrides-balanceoutboundconnections): string | no default - - [`limits`](#upstreamconfig-overrides-limits): map | optional - - [`maxConnections`](#upstreamconfig-overrides-limits): integer | `0` - - [`maxPendingRequests`](#upstreamconfig-overrides-limits): integer | `0` - - [`maxConcurrentRequests`](#upstreamconfig-overrides-limits): integer | `0` - - [`passiveHealthCheck`](#upstreamconfig-overrides-passivehealthcheck): map | optional +- [`apiVersion`](#apiversion): string | required | must be set to `consul.hashicorp.com/v1alpha1` +- [`kind`](#kind): string +- [`metadata`](#metadata): map + - [`name`](#name): string + - [`namespace`](#namespace): string | `default`` | +- [`spec`](#spec): map + - [`protocol`](#protocol): string | `tcp` + - [`balanceInboundConnections`](#balanceinboundconnections): string + - [`mode`](#mode): string + - [`upstreamConfig`](#upstreamconfig): map + - [`overrides`](#upstreamconfig-overrides): list + - [`name`](#upstreamconfig-overrides-name): string + - [`namespace`](#upstreamconfig-overrides-namespace): string + - [`peer`](#upstreamconfig-overrides-peer): string + - [`protocol`](#upstreamconfig-overrides-protocol): string + - [`connectTimeoutMs`](#upstreamconfig-overrides-connecttimeoutms): number | `5000` + - [`meshGateway`](#upstreamconfig-overrides-meshgateway): map + - [`mode`](#upstreamconfig-overrides-meshgateway): string + - [`balanceOutboundConnections`](#overrides-balanceoutboundconnections): string + - [`limits`](#upstreamconfig-overrides-limits): map + - [`maxConnections`](#upstreamconfig-overrides-limits): number | `0` + - [`maxPendingRequests`](#upstreamconfig-overrides-limits): number | `0` + - [`maxConcurrentRequests`](#upstreamconfig-overrides-limits): number | `0` + - [`passiveHealthCheck`](#upstreamconfig-overrides-passivehealthcheck): map - [`interval`](#upstreamconfig-overrides-passivehealthcheck): string | `0s` - - [`maxFailures`](#upstreamconfig-overrides-passivehealthcheck): integer | `0` - - [`enforcingConsecutive5xx`](#upstreamconfig-overrides-passivehealthcheck): integer | `100` - - [`maxEjectionPercent`](#upstreamconfig-overrides-passivehealthcheck): integer | `10` + - [`maxFailures`](#upstreamconfig-overrides-passivehealthcheck): number | `0` + - [`enforcingConsecutive5xx`](#upstreamconfig-overrides-passivehealthcheck): number | `100` + - [`maxEjectionPercent`](#upstreamconfig-overrides-passivehealthcheck): number | `10` - [`baseEjectionTime`](#upstreamconfig-overrides-passivehealthcheck): string | `30s` - - [`defaults`](#upstreamconfig-defaults): map | no default - - [`protocol`](#upstreamconfig-defaults-protocol): string | no default - - [`connectTimeoutMs`](#upstreamconfig-defaults-connecttimeoutms): int | default: `5000` - - [`meshGateway`](#upstreamconfig-defaults-meshgateway): map | no default - - [`mode`](#upstreamconfig-defaults-meshgateway): string | no default - - [`balanceOutboundConnections`](#upstreamconfig-defaults-balanceoutboundconnections): string | no default - - [`limits`](#upstreamconfig-defaults-limits): map | optional - - [`maxConnections`](#upstreamconfig-defaults-limits): integer | `0` - - [`maxPendingRequests`](#upstreamconfig-defaults-limits): integer | `0` - - [`maxConcurrentRequests`](#upstreamconfig-defaults-limits): integer | `0` - - [`passiveHealthCheck`](#upstreamconfig-defaults-passivehealthcheck): map | optional + - [`defaults`](#upstreamconfig-defaults): map + - [`protocol`](#upstreamconfig-defaults-protocol): string + - [`connectTimeoutMs`](#upstreamconfig-defaults-connecttimeoutms): number | `5000` + - [`meshGateway`](#upstreamconfig-defaults-meshgateway): map + - [`mode`](#upstreamconfig-defaults-meshgateway): string + - [`balanceOutboundConnections`](#upstreamconfig-defaults-balanceoutboundconnections): string + - [`limits`](#upstreamconfig-defaults-limits): map + - [`maxConnections`](#upstreamconfig-defaults-limits): number | `0` + - [`maxPendingRequests`](#upstreamconfig-defaults-limits): number | `0` + - [`maxConcurrentRequests`](#upstreamconfig-defaults-limits): number | `0` + - [`passiveHealthCheck`](#upstreamconfig-defaults-passivehealthcheck): map - [`interval`](#upstreamconfig-defaults-passivehealthcheck): string | `0s` - - [`maxFailures`](#upstreamconfig-defaults-passivehealthcheck): integer | `0` - - [`enforcingConsecutive5xx`](#upstreamconfig-defaults-passivehealthcheck): integer | `100` - - [`maxEjectionPercent`](#upstreamconfig-defaults-passivehealthcheck): integer | `10` + - [`maxFailures`](#upstreamconfig-defaults-passivehealthcheck): number | `0` + - [`enforcingConsecutive5xx`](#upstreamconfig-defaults-passivehealthcheck): number | `100` + - [`maxEjectionPercent`](#upstreamconfig-defaults-passivehealthcheck): number | `10` - [`baseEjectionTime`](#upstreamconfig-defaults-passivehealthcheck): string | `30s` - - [`transparentProxy`](#transparentproxy): map | no default - - [`outboundListenerPort`](#transparentproxy): integer | `15001` + - [`transparentProxy`](#transparentproxy): map + - [`outboundListenerPort`](#transparentproxy): number | `15001` - [`dialedDirectly`](#transparentproxy): boolean | `false` - - [`mutualTLSMode`](#mutualtlsmode): string | `""` - - [`envoyExtensions`](#envoyextensions): list | no default - - [`name`](#envoyextensions): string | `""` - - [`required`](#envoyextensions): string | `""` - - [`arguments`](#envoyextensions): map | `nil` - - [`destination`](#destination): map | no default - - [`addresses`](#destination): list | no default - - [`port`](#destination): integer | `0` - - [`maxInboundConnections`](#maxinboundconnections): integer | `0` - - [`localConnectTimeoutMs`](#localconnecttimeoutms): integer | `0` - - [`localRequestTimeoutMs`](#localrequesttimeoutms): integer | `0` - - [`meshGateway`](#meshgateway): map | no default - - [`mode`](#meshgateway): string | no default - - [`externalSNI`](#externalsni): string | no default - - [`expose`](#expose): map | no default + - [`mutualTLSMode`](#mutualtlsmode): string + - [`envoyExtensions`](#envoyextensions): list + - [`name`](#envoyextensions): string + - [`required`](#envoyextensions): string + - [`arguments`](#envoyextensions): map + - [`consulVersion`](#envoyextensions): string + - [`envoyVersion`](#envoyextensions): string + - [`destination`](#destination): map + - [`addresses`](#destination): list + - [`port`](#destination): number | `0` + - [`maxInboundConnections`](#maxinboundconnections): number | `0` + - [`localConnectTimeoutMs`](#localconnecttimeoutms): number | `0` + - [`localRequestTimeoutMs`](#localrequesttimeoutms): number | `0` + - [`meshGateway`](#meshgateway): map + - [`mode`](#meshgateway): string + - [`externalSNI`](#externalsni): string + - [`expose`](#expose): map - [`checks`](#expose-checks): boolean | `false` - - [`paths`](#expose-paths): list | no default - - [`path`](#expose-paths): string | no default - - [`localPathPort`](#expose-paths): integer | `0` - - [`listenerPort`](#expose-paths): integer | `0` + - [`paths`](#expose-paths): list + - [`path`](#expose-paths): string + - [`localPathPort`](#expose-paths): number | `0` + - [`listenerPort`](#expose-paths): number | `0` - [`protocol`](#expose-paths): string | `http` @@ -169,87 +174,96 @@ When every field is defined, a service-defaults configuration entry has the foll ```hcl Kind = "service-defaults" -Name = "service_name" -Namespace = "namespace" -Partition = "partition" +Name = "" +Namespace = "default" +Partition = "default" Meta = { Key = "value" } Protocol = "tcp" BalanceInboundConnections = "exact_balance" -Mode = "transparent" +Mode = "" UpstreamConfig = { Overrides = { - Name = "name-of-upstreams-to-override" - Namespace = "namespace-containing-upstreams-to-override" - Peer = "peer-name-of-upstream-service" - Protocol = "http" - ConnectTimeoutMs = 100 + Name = "" + Namespace = "" + Peer = "" + Protocol = "" + ConnectTimeoutMs = 5000 MeshGateway = { - mode = "remote" + mode = "" } BalanceOutboundConnections = "exact_balance" Limits = { - MaxConnections = 10 - MaxPendingRequests = 50 - MaxConcurrentRequests = 100 + MaxConnections = 0 + MaxPendingRequests = 0 + MaxConcurrentRequests = 0 } PassiveHealthCheck = { - Interval = "5s" - MaxFailures = 5 - EnforcingConsecutive5xx = 99 + Interval = "0s" + MaxFailures = 0 + EnforcingConsecutive5xx = 100 MaxEjectionPercent = 10 BaseEjectionTime = "30s" } } Defaults = { - Protocol = "http2" - ConnectTimeoutMs = 2000 + Protocol = "" + ConnectTimeoutMs = 5000 MeshGateway = { - mode = "local" + mode = "" } BalanceOutboundConnections = "exact_balance" Limits = { - MaxConnections = 100 - MaxPendingRequests = 500 - MaxConcurrentRequests = 1000 + MaxConnections = 0 + MaxPendingRequests = 0 + MaxConcurrentRequests = 0 } PassiveHealthCheck = { - Interval = "1s" - MaxFailures = 1 - EnforcingConsecutive5xx = 89 + Interval = "0s" + MaxFailures = 0 + EnforcingConsecutive5xx = 100 MaxEjectionPercent = 10 BaseEjectionTime = "30s" } } } TransparentProxy = { - OutboundListenerPort = 15002 - DialedDirectly = true + OutboundListenerPort = 15001 + DialedDirectly = false } -MutualTLSMode = "strict" +MutualTLSMode = "strict" # only supported when services are in transparent proxy mode +EnvoyExtensions = [ + { + Name = "" + Required = `false` + Arguments = { } + ConsulVersion = "" + EnvoyVersion = "" + } +] Destination = { Addresses = [ - "First IP address", - "Second IP address" + "", + "" ] - Port = 88 + Port = 0 } -MaxInboundConnections = 100 -LocalConnectTimeoutMs = 10 -LocalRequestTimeoutMs = 10 +MaxInboundConnections = 0 +LocalConnectTimeoutMs = 5000 +LocalRequestTimeoutMs = "15s" MeshGateway = { - Mode = "remote" + Mode = "" } -ExternalSNI = "sni-server-host" +ExternalSNI = "" Expose = { - Checks = true + Checks = false Paths = [ { - Path = "/local/dir" - LocalPathPort = 99 - LocalListenerPort = 98 - Protocol = "http2" + Path = "" + LocalPathPort = 0 + LocalListenerPort = 0 + Protocol = "http" } ] } @@ -263,36 +277,36 @@ apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceDefaults metadata: name: - namespace: + namespace: default spec: protocol: tcp balanceInboundConnections: exact_balance - mode: transparent + mode: upstreamConfig: overrides: - - name: - namespace: - peer: + - name: + namespace: + peer: protocol: connectTimeoutMs: 5000 meshGateway: - mode: + mode: balanceOutboundConnections: exact_balance limits: maxConnections: 0 maxPendingRequests: 0 maxConcurrentRequests: 0 passiveHealthCheck: - interval: "10s" + interval: "0s" maxFailures: 0 enforcingConsecutive5xx: 100 maxEjectionPercent: 10 baseEjectionTime: "30s" defaults: - protocol: + protocol: connectTimeoutMs: 5000 meshGateway: - mode: + mode: balanceOutboundConnections: exact_balance limits: maxConnections: 0 @@ -308,14 +322,21 @@ spec: outboundListenerPort: 15001 dialedDirectly: false mutualTLSMode: strict + envoyExtensions: + - name: + required: false + arguments: + - + consulVersion: + envoyVersion: destination: addresses: - - - + - + port: 0 maxInboundConnections: 0 meshGateway: - mode: + mode: externalSNI: expose: checks: false @@ -332,93 +353,96 @@ spec: ```json { - "apiVersion": "consul.hashicorp.com/v1alpha1", - "kind": "ServiceDefaults", - "metadata": { - "name": "", - "namespace": "", - "partition": "" - }, - "spec": { - "protocol": "tcp", - "balanceInboundConnections": "exact_balance", - "mode": "transparent", - "upstreamConfig": { - "overrides": [ - { - "name": "", - "namespace": "", - "peer": "", - "protocol": "", - "connectTimeoutMs": 5000, - "meshGateway": { - "mode": "" - }, - "balanceOutboundConnections": "exact_balance", - "limits": { - "maxConnections": 0, - "maxPendingRequests": 0, - "maxConcurrentRequests": 0 - }, - "passiveHealthCheck": { - "interval": "0s", - "maxFailures": 0, - "enforcingConsecutive5xx": 100, - "maxEjectionPercent": 10, - "baseEjectionTime": "30s", - }, - } - ], - "defaults": { - "protocol": "", - "connectTimeoutMs": 5000, - "meshGateway": { - "mode": "" - }, - "balanceOutboundConnections": "exact_balance", - "limits": { - "maxConnections": 0, - "maxPendingRequests": 0, - "maxConcurrentRequests": 0 - }, - "passiveHealthCheck": { - "interval": "0s", - "maxFailures": 0, - "enforcingConsecutive5xx": 100, - "maxEjectionPercent": 10, - "baseEjectionTime": "30s", - } - } - }, - "transparentProxy": { - "outboundListenerPort": 15001, - "dialedDirectly": false - }, - "mutualTLSMode": "strict", - "destination": { - "addresses": [ - "", - "" - ], - "port": 0 - }, - "maxInboundConnections": 0, - "meshGateway": { - "mode": "" - }, - "externalSNI": "", - "expose": { - "checks": false, - "paths": [ - { - "path": "", - "localPathPort": 0, - "listenerPort": 0, - "protocol": "http" - } - ] - } - } + "Kind": "ServiceDefaults", + "Name": "", + "Namespace": "default", + "Partition": "default", + "Meta": { + "": "" + }, + "Protocol": "tcp", + "BalanceInboundConnections": "exact_balance", + "Mode": "", + "UpstreamConfig": { + "Overrides": [{ + "Name": "", + "Namespace": "", + "Peer": "", + "Protocol": "", + "ConnectTimeoutMs": 5000, + "MeshGateway": { + "Mode": "" + }, + "BalanceOutboundConnections": "exact_balance", + "Limits": { + "MaxConnections": 0, + "MaxPendingRequests": 0, + "MaxConcurrentRequests": 0 + }, + "PassiveHealthCheck": { + "Interval": "0s", + "MaxFailures": 0, + "EnforcingConsecutive5xx": 100, + "MaxEjectionPercent": 10, + "BaseEjectionTime": "30s" + } + }] + }, + "Defaults": { + "Protocol": "", + "ConnectTimeoutMs": 5000, + "MeshGateway": { + "Mode": "" + }, + "BalanceOutboundConnections": "exact_balance", + "Limits": { + "MaxConnections": 0, + "MaxPendingRequests": 0, + "MaxConcurrentRequests": 0 + }, + "PassiveHealthCheck": { + "Interval": "0s", + "MaxFailures": 0, + "EnforcingConsecutive5xx": 100, + "MaxEjectionPercent": 10, + "BaseEjectionTime": "30s" + } + }, + "TransparentProxy": { + "OutboundListenerPort": 15001, + "DialedDirectly": false + }, + "MutualTLSMode": "strict", + "EnvoyExtensions": [{ + "Name": "", + "Required": false, + "Arguments": { + "": "" + }, + "ConsulVersion": "", + "EnvoyVersion": "" + }], + "Destination": { + "Addresses": [ + "", + "" + ], + "Port": 0 + }, + "MaxInboundConnections": 0, + "MeshGateway": { + "Mode": "" + }, + "ExternalSNI": "", + "Expose": { + "Checks": false, + "Paths": [{ + "Path": "", + "LocalPathPort": 0, + "ListenerPort": 0, + "Protocol": "http" + }] + } } ``` @@ -431,11 +455,11 @@ spec: This section provides details about the fields you can configure in the service defaults configuration entry. - + ### `Kind` -Specifies the configuration entry type. +Specifies the configuration entry type. The value must be set to `service-defaults`. #### Values @@ -451,25 +475,25 @@ Specifies the name of the service you are setting the defaults for. - Default: none - This field is required. -- Data type: string +- Data type: String -### `Namespace` +### `Namespace` Specifies the Consul namespace that the configuration entry applies to. #### Values - Default: `default` -- Data type: string +- Data type: String -### `Partition` +### `Partition` Specifies the name of the name of the Consul admin partition that the configuration entry applies to. Refer to [Admin Partitions](/consul/docs/enterprise/admin-partitions) for additional information. #### Values - Default: `default` -- Data type: string +- Data type: String ### `Meta` @@ -479,8 +503,8 @@ Specifies a set of custom key-value pairs to add to the [Consul KV](/consul/docs - Default: none - Data type: Map of one or more key-value pairs. - - keys: string - - values: string, integer, or float + - keys: String + - values: String, integer, or float ### `Protocol` @@ -496,7 +520,7 @@ You can set the global protocol for proxies in the [`proxy-defaults`](/consul/do #### Values - Default: `tcp` -- You can specify one of the following string values: +- You can specify one of the following String values: - `tcp` (default) - `http` - `http2` @@ -510,14 +534,14 @@ Specifies the strategy for allocating inbound connections to the service across #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `Mode` Specifies a mode for how the service directs inbound and outbound traffic. -- Default: none +- Default: None - You can specify the following string values: - `direct`: The proxy's listeners must be dialed directly by the local application and other proxies. - `transparent`: The service captures inbound and outbound traffic and redirects it through the proxy. The mode does not enable the traffic redirection. It instructs Consul to configure Envoy as if traffic is already being redirected. @@ -532,8 +556,8 @@ Controls default upstream connection settings and custom overrides for individua #### Values -- Default: none -- Data type: map +- Default: None +- Data type: Map ### `UpstreamConfig.Overrides[]` @@ -541,8 +565,8 @@ Specifies options that override the [default upstream configurations](#upstreamc #### Values -- Default: none -- Data type: list +- Default: None +- Data type: List ### `UpstreamConfig.Overrides[].Name` @@ -550,8 +574,8 @@ Specifies the name of the upstream service that the configuration applies to. We #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `UpstreamConfig.Overrides[].Namespace` @@ -559,8 +583,8 @@ Specifies the namespace containing the upstream service that the configuration a #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `UpstreamConfig.Overrides[].Peer` @@ -568,8 +592,8 @@ Specifies the peer name of the upstream service that the configuration applies t #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `UpstreamConfig.Overrides[].Protocol` Specifies the protocol to use for requests to the upstream listener. @@ -578,8 +602,8 @@ We recommend configuring the protocol in the main [`Protocol`](#protocol) field #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `UpstreamConfig.Overrides[].ConnectTimeoutMs` @@ -591,7 +615,7 @@ We recommend configuring the upstream timeout in the [`connection_timeout`](/con #### Values - Default: `5000` -- Data type: integer +- Data type: Integer ### `UpstreamConfig.Overrides[].MeshGateway` @@ -614,8 +638,8 @@ Sets the strategy for allocating outbound connections from the upstream across E The only supported value is `exact_balance`. By default, no connections are balanced. Refer to the [Envoy documentation](https://cloudnative.to/envoy/api-v3/config/listener/v3/listener.proto.html#config-listener-v3-listener-connectionbalanceconfig) for details. -- Default: none -- Data type: string +- Default: None +- Data type: String ### `UpstreamConfig.Overrides[].Limits` @@ -627,9 +651,9 @@ The following table describes limits you can configure: | Limit | Description | Data type | Default | | --- | --- | --- | --- | -| `MaxConnections` | Specifies the maximum number of connections a service instance can establish against the upstream. Define this limit for HTTP/1.1 traffic. | integer | `0` | -| `MaxPendingRequests` | Specifies the maximum number of requests that are queued while waiting for a connection to establish. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | integer | `0` | -| `MaxConcurrentRequests` | Specifies the maximum number of concurrent requests. Define this limit for HTTP/2 traffic. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | integer | `0` | +| `MaxConnections` | Specifies the maximum number of connections a service instance can establish against the upstream. Define this limit for HTTP/1.1 traffic. | Integer | `0` | +| `MaxPendingRequests` | Specifies the maximum number of requests that are queued while waiting for a connection to establish. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | Integer | `0` | +| `MaxConcurrentRequests` | Specifies the maximum number of concurrent requests. Define this limit for HTTP/2 traffic. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | Integer | `0` | Refer to the [upstream configuration example](#upstream-configuration) for additional guidance. @@ -643,11 +667,11 @@ The following table describes passive health check parameters you can configure: | Limit | Description | Data type | Default | | --- | --- | --- | --- | -| `Interval` | Specifies the time between checks. | string | `0s` | -| `MaxFailures` | Specifies the number of consecutive failures allowed per check interval. If exceeded, Consul removes the host from the load balancer. | integer | `0` | -| `EnforcingConsecutive5xx` | Specifies a percentage that indicates how many times out of 100 that Consul ejects the host when it detects an outlier status. The outlier status is determined by consecutive errors in the 500-599 response range. | integer | `100` | - | `MaxEjectionPercent` | Specifies the maximum percentage of an upstream cluster that Consul ejects when the proxy reports an outlier. Consul ejects at least one host when an outlier is detected regardless of the value. | integer | `10` | - | `BaseEjectionTime` | Specifies the minimum amount of time that an ejected host must remain outside the cluster before rejoining. The real time is equal to the value of the `BaseEjectionTime` multiplied by the number of times the host has been ejected. | string | `30s` | +| `Interval` | Specifies the time between checks. | String | `0s` | +| `MaxFailures` | Specifies the number of consecutive failures allowed per check interval. If exceeded, Consul removes the host from the load balancer. | Integer | `0` | +| `EnforcingConsecutive5xx` | Specifies a percentage that indicates how many times out of 100 that Consul ejects the host when it detects an outlier status. The outlier status is determined by consecutive errors in the 500-599 response range. | Integer | `100` | + | `MaxEjectionPercent` | Specifies the maximum percentage of an upstream cluster that Consul ejects when the proxy reports an outlier. Consul ejects at least one host when an outlier is detected regardless of the value. | Integer | `10` | + | `BaseEjectionTime` | Specifies the minimum amount of time that an ejected host must remain outside the cluster before rejoining. The real time is equal to the value of the `BaseEjectionTime` multiplied by the number of times the host has been ejected. | String | `30s` | ### `UpstreamConfig.Defaults` @@ -655,8 +679,8 @@ Specifies configurations that set default upstream settings. For information abo #### Values -- Default: none -- Data type: map +- Default: None +- Data type: Map ### `UpstreamConfig.Defaults.Protocol` @@ -664,8 +688,8 @@ Specifies default protocol for upstream listeners. We recommend configuring the protocol in the main [`Protocol`](#protocol) field of the configuration entry so that you can leverage [L7 features](/consul/docs/connect/l7-traffic). Setting the protocol in an upstream configuration limits L7 management functionality. -- Default: none -- Data type: string +- Default: None +- Data type: String ### `UpstreamConfig.Defaults.ConnectTimeoutMs` @@ -674,7 +698,7 @@ Specifies how long in milliseconds that all services should continue attempting For non-Kubernetes environments, we recommend configuring the upstream timeout in the [`connection_timeout`](/consul/docs/connect/config-entries/service-resolver#connecttimeout) field of the `service-resolver` configuration entry for the upstream destination service. Doing so enables you to leverage [L7 features](/consul/docs/connect/l7-traffic). Configuring the timeout in the `service-defaults` upstream configuration limits L7 management functionality. - Default: `5000` -- Data type: integer +- Data type: Integer ### `UpstreamConfig.Defaults.MeshGateway` @@ -682,7 +706,7 @@ Specifies the default mesh gateway `mode` field for all upstreams. Refer to [Ser You can specify the following string values for the `mode` field: -- `none`: The service does not make outbound connections through a mesh gateway. Instead, the service makes outbound connections directly to the destination services. +- `None`: The service does not make outbound connections through a mesh gateway. Instead, the service makes outbound connections directly to the destination services. - `local`: The service mesh proxy makes an outbound connection to a gateway running in the same datacenter. - `remote`: The service mesh proxy makes an outbound connection to a gateway running in the destination datacenter. @@ -690,8 +714,8 @@ You can specify the following string values for the `mode` field: Sets the strategy for allocating outbound connections from upstreams across Envoy proxy threads. The only supported value is `exact_balance`. By default, no connections are balanced. Refer to the [Envoy documentation](https://cloudnative.to/envoy/api-v3/config/listener/v3/listener.proto.html#config-listener-v3-listener-connectionbalanceconfig) for details. -- Default: none -- Data type: string +- Default: None +- Data type: String ### `UpstreamConfig.Defaults.Limits` @@ -699,9 +723,9 @@ Map that specifies a set of limits to apply to when connecting upstream services | Limit | Description | Data type | Default | | --- | --- | --- | --- | -| `MaxConnections` | Specifies the maximum number of connections a service instance can establish against the upstream. Define this limit for HTTP/1.1 traffic. | integer | `0` | -| `MaxPendingRequests` | Specifies the maximum number of requests that are queued while waiting for a connection to establish. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | integer | `0` | -| `MaxConcurrentRequests` | Specifies the maximum number of concurrent requests. Define this limit for HTTP/2 traffic. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | integer | `0` | +| `MaxConnections` | Specifies the maximum number of connections a service instance can establish against the upstream. Define this limit for HTTP/1.1 traffic. | Integer | `0` | +| `MaxPendingRequests` | Specifies the maximum number of requests that are queued while waiting for a connection to establish. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | Integer | `0` | +| `MaxConcurrentRequests` | Specifies the maximum number of concurrent requests. Define this limit for HTTP/2 traffic. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | Integer | `0` | ### `UpstreamConfig.Defaults.PassiveHealthCheck` @@ -709,22 +733,22 @@ Map that specifies a set of rules that enable Consul to remove hosts from the up | Limit | Description | Data type | Default | | --- | --- | --- | --- | -| `Interval` | Specifies the time between checks. | string | `0s` | -| `MaxFailures` | Specifies the number of consecutive failures allowed per check interval. If exceeded, Consul removes the host from the load balancer. | integer | `0` | -| `EnforcingConsecutive5xx ` | Specifies a percentage that indicates how many times out of 100 that Consul ejects the host when it detects an outlier status. The outlier status is determined by consecutive errors in the 500-599 response range. | integer | `100` | - | `MaxEjectionPercent` | Specifies the maximum percentage of an upstream cluster that Consul ejects when the proxy reports an outlier. Consul ejects at least one host when an outlier is detected regardless of the value. | integer | `10` | - | `BaseEjectionTime` | Specifies the minimum amount of time that an ejected host must remain outside the cluster before rejoining. The real time is equal to the value of the `BaseEjectionTime` multiplied by the number of times the host has been ejected. | string | `30s` | +| `Interval` | Specifies the time between checks. | String | `0s` | +| `MaxFailures` | Specifies the number of consecutive failures allowed per check interval. If exceeded, Consul removes the host from the load balancer. | Integer | `0` | +| `EnforcingConsecutive5xx ` | Specifies a percentage that indicates how many times out of 100 that Consul ejects the host when it detects an outlier status. The outlier status is determined by consecutive errors in the 500-599 response range. | Integer | `100` | + | `MaxEjectionPercent` | Specifies the maximum percentage of an upstream cluster that Consul ejects when the proxy reports an outlier. Consul ejects at least one host when an outlier is detected regardless of the value. | Integer | `10` | + | `BaseEjectionTime` | Specifies the minimum amount of time that an ejected host must remain outside the cluster before rejoining. The real time is equal to the value of the `BaseEjectionTime` multiplied by the number of times the host has been ejected. | String | `30s` | ### `TransparentProxy` -Controls configurations specific to proxies in transparent mode. Refer to [Transparent Proxy](/consul/docs/connect/transparent-proxy) for additional information. +Controls configurations specific to proxies in transparent mode. Refer to [Transparent Proxy Mode](/consul/docs/k8s/connect/transparent-proxy) for additional information. You can configure the following parameters in the `TransparentProxy` block: | Parameter | Description | Data type | Default | | --- | --- | --- | --- | -| `OutboundListenerPort` | Specifies the port that the proxy listens on for outbound traffic. This must be the same port number where outbound application traffic is redirected. | integer | `15001` | -| `DialedDirectly` | Enables transparent proxies to dial the proxy instance's IP address directly when set to `true`. Transparent proxies commonly dial upstreams at the `"virtual"` tagged address, which load balances across instances. Dialing individual instances can be helpful for stateful services, such as a database cluster with a leader. | boolean | `false` | +| `OutboundListenerPort` | Specifies the port that the proxy listens on for outbound traffic. This must be the same port number where outbound application traffic is redirected. | Integer | `15001` | +| `DialedDirectly` | Enables transparent proxies to dial the proxy instance's IP address directly when set to `true`. Transparent proxies commonly dial upstreams at the `"virtual"` tagged address, which load balances across instances. Dialing individual instances can be helpful for stateful services, such as a database cluster with a leader. | Boolean | `false` | ### `MutualTLSMode` @@ -743,45 +767,49 @@ You can specify the following string values for the `MutualTLSMode` field: List of extensions to modify Envoy proxy configuration. Refer to [Envoy Extensions](/consul/docs/connect/proxies/envoy-extensions) for additional information. -You can configure the following parameters in the `EnvoyExtensions` block: +The following table describes how to configure values in the `EnvoyExtensions` map: | Parameter | Description | Data type | Default | -| --- | --- | --- | --- | -| `Name` | Name of the extension. | string | `""` | -| `Required` | When Required is true and the extension does not update any Envoy resources, an error is returned. Use this parameter to ensure that extensions required for secure communication are not unintentionally bypassed. | string | `""` | -| `Arguments` | Arguments to pass to the extension executable. | map | `nil` | +| --- | --- | --- | --- | +| `Name` | Specifies the name of the extension. | String | None | +| `Required` | Specify `true` to require the extension to apply successfully.

Use this parameter to ensure that extensions required for secure communication are not unintentionally bypassed.

When Envoy fails to apply a required extension, Consul logs an error and skips all extensions, leaving xDS resources unchanged.

| String | None | +| `Arguments` | Specifies the arguments to pass to the extension. Refer to the documentation for the extension you want to implement for additional information. | Map | None | +| `ConsulVersion` | Specifies the Consul [version constraint](https://github.com/hashicorp/go-version) for the extension. Consul validates the version constraint against the runtime version during xDS updates. If a non-matching version is in use, Consul logs and skips the extension.

Use this parameter to avoid upgrade issues when a configured extension is not compatible with a new version of Consul.

| String | None | +| `EnvoyVersion` | Specifies the Envoy [version constraint](https://github.com/hashicorp/go-version) for the extension. Consul validates the version constraint against the version of the running Envoy proxy during xDS updates. If a non-matching version is in use, Consul logs and skips the extension.

Use this parameter to avoid upgrade issues when a configured extension is not compatible with a new version of Envoy.

| String | None | -### `Destination[]` +### `Destination{}` Configures the destination for service traffic through terminating gateways. Refer to [Terminating Gateway](/consul/docs/connect/gateways/terminating-gateway) for additional information. +To use the `Destination` block, proxy services must be in transparent proxy mode. Refer to [Enable transparent proxy mode](/consul/docs/k8s/connect/transparent-proxy/enable-transparent-proxy) for additional information. + You can configure the following parameters in the `Destination` block: | Parameter | Description | Data type | Default | | --- | --- | --- | --- | -| `Address` | Specifies a list of addresses for the destination. You can configure a list of hostnames and IP addresses. Wildcards are not supported. | list | none | -| `Port` | Specifies the port number of the destination. | integer | `0` | +| `Addresses` | Specifies a list of addresses for the destination. You can configure a list of hostnames and IP addresses. Wildcards are not supported. | List | None | +| `Port` | Specifies the port number of the destination. | Integer | `0` | ### `MaxInboundConnections` Specifies the maximum number of concurrent inbound connections to each service instance. - Default: `0` -- Data type: integer +- Data type: Integer ### `LocalConnectTimeoutMs` Specifies the number of milliseconds allowed for establishing connections to the local application instance before timing out. - Default: `5000` -- Data type: integer +- Data type: Integer ### `LocalRequestTimeoutMs` Specifies the timeout for HTTP requests to the local application instance. Applies to HTTP-based protocols only. If not specified, inherits the Envoy default for route timeouts. - Default: Inherits `15s` from Envoy as the default -- Data type: string +- Data type: String ### `MeshGateway` @@ -797,15 +825,15 @@ You can specify the following string values for the `mode` field: Specifies the TLS server name indication (SNI) when federating with an external system. -- Default: none -- Data type: string +- Default: None +- Data type: String ### `Expose` Specifies default configurations for exposing HTTP paths through Envoy. Exposing paths through Envoy enables services to listen on `localhost` only. Applications that are not Consul service mesh-enabled can still contact an HTTP endpoint. Refer to [Expose Paths Configuration Reference](/consul/docs/proxies/proxy-config-reference#expose-paths-configuration-reference) for additional information and example configurations. -- Default: none -- Data type: map +- Default: None +- Data type: Map ### `Expose.Checks` @@ -814,7 +842,7 @@ Exposes all HTTP and gRPC checks registered with the agent if set to `true`. Env We recommend enabling the `Checks` configuration when a Consul client cannot reach registered services over localhost, such as when Consul agents run in their own pods in Kubernetes. - Default: `false` -- Data type: boolean +- Data type: Boolean ### `Expose.Paths[]` @@ -822,10 +850,10 @@ Specifies a list of configuration maps that define paths to expose through Envoy | Parameter | Description | Data type | Default | | --- | --- | --- | --- | -| `Path` | Specifies the HTTP path to expose. You must prepend the path with a forward slash (`/`). | string | none | -| `LocalPathPort` | Specifies the port where the local service listens for connections to the path. | integer | `0` | -| `ListenPort` | Specifies the port where the proxy listens for connections. The port must be available. If the port is unavailable, Envoy does not expose a listener for the path and the proxy registration still succeeds. | integer | `0` | -| `Protocol` | Specifies the protocol of the listener. You can configure one of the following values:
  • `http`
  • `http2`: Use with gRPC traffic
  • | integer | `http` | +| `Path` | Specifies the HTTP path to expose. You must prepend the path with a forward slash (`/`). | String | None | +| `LocalPathPort` | Specifies the port where the local service listens for connections to the path. | Integer | `0` | +| `ListenPort` | Specifies the port where the proxy listens for connections. The port must be available. If the port is unavailable, Envoy does not expose a listener for the path and the proxy registration still succeeds. | Integer | `0` | +| `Protocol` | Specifies the protocol of the listener. You can configure one of the following values:
  • `http`
  • `http2`: Use with gRPC traffic
  • | Integer | `http` |
    @@ -835,7 +863,7 @@ Specifies a list of configuration maps that define paths to expose through Envoy Specifies the version of the Consul API for integrating with Kubernetes. The value must be `consul.hashicorp.com/v1alpha1`. The `apiVersion` field is not supported for non-Kubernetes deployments. -- Default: none +- Default: None - This field is required. - String value that must be set to `consul.hashicorp.com/v1alpha1`. @@ -852,7 +880,7 @@ Map that contains the service name, namespace, and admin partition that the conf #### Values -- Default: none +- Default: None - Map containing the following strings: - [`name`](#name) - [`namespace`](#namespace) @@ -865,16 +893,16 @@ Specifies the name of the service you are setting the defaults for. #### Values -- Default: none +- Default: None - This field is required -- Data type: string +- Data type: String ### `metadata.namespace` Specifies the Consul namespace that the configuration entry applies to. Refer to [Consul Enterprise](/consul/docs/k8s/crds#consul-enterprise) for information about how Consul namespaces map to Kubernetes Namespaces. Open source Consul distributions (Consul OSS) ignore the `metadata.namespace` configuration. - Default: `default` -- Data type: string +- Data type: String ### `spec` @@ -908,8 +936,8 @@ Specifies the strategy for allocating inbound connections to the service across #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `spec.mode` @@ -917,7 +945,7 @@ Specifies a mode for how the service directs inbound and outbound traffic. #### Values -- Default: none +- Default: None - Required: optional - You can specified the following string values: @@ -930,7 +958,7 @@ Specifies a map that controls default upstream connection settings and custom ov #### Values -- Default: none +- Default: None - Map that contains the following configurations: - [`UpstreamConfig.Overrides`](#upstreamconfig-overrides) - [`UpstreamConfig.Defaults`](#upstreamconfig-defaults) @@ -941,8 +969,8 @@ Specifies options that override the [default upstream configurations](#spec-upst #### Values -- Default: none -- Data type: list +- Default: None +- Data type: List ### `spec.upstreamConfig.overrides[].name` @@ -950,17 +978,17 @@ Specifies the name of the upstream service that the configuration applies to. Do #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String -### `spec.upstreamConfig.overrides[].namespace` +### `spec.upstreamConfig.overrides[].namespace` Specifies the namespace containing the upstream service that the configuration applies to. Do not use the `*` wildcard to prevent the configuration from applying to unintended upstreams. #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `spec.upstreamConfig.overrides[].peer` @@ -968,8 +996,8 @@ Specifies the peer name of the upstream service that the configuration applies t #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `spec.upstreamConfig.overrides[].protocol` @@ -978,7 +1006,7 @@ Specifies the protocol to use for requests to the upstream listener. We recommen #### Values - Default: inherits the main [`protocol`](#protocol) configuration -- Data type: string +- Data type: String ### `spec.upstreamConfig.overrides[].connectTimeoutMs` @@ -990,7 +1018,7 @@ We recommend configuring the upstream timeout in the [`connectTimeout`](/consul/ #### Values - Default: `5000` -- Data type: integer +- Data type: Integer ### `spec.upstreamConfig.overrides[].meshGateway.mode` @@ -1010,8 +1038,8 @@ Sets the strategy for allocating outbound connections from the upstream across E #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `spec.upstreamConfig.overrides[].limits` @@ -1023,9 +1051,9 @@ The following table describes limits you can configure: | Limit | Description | Data type | Default | | --- | --- | --- | --- | -| `maxConnections` | Specifies the maximum number of connections a service instance can establish against the upstream. Define this limit for HTTP/1.1 traffic. | integer | `0` | -| `maxPendingRequests` | Specifies the maximum number of requests that are queued while waiting for a connection to establish. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | integer | `0` | -| `maxConcurrentRequests` | Specifies the maximum number of concurrent requests. Define this limit for HTTP/2 traffic. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | integer | `0` | +| `maxConnections` | Specifies the maximum number of connections a service instance can establish against the upstream. Define this limit for HTTP/1.1 traffic. | Integer | `0` | +| `maxPendingRequests` | Specifies the maximum number of requests that are queued while waiting for a connection to establish. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | Integer | `0` | +| `maxConcurrentRequests` | Specifies the maximum number of concurrent requests. Define this limit for HTTP/2 traffic. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | Integer | `0` | ### `spec.upstreamConfig.overrides[].passiveHealthCheck` @@ -1037,11 +1065,11 @@ The following table describes passive health check parameters you can configure: | Limit | Description | Data type | Default | | --- | --- | --- | --- | -| `interval` | Specifies the time between checks. | string | `0s` | -| `maxFailures` | Specifies the number of consecutive failures allowed per check interval. If exceeded, Consul removes the host from the load balancer. | integer | `0` | -| `enforcingConsecutive5xx ` | Specifies a percentage that indicates how many times out of 100 that Consul ejects the host when it detects an outlier status. The outlier status is determined by consecutive errors in the 500-599 response range. | integer | `100` | - | `maxEjectionPercent` | The maximum % of an upstream cluster that can be ejected due to outlier detection. Defaults to 10% but will eject at least one host regardless of the value. | integer | `10` | - | `baseEjectionTime` | The base time that a host is ejected for. The real time is equal to the base time multiplied by the number of times the host has been ejected and is capped by max_ejection_time (Default 300s). Defaults to 30000ms or 30s. | string | `30s` | +| `interval` | Specifies the time between checks. | String | `0s` | +| `maxFailures` | Specifies the number of consecutive failures allowed per check interval. If exceeded, Consul removes the host from the load balancer. | Integer | `0` | +| `enforcingConsecutive5xx ` | Specifies a percentage that indicates how many times out of 100 that Consul ejects the host when it detects an outlier status. The outlier status is determined by consecutive errors in the 500-599 response range. | Integer | `100` | + | `maxEjectionPercent` | The maximum % of an upstream cluster that can be ejected due to outlier detection. Defaults to 10% but will eject at least one host regardless of the value. | Integer | `10` | + | `baseEjectionTime` | The base time that a host is ejected for. The real time is equal to the base time multiplied by the number of times the host has been ejected and is capped by max_ejection_time (Default 300s). Defaults to 30000ms or 30s. | String | `30s` | ### `spec.upstreamConfig.defaults` @@ -1049,8 +1077,8 @@ Map of configurations that set default upstream configurations for the service. #### Values -- Default: none -- Data type: list +- Default: None +- Data type: List ### `spec.upstreamConfig.defaults.protocol` @@ -1058,8 +1086,8 @@ Specifies default protocol for upstream listeners. We recommend configuring the #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `spec.upstreamConfig.default.connectTimeoutMs` @@ -1070,7 +1098,7 @@ We recommend configuring the upstream timeout in the [`connectTimeout`](/consul/ #### Values - Default: `5000` -- Data type: integer +- Data type: Integer ### `spec.upstreamConfig.defaults.meshGateway.mode` @@ -1090,8 +1118,8 @@ Sets the strategy for allocating outbound connections from upstreams across Envo #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `spec.upstreamConfig.defaults.limits` @@ -1103,9 +1131,9 @@ The following table describes limits you can configure: | Limit | Description | Data type | Default | | --- | --- | --- | --- | -| `maxConnections` | Specifies the maximum number of connections a service instance can establish against the upstream. Define this limit for HTTP/1.1 traffic. | integer | `0` | -| `maxPendingRequests` | Specifies the maximum number of requests that are queued while waiting for a connection to establish. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | integer | `0` | -| `maxConcurrentRequests` | Specifies the maximum number of concurrent requests. Define this limit for HTTP/2 traffic. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | integer | `0` | +| `maxConnections` | Specifies the maximum number of connections a service instance can establish against the upstream. Define this limit for HTTP/1.1 traffic. | Integer | `0` | +| `maxPendingRequests` | Specifies the maximum number of requests that are queued while waiting for a connection to establish. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | Integer | `0` | +| `maxConcurrentRequests` | Specifies the maximum number of concurrent requests. Define this limit for HTTP/2 traffic. An L7 protocol must be defined in the [`protocol`](#protocol) field for this limit to take effect. | Integer | `0` | ### `spec.upstreamConfig.defaults.passiveHealthCheck` Map that specifies a set of rules that enable Consul to remove hosts from the upstream cluster that are unreachable or that return errors. @@ -1116,11 +1144,11 @@ The following table describes the health check parameters you can configure: | Limit | Description | Data type | Default | | --- | --- | --- | --- | -| `interval` | Specifies the time between checks. | string | `0s` | -| `maxFailures` | Specifies the number of consecutive failures allowed per check interval. If exceeded, Consul removes the host from the load balancer. | integer | `0` | -| `enforcingConsecutive5xx ` | Specifies a percentage that indicates how many times out of 100 that Consul ejects the host when it detects an outlier status. The outlier status is determined by consecutive errors in the 500-599 response range. | integer | `100` | - | `MaxEjectionPercent` | Specifies the maximum percentage of an upstream cluster that Consul ejects when the proxy reports an outlier. Consul ejects at least one host when an outlier is detected regardless of the value. | integer | `10` | - | `BaseEjectionTime` | Specifies the minimum amount of time that an ejected host must remain outside the cluster before rejoining. The real time is equal to the value of the `BaseEjectionTime` multiplied by the number of times the host has been ejected. | string | `30s` | +| `interval` | Specifies the time between checks. | String | `0s` | +| `maxFailures` | Specifies the number of consecutive failures allowed per check interval. If exceeded, Consul removes the host from the load balancer. | Integer | `0` | +| `enforcingConsecutive5xx ` | Specifies a percentage that indicates how many times out of 100 that Consul ejects the host when it detects an outlier status. The outlier status is determined by consecutive errors in the 500-599 response range. | Integer | `100` | + | `MaxEjectionPercent` | Specifies the maximum percentage of an upstream cluster that Consul ejects when the proxy reports an outlier. Consul ejects at least one host when an outlier is detected regardless of the value. | Integer | `10` | + | `BaseEjectionTime` | Specifies the minimum amount of time that an ejected host must remain outside the cluster before rejoining. The real time is equal to the value of the `BaseEjectionTime` multiplied by the number of times the host has been ejected. | String | `30s` | ### `spec.transparentProxy` @@ -1132,8 +1160,8 @@ You can configure the following parameters in the `TransparentProxy` block: | Parameter | Description | Data type | Default | | --- | --- | --- | --- | -| `outboundListenerPort` | Specifies the port that the proxy listens on for outbound traffic. This must be the same port number where outbound application traffic is redirected. | integer | `15001` | -| `dialedDirectly` | Enables transparent proxies to dial the proxy instance's IP address directly when set to `true`. Transparent proxies commonly dial upstreams at the `"virtual"` tagged address, which load balances across instances. Dialing individual instances can be helpful for stateful services, such as a database cluster with a leader. | boolean | `false` | +| `outboundListenerPort` | Specifies the port that the proxy listens on for outbound traffic. This must be the same port number where outbound application traffic is redirected. | Integer | `15001` | +| `dialedDirectly` | Enables transparent proxies to dial the proxy instance's IP address directly when set to `true`. Transparent proxies commonly dial upstreams at the `"virtual"` tagged address, which load balances across instances. Dialing individual instances can be helpful for stateful services, such as a database cluster with a leader. | Boolean | `false` | ### `spec.mutualTLSMode` @@ -1156,26 +1184,30 @@ List of extensions to modify Envoy proxy configuration. Refer to [Envoy Extensio #### Values -You can configure the following parameters in the `EnvoyExtensions` block: +The following table describes how to configure values in the `envoyExtensions` map: | Parameter | Description | Data type | Default | -| --- | --- | --- | --- | -| `name` | Name of the extension. | string | `""` | -| `required` | When Required is true and the extension does not update any Envoy resources, an error is returned. Use this parameter to ensure that extensions required for secure communication are not unintentionally bypassed. | string | `""` | -| `arguments` | Arguments to pass to the extension executable. | map | `nil` | +| --- | --- | --- | --- | +| `name` | Specifies the name of the extension. | String | None | +| `required` | Specify `true` to require the extension to apply successfully.

    Use this parameter to ensure that extensions required for secure communication are not unintentionally bypassed.

    When Envoy fails to apply a required extension, Consul logs an error and skips all extensions, leaving xDS resources unchanged.

    | String | None | +| `arguments` | Specifies the arguments to pass to the extension. Refer to the documentation for the extension for additional information. | Map | None | +| `consulVersion` | Specifies the Consul [version constraint](https://github.com/hashicorp/go-version) for the extension. Consul validates the version constraint against the runtime version during xDS updates. If a non-matching version is in use, Consul logs and skips the extension.

    Use this parameter to avoid upgrade issues when a configured extension is not compatible with a new version of Consul.

    | String | None | +| `envoyVersion` | Specifies the Envoy [version constraint](https://github.com/hashicorp/go-version) for the extension. Consul validates the version constraint against the version of the running Envoy proxy during xDS updates. If a non-matching version is in use, Consul logs and skips the extension.

    Use this parameter to avoid upgrade issues when a configured extension is not compatible with a new version of Envoy.

    | String | None | ### `spec.destination` Map of configurations that specify one or more destinations for service traffic routed through terminating gateways. Refer to [Terminating Gateway](/consul/docs/connect/gateways/terminating-gateway) for additional information. +To use the `destination` block, proxy services must be in transparent proxy mode. Refer to [Enable transparent proxy mode](/consul/docs/k8s/connect/transparent-proxy/enable-transparent-proxy) for additional information. + #### Values -You can configure the following parameters in the `Destination` block: +You can configure the following parameters in the `destination` block: | Parameter | Description | Data type | Default | | --- | --- | --- | --- | -| `address` | Specifies a list of addresses for the destination. You can configure a list of hostnames and IP addresses. Wildcards are not supported. | list | none | -| `port` | Specifies the port number of the destination. | integer | `0` | +| `addresses` | Specifies a list of addresses for the destination. You can configure a list of hostnames and IP addresses. Wildcards are not supported. | List | None | +| `port` | Specifies the port number of the destination. | Integer | `0` | ### `spec.maxInboundConnections` @@ -1184,7 +1216,7 @@ Specifies the maximum number of concurrent inbound connections to each service i #### Values - Default: `0` -- Data type: integer +- Data type: Integer ### `spec.localConnectTimeoutMs` @@ -1193,7 +1225,7 @@ Specifies the number of milliseconds allowed for establishing connections to the #### Values - Default: `5000` -- Data type: integer +- Data type: Integer ### `spec.localRequestTimeoutMs` @@ -1202,7 +1234,7 @@ Specifies the timeout for HTTP requests to the local application instance. Appli #### Values - Default of `15s` is inherited from Envoy -- Data type: string +- Data type: String ### `spec.meshGateway.mode` Specifies the default mesh gateway `mode` field for the service. Refer to [Service Mesh Proxy Configuration](/consul/docs/connect/gateways/mesh-gateway#service-mesh-proxy-configuration) in the mesh gateway documentation for additional information. @@ -1221,8 +1253,8 @@ Specifies the TLS server name indication (SNI) when federating with an external #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `spec.expose` @@ -1230,8 +1262,8 @@ Specifies default configurations for exposing HTTP paths through Envoy. Exposing #### Values -- Default: none -- Data type: string +- Default: None +- Data type: String ### `spec.expose.checks` @@ -1242,7 +1274,7 @@ We recommend enabling the `Checks` configuration when a Consul client cannot rea #### Values - Default: `false` -- Data type: boolean +- Data type: Boolean ### `spec.expose.paths[]` @@ -1254,10 +1286,10 @@ The following table describes the parameters for each map: | Parameter | Description | Data type | Default | | --- | --- | --- | --- | -| `path` | Specifies the HTTP path to expose. You must prepend the path with a forward slash (`/`). | string | none | -| `localPathPort` | Specifies the port where the local service listens for connections to the path. | integer | `0` | -| `listenPort` | Specifies the port where the proxy listens for connections. The port must be available. If the port is unavailable, Envoy does not expose a listener for the path and the proxy registration still succeeds. | integer | `0` | -| `protocol` | Specifies the protocol of the listener. You can configure one of the following values:
  • `http`
  • `http2`: Use with gRPC traffic
  • | integer | `http` | +| `path` | Specifies the HTTP path to expose. You must prepend the path with a forward slash (`/`). | String | None | +| `localPathPort` | Specifies the port where the local service listens for connections to the path. | Integer | `0` | +| `listenPort` | Specifies the port where the proxy listens for connections. The port must be available. If the port is unavailable, Envoy does not expose a listener for the path and the proxy registration still succeeds. | Integer | `0` | +| `protocol` | Specifies the protocol of the listener. You can configure one of the following values:
  • `http`
  • `http2`: Use with gRPC traffic
  • | Integer | `http` |
    @@ -1483,6 +1515,7 @@ spec: The following examples creates a default destination assigned to a terminating gateway. A destination represents a location outside the Consul cluster. Services can dial destinations dialed directly when transparent proxy mode is enabled. +Proxy services must be in transparent proxy mode to configure destinations. Refer to [Enable transparent proxy mode](/consul/docs/k8s/connect/transparent-proxy/enable-transparent-proxy) for additional information. From 6838441c54c157ef1b22cf39771c5a4d9e23d73e Mon Sep 17 00:00:00 2001 From: Iryna Shustava Date: Fri, 15 Sep 2023 10:39:53 -0600 Subject: [PATCH 05/28] Default to tcp protocol when workload protocol is unspecified (#18824) --- .../mesh/internal/controllers/sidecarproxy/builder/local_app.go | 2 +- .../mesh/internal/mappers/sidecarproxymapper/service_mapper.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go index 2a34e366dc2f..bfb3f0b066c0 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go @@ -243,7 +243,7 @@ func (l *ListenerBuilder) addInboundRouter(clusterName string, port *pbcatalog.W return l } - if port.Protocol == pbcatalog.Protocol_PROTOCOL_TCP { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_TCP || port.Protocol == pbcatalog.Protocol_PROTOCOL_UNSPECIFIED { r := &pbproxystate.Router{ Destination: &pbproxystate.Router_L4{ L4: &pbproxystate.L4Destination{ diff --git a/internal/mesh/internal/mappers/sidecarproxymapper/service_mapper.go b/internal/mesh/internal/mappers/sidecarproxymapper/service_mapper.go index 8cc37d426d6d..cce31b2b67e1 100644 --- a/internal/mesh/internal/mappers/sidecarproxymapper/service_mapper.go +++ b/internal/mesh/internal/mappers/sidecarproxymapper/service_mapper.go @@ -24,7 +24,7 @@ func (m *Mapper) MapServiceToProxyStateTemplate(ctx context.Context, rt controll return controller.MakeRequests(types.ProxyStateTemplateType, ids), nil } -// mapServiceThroughDestinationsToProxyStateTemplates takes and explicit +// mapServiceThroughDestinationsToProxyStateTemplates takes an explicit // Service and traverses back through Destinations to Workloads to // ProxyStateTemplates. // From 753c8f177484dfaa674c0a496abfc5f91464629e Mon Sep 17 00:00:00 2001 From: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Date: Fri, 15 Sep 2023 11:54:51 -0500 Subject: [PATCH 06/28] Retry and timeout test acceptance test (#18791) * retry and timeout test * add docker mirrior * checkpoint * add in error * add in delay * up error rate * fix status code --- .../consul-container/libs/service/common.go | 2 +- .../consul-container/libs/service/examples.go | 2 +- .../test/gateways/gateway_endpoint_test.go | 10 +- .../test/gateways/http_route_test.go | 232 ++++++++++++++++++ 4 files changed, 242 insertions(+), 4 deletions(-) diff --git a/test/integration/consul-container/libs/service/common.go b/test/integration/consul-container/libs/service/common.go index 0e0e2739571b..775113a58027 100644 --- a/test/integration/consul-container/libs/service/common.go +++ b/test/integration/consul-container/libs/service/common.go @@ -9,5 +9,5 @@ import ( const ( envoyLogLevel = "debug" - hashicorpDockerProxy = "docker.mirror.hashicorp.services" + HashicorpDockerProxy = "docker.mirror.hashicorp.services" ) diff --git a/test/integration/consul-container/libs/service/examples.go b/test/integration/consul-container/libs/service/examples.go index cc37cdb0a750..bfcaf22b2d5b 100644 --- a/test/integration/consul-container/libs/service/examples.go +++ b/test/integration/consul-container/libs/service/examples.go @@ -187,7 +187,7 @@ func NewExampleService(ctx context.Context, name string, httpPort int, grpcPort command = append(command, containerArgs...) req := testcontainers.ContainerRequest{ - Image: hashicorpDockerProxy + "/fortio/fortio", + Image: HashicorpDockerProxy + "/fortio/fortio", WaitingFor: wait.ForLog("").WithStartupTimeout(60 * time.Second), AutoRemove: false, Name: containerName, diff --git a/test/integration/consul-container/test/gateways/gateway_endpoint_test.go b/test/integration/consul-container/test/gateways/gateway_endpoint_test.go index 84981cc5966d..76d233051d36 100644 --- a/test/integration/consul-container/test/gateways/gateway_endpoint_test.go +++ b/test/integration/consul-container/test/gateways/gateway_endpoint_test.go @@ -227,6 +227,7 @@ type checkOptions struct { responseHeaders map[string]string statusCode int testName string + expectedBody string } // checkRoute, customized version of libassert.RouteEchos to allow for headers/distinguishing between the server instances @@ -289,8 +290,13 @@ func checkRoute(t *testing.T, port int, path string, headers map[string]string, return false } } - if !strings.Contains(string(body), "hello") { - t.Log("body does not contain 'hello'") + expectedBody := expected.expectedBody + if expectedBody == "" { + expectedBody = "hello" + } + if !strings.Contains(string(body), expectedBody) { + t.Log(string(body)) + t.Log("body does not contain " + expectedBody) return false } diff --git a/test/integration/consul-container/test/gateways/http_route_test.go b/test/integration/consul-container/test/gateways/http_route_test.go index bc9c971e7d97..96c93da955a4 100644 --- a/test/integration/consul-container/test/gateways/http_route_test.go +++ b/test/integration/consul-container/test/gateways/http_route_test.go @@ -8,6 +8,8 @@ import ( "crypto/rand" "encoding/hex" "fmt" + "github.com/testcontainers/testcontainers-go" + "k8s.io/utils/pointer" "testing" "time" @@ -737,3 +739,233 @@ func TestHTTPRouteParentRefChange(t *testing.T) { "Host": "test.foo", }, "") } + +func TestHTTPRouteRetryAndTimeout(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + + // infrastructure set up + listenerPort := 6018 + retryServiceHTTPPort := 6019 + retryServiceGRPCPort := 6020 + timeoutServiceHTTPPort := 6021 + timeoutServiceGRPCPort := 6022 + + retryServiceName := randomName("service", 16) + gatewayName := randomName("gw", 16) + retryRouteName := randomName("route", 16) + timeoutServiceName := randomName("service", 16) + timeoutRouteName := randomName("route", 16) + retryPath := "/retry" + timeoutPath := "/timeout" + + clusterConfig := &libtopology.ClusterConfig{ + NumServers: 1, + NumClients: 1, + BuildOpts: &libcluster.BuildOptions{ + Datacenter: "dc1", + InjectAutoEncryption: true, + InjectGossipEncryption: true, + AllowHTTPAnyway: true, + }, + ExposedPorts: []int{ + listenerPort, + retryServiceGRPCPort, + retryServiceHTTPPort, + timeoutServiceGRPCPort, + timeoutServiceHTTPPort, + }, + ApplyDefaultProxySettings: true, + } + + cluster, _, _ := libtopology.NewCluster(t, clusterConfig) + client := cluster.Agents[0].GetClient() + + namespace := getOrCreateNamespace(t, client) + + _, _, err := libservice.CreateAndRegisterCustomServiceAndSidecar( + cluster.Agents[0], &libservice.ServiceOpts{ + ID: retryServiceName, + Name: retryServiceName, + Namespace: namespace, + HTTPPort: retryServiceHTTPPort, + GRPCPort: retryServiceGRPCPort, + }, + testcontainers.ContainerRequest{ + Image: libservice.HashicorpDockerProxy + "/nicholasjackson/fake-service:v0.26.0", + Env: map[string]string{ + "LISTEN_ADDR": fmt.Sprintf("0.0.0.0:%d", retryServiceHTTPPort), + "ERROR_RATE": "0.5", + }, + }, + nil, + ) + require.NoError(t, err) + + _, _, err = libservice.CreateAndRegisterCustomServiceAndSidecar( + cluster.Agents[0], &libservice.ServiceOpts{ + ID: timeoutServiceName, + Name: timeoutServiceName, + Namespace: namespace, + HTTPPort: timeoutServiceHTTPPort, + GRPCPort: timeoutServiceGRPCPort, + }, + testcontainers.ContainerRequest{ + Image: libservice.HashicorpDockerProxy + "/nicholasjackson/fake-service:v0.26.0", + Env: map[string]string{ + "LISTEN_ADDR": fmt.Sprintf("0.0.0.0:%d", timeoutServiceHTTPPort), + "ERROR_RATE": "1.0", + "ERROR_DELAY": "1m", + }, + }, + nil, + ) + + require.NoError(t, err) + + // write config entries + proxyDefaults := &api.ProxyConfigEntry{ + Kind: api.ProxyDefaults, + Name: api.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + } + + require.NoError(t, cluster.ConfigEntryWrite(proxyDefaults)) + + apiGateway := &api.APIGatewayConfigEntry{ + Kind: "api-gateway", + Name: gatewayName, + Listeners: []api.APIGatewayListener{ + { + Name: "listener", + Port: listenerPort, + Protocol: "http", + }, + }, + Namespace: namespace, + } + + retryRoute := &api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: retryRouteName, + Namespace: namespace, + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: gatewayName, + Namespace: namespace, + }, + }, + Hostnames: []string{ + "test.foo", + "test.example", + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + RetryFilter: &api.RetryFilter{ + NumRetries: pointer.Uint32(10), + RetryOnStatusCodes: []uint32{500}, + }, + }, + Services: []api.HTTPService{ + { + Name: retryServiceName, + Namespace: namespace, + }, + }, + Matches: []api.HTTPMatch{ + { + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: retryPath, + }, + }, + }, + }, + }, + } + + timeoutRoute := &api.HTTPRouteConfigEntry{ + Kind: api.HTTPRoute, + Name: timeoutRouteName, + Namespace: namespace, + Parents: []api.ResourceReference{ + { + Kind: api.APIGateway, + Name: gatewayName, + Namespace: namespace, + }, + }, + Hostnames: []string{ + "test.foo", + "test.example", + }, + Rules: []api.HTTPRouteRule{ + { + Filters: api.HTTPFilters{ + TimeoutFilter: &api.TimeoutFilter{ + RequestTimeout: 1, + IdleTimeout: 1, + }, + }, + Services: []api.HTTPService{ + { + Name: timeoutServiceName, + Namespace: namespace, + }, + }, + Matches: []api.HTTPMatch{ + { + Path: api.HTTPPathMatch{ + Match: api.HTTPPathMatchPrefix, + Value: timeoutPath, + }, + }, + }, + }, + }, + } + + require.NoError(t, cluster.ConfigEntryWrite(apiGateway)) + require.NoError(t, cluster.ConfigEntryWrite(timeoutRoute)) + require.NoError(t, cluster.ConfigEntryWrite(retryRoute)) + + // create gateway service + gwCfg := libservice.GatewayConfig{ + Name: gatewayName, + Kind: "api", + Namespace: namespace, + } + gatewayService, err := libservice.NewGatewayService(context.Background(), gwCfg, cluster.Agents[0], listenerPort) + require.NoError(t, err) + libassert.CatalogServiceExists(t, client, gatewayName, &api.QueryOptions{Namespace: namespace}) + + // make sure config entries have been properly created + checkGatewayConfigEntry(t, client, gatewayName, &api.QueryOptions{Namespace: namespace}) + t.Log("checking retry route") + checkHTTPRouteConfigEntry(t, client, retryRouteName, &api.QueryOptions{Namespace: namespace}) + + t.Log("checking timeout route") + checkHTTPRouteConfigEntry(t, client, timeoutRouteName, &api.QueryOptions{Namespace: namespace}) + + // gateway resolves routes + gatewayPort, err := gatewayService.GetPort(listenerPort) + require.NoError(t, err) + fmt.Println("Gateway Port: ", gatewayPort) + + // hit service 1 by hitting root path + checkRoute(t, gatewayPort, retryPath, map[string]string{ + "Host": "test.foo", + }, checkOptions{debug: false, statusCode: 200, testName: "retry should succeed cleanly", expectedBody: "Hello World"}) + + checkRoute(t, gatewayPort, timeoutPath, map[string]string{ + "Host": "test.foo", + }, checkOptions{debug: false, statusCode: 500, testName: "timeout should timeout", expectedBody: "timeout"}) + +} From aff13cd4c2711f8a2fe2ab07659a0f37d4355d46 Mon Sep 17 00:00:00 2001 From: Ronald Date: Fri, 15 Sep 2023 13:49:22 -0400 Subject: [PATCH 07/28] Use embedded strings for templated policies (#18829) --- agent/acl_endpoint_test.go | 2 +- agent/structs/acl_templated_policy.go | 31 +++++++------------ .../acltemplatedpolicy/schemas/node.json | 13 ++++++++ .../acltemplatedpolicy/schemas/service.json | 13 ++++++++ command/acl/templatedpolicy/formatter_test.go | 8 ++--- .../read/templated_policy_read_test.go | 2 +- .../ce/node-templated-policy.json.golden | 2 +- .../node-templated-policy.pretty-meta.golden | 1 - .../ce/service-templated-policy.json.golden | 2 +- ...ervice-templated-policy.pretty-meta.golden | 1 - .../ce/list.json.golden | 4 +-- 11 files changed, 48 insertions(+), 31 deletions(-) create mode 100644 agent/structs/acltemplatedpolicy/schemas/node.json create mode 100644 agent/structs/acltemplatedpolicy/schemas/service.json diff --git a/agent/acl_endpoint_test.go b/agent/acl_endpoint_test.go index 50b2ad20d445..ca4fb3668d80 100644 --- a/agent/acl_endpoint_test.go +++ b/agent/acl_endpoint_test.go @@ -1378,7 +1378,7 @@ func TestACL_HTTP(t *testing.T) { require.Equal(t, api.ACLTemplatedPolicyResponse{ TemplateName: api.ACLTemplatedPolicyServiceName, - Schema: structs.ACLTemplatedPolicyIdentitiesSchema, + Schema: structs.ACLTemplatedPolicyServiceSchema, Template: structs.ACLTemplatedPolicyService, }, list[api.ACLTemplatedPolicyServiceName]) }) diff --git a/agent/structs/acl_templated_policy.go b/agent/structs/acl_templated_policy.go index 1e62c984ce29..fcb50032915d 100644 --- a/agent/structs/acl_templated_policy.go +++ b/agent/structs/acl_templated_policy.go @@ -5,6 +5,7 @@ package structs import ( "bytes" + _ "embed" "fmt" "hash" "hash/fnv" @@ -18,26 +19,17 @@ import ( "golang.org/x/exp/slices" ) +//go:embed acltemplatedpolicy/schemas/node.json +var ACLTemplatedPolicyNodeSchema string + +//go:embed acltemplatedpolicy/schemas/service.json +var ACLTemplatedPolicyServiceSchema string + type ACLTemplatedPolicies []*ACLTemplatedPolicy const ( - ACLTemplatedPolicyNodeID = "00000000-0000-0000-0000-000000000004" - ACLTemplatedPolicyServiceID = "00000000-0000-0000-0000-000000000003" - ACLTemplatedPolicyIdentitiesSchema = ` -{ - "type": "object", - "properties": { - "name": { "type": "string", "$ref": "#/definitions/min-length-one" } - }, - "required": ["name"], - "definitions": { - "min-length-one": { - "type": "string", - "minLength": 1 - } - } -}` - + ACLTemplatedPolicyServiceID = "00000000-0000-0000-0000-000000000003" + ACLTemplatedPolicyNodeID = "00000000-0000-0000-0000-000000000004" ACLTemplatedPolicyDNSID = "00000000-0000-0000-0000-000000000005" ACLTemplatedPolicyDNSSchema = "" // empty schema as it does not require variables ) @@ -59,13 +51,13 @@ var ( api.ACLTemplatedPolicyServiceName: { TemplateID: ACLTemplatedPolicyServiceID, TemplateName: api.ACLTemplatedPolicyServiceName, - Schema: ACLTemplatedPolicyIdentitiesSchema, + Schema: ACLTemplatedPolicyServiceSchema, Template: ACLTemplatedPolicyService, }, api.ACLTemplatedPolicyNodeName: { TemplateID: ACLTemplatedPolicyNodeID, TemplateName: api.ACLTemplatedPolicyNodeName, - Schema: ACLTemplatedPolicyIdentitiesSchema, + Schema: ACLTemplatedPolicyNodeSchema, Template: ACLTemplatedPolicyNode, }, api.ACLTemplatedPolicyDNSName: { @@ -273,6 +265,7 @@ func GetACLTemplatedPolicyBase(templateName string) (*ACLTemplatedPolicyBase, bo return nil, false } +// GetACLTemplatedPolicyList returns a copy of the list of templated policies func GetACLTemplatedPolicyList() map[string]*ACLTemplatedPolicyBase { m := make(map[string]*ACLTemplatedPolicyBase, len(aclTemplatedPoliciesList)) for k, v := range aclTemplatedPoliciesList { diff --git a/agent/structs/acltemplatedpolicy/schemas/node.json b/agent/structs/acltemplatedpolicy/schemas/node.json new file mode 100644 index 000000000000..8a3d19326821 --- /dev/null +++ b/agent/structs/acltemplatedpolicy/schemas/node.json @@ -0,0 +1,13 @@ +{ + "type": "object", + "properties": { + "name": { "type": "string", "$ref": "#/definitions/min-length-one" } + }, + "required": ["name"], + "definitions": { + "min-length-one": { + "type": "string", + "minLength": 1 + } + } +} \ No newline at end of file diff --git a/agent/structs/acltemplatedpolicy/schemas/service.json b/agent/structs/acltemplatedpolicy/schemas/service.json new file mode 100644 index 000000000000..8a3d19326821 --- /dev/null +++ b/agent/structs/acltemplatedpolicy/schemas/service.json @@ -0,0 +1,13 @@ +{ + "type": "object", + "properties": { + "name": { "type": "string", "$ref": "#/definitions/min-length-one" } + }, + "required": ["name"], + "definitions": { + "min-length-one": { + "type": "string", + "minLength": 1 + } + } +} \ No newline at end of file diff --git a/command/acl/templatedpolicy/formatter_test.go b/command/acl/templatedpolicy/formatter_test.go index d6e8fa4d0c20..887e518ea0bd 100644 --- a/command/acl/templatedpolicy/formatter_test.go +++ b/command/acl/templatedpolicy/formatter_test.go @@ -35,7 +35,7 @@ func testFormatTemplatedPolicy(t *testing.T, dirPath string) { "node-templated-policy": { templatedPolicy: api.ACLTemplatedPolicyResponse{ TemplateName: api.ACLTemplatedPolicyNodeName, - Schema: structs.ACLTemplatedPolicyIdentitiesSchema, + Schema: structs.ACLTemplatedPolicyNodeSchema, Template: structs.ACLTemplatedPolicyNode, }, }, @@ -49,7 +49,7 @@ func testFormatTemplatedPolicy(t *testing.T, dirPath string) { "service-templated-policy": { templatedPolicy: api.ACLTemplatedPolicyResponse{ TemplateName: api.ACLTemplatedPolicyServiceName, - Schema: structs.ACLTemplatedPolicyIdentitiesSchema, + Schema: structs.ACLTemplatedPolicyServiceSchema, Template: structs.ACLTemplatedPolicyService, }, }, @@ -89,7 +89,7 @@ func testFormatTemplatedPolicyList(t *testing.T, dirPath string) { policies := map[string]api.ACLTemplatedPolicyResponse{ "builtin/node": { TemplateName: api.ACLTemplatedPolicyNodeName, - Schema: structs.ACLTemplatedPolicyIdentitiesSchema, + Schema: structs.ACLTemplatedPolicyNodeSchema, Template: structs.ACLTemplatedPolicyNode, }, "builtin/dns": { @@ -99,7 +99,7 @@ func testFormatTemplatedPolicyList(t *testing.T, dirPath string) { }, "builtin/service": { TemplateName: api.ACLTemplatedPolicyServiceName, - Schema: structs.ACLTemplatedPolicyIdentitiesSchema, + Schema: structs.ACLTemplatedPolicyServiceSchema, Template: structs.ACLTemplatedPolicyService, }, } diff --git a/command/acl/templatedpolicy/read/templated_policy_read_test.go b/command/acl/templatedpolicy/read/templated_policy_read_test.go index 9059ed99c165..99ee66efc928 100644 --- a/command/acl/templatedpolicy/read/templated_policy_read_test.go +++ b/command/acl/templatedpolicy/read/templated_policy_read_test.go @@ -128,7 +128,7 @@ func TestTemplatedPolicyReadCommand_JSON(t *testing.T) { err := json.Unmarshal([]byte(output), &templatedPolicy) assert.NoError(t, err) - assert.Equal(t, structs.ACLTemplatedPolicyIdentitiesSchema, templatedPolicy.Schema) + assert.Equal(t, structs.ACLTemplatedPolicyNodeSchema, templatedPolicy.Schema) assert.Equal(t, api.ACLTemplatedPolicyNodeName, templatedPolicy.TemplateName) }) } diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.json.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.json.golden index 2643a2b9ee02..22981af04666 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.json.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.json.golden @@ -1,5 +1,5 @@ { "TemplateName": "builtin/node", - "Schema": "\n{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"name\": { \"type\": \"string\", \"$ref\": \"#/definitions/min-length-one\" }\n\t},\n\t\"required\": [\"name\"],\n\t\"definitions\": {\n\t\t\"min-length-one\": {\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"minLength\": 1\n\t\t}\n\t}\n}", + "Schema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"name\": { \"type\": \"string\", \"$ref\": \"#/definitions/min-length-one\" }\n\t},\n\t\"required\": [\"name\"],\n\t\"definitions\": {\n\t\t\"min-length-one\": {\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"minLength\": 1\n\t\t}\n\t}\n}", "Template": "\nnode \"{{.Name}}\" {\n\tpolicy = \"write\"\n}\nservice_prefix \"\" {\n\tpolicy = \"read\"\n}" } \ No newline at end of file diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty-meta.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty-meta.golden index ff42bd711f6d..fda0d9559e37 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty-meta.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/node-templated-policy.pretty-meta.golden @@ -4,7 +4,6 @@ Input variables: Example usage: consul acl token create -templated-policy builtin/node -var name:node-1 Schema: - { "type": "object", "properties": { diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.json.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.json.golden index a23b5f8c7ca0..e4b71de9b840 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.json.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.json.golden @@ -1,5 +1,5 @@ { "TemplateName": "builtin/service", - "Schema": "\n{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"name\": { \"type\": \"string\", \"$ref\": \"#/definitions/min-length-one\" }\n\t},\n\t\"required\": [\"name\"],\n\t\"definitions\": {\n\t\t\"min-length-one\": {\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"minLength\": 1\n\t\t}\n\t}\n}", + "Schema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"name\": { \"type\": \"string\", \"$ref\": \"#/definitions/min-length-one\" }\n\t},\n\t\"required\": [\"name\"],\n\t\"definitions\": {\n\t\t\"min-length-one\": {\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"minLength\": 1\n\t\t}\n\t}\n}", "Template": "\nservice \"{{.Name}}\" {\n\tpolicy = \"write\"\n}\nservice \"{{.Name}}-sidecar-proxy\" {\n\tpolicy = \"write\"\n}\nservice_prefix \"\" {\n\tpolicy = \"read\"\n}\nnode_prefix \"\" {\n\tpolicy = \"read\"\n}" } \ No newline at end of file diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty-meta.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty-meta.golden index 49b347efcb7c..f3ae5c6d7be2 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty-meta.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicy/ce/service-templated-policy.pretty-meta.golden @@ -4,7 +4,6 @@ Input variables: Example usage: consul acl token create -templated-policy builtin/service -var name:api Schema: - { "type": "object", "properties": { diff --git a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicyList/ce/list.json.golden b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicyList/ce/list.json.golden index 1cb724df8bde..b634ddc9d8f4 100644 --- a/command/acl/templatedpolicy/testdata/FormatTemplatedPolicyList/ce/list.json.golden +++ b/command/acl/templatedpolicy/testdata/FormatTemplatedPolicyList/ce/list.json.golden @@ -6,12 +6,12 @@ }, "builtin/node": { "TemplateName": "builtin/node", - "Schema": "\n{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"name\": { \"type\": \"string\", \"$ref\": \"#/definitions/min-length-one\" }\n\t},\n\t\"required\": [\"name\"],\n\t\"definitions\": {\n\t\t\"min-length-one\": {\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"minLength\": 1\n\t\t}\n\t}\n}", + "Schema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"name\": { \"type\": \"string\", \"$ref\": \"#/definitions/min-length-one\" }\n\t},\n\t\"required\": [\"name\"],\n\t\"definitions\": {\n\t\t\"min-length-one\": {\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"minLength\": 1\n\t\t}\n\t}\n}", "Template": "\nnode \"{{.Name}}\" {\n\tpolicy = \"write\"\n}\nservice_prefix \"\" {\n\tpolicy = \"read\"\n}" }, "builtin/service": { "TemplateName": "builtin/service", - "Schema": "\n{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"name\": { \"type\": \"string\", \"$ref\": \"#/definitions/min-length-one\" }\n\t},\n\t\"required\": [\"name\"],\n\t\"definitions\": {\n\t\t\"min-length-one\": {\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"minLength\": 1\n\t\t}\n\t}\n}", + "Schema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"name\": { \"type\": \"string\", \"$ref\": \"#/definitions/min-length-one\" }\n\t},\n\t\"required\": [\"name\"],\n\t\"definitions\": {\n\t\t\"min-length-one\": {\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"minLength\": 1\n\t\t}\n\t}\n}", "Template": "\nservice \"{{.Name}}\" {\n\tpolicy = \"write\"\n}\nservice \"{{.Name}}-sidecar-proxy\" {\n\tpolicy = \"write\"\n}\nservice_prefix \"\" {\n\tpolicy = \"read\"\n}\nnode_prefix \"\" {\n\tpolicy = \"read\"\n}" } } \ No newline at end of file From b2e21c103f1836676eb72149863bc845a59089e0 Mon Sep 17 00:00:00 2001 From: James Hartig Date: Fri, 15 Sep 2023 14:38:59 -0400 Subject: [PATCH 08/28] consul operator raft transfer-leader should send the id (#17107) Fixes #16955 Co-authored-by: Dhia Ayachi --- .changelog/17107.txt | 3 + api/operator_raft.go | 7 +- api/operator_raft_test.go | 187 ++++++++++++++++-- .../raft/transferleader/transfer_leader.go | 6 +- sdk/testutil/server.go | 53 ++++- 5 files changed, 232 insertions(+), 24 deletions(-) create mode 100644 .changelog/17107.txt diff --git a/.changelog/17107.txt b/.changelog/17107.txt new file mode 100644 index 000000000000..5694fca2c9cc --- /dev/null +++ b/.changelog/17107.txt @@ -0,0 +1,3 @@ +```release-note:breaking-change +api: RaftLeaderTransfer now requires an id string. An empty string can be specified to keep the old behavior. +``` diff --git a/api/operator_raft.go b/api/operator_raft.go index d72c00c97b93..f0f5794aa5af 100644 --- a/api/operator_raft.go +++ b/api/operator_raft.go @@ -68,9 +68,14 @@ func (op *Operator) RaftGetConfiguration(q *QueryOptions) (*RaftConfiguration, e } // RaftLeaderTransfer is used to transfer the current raft leader to another node -func (op *Operator) RaftLeaderTransfer(q *QueryOptions) (*TransferLeaderResponse, error) { +// Optionally accepts a non-empty id of another node to transfer leadership to. +func (op *Operator) RaftLeaderTransfer(id string, q *QueryOptions) (*TransferLeaderResponse, error) { r := op.c.newRequest("POST", "/v1/operator/raft/transfer-leader") r.setQueryOptions(q) + + if id != "" { + r.params.Set("id", id) + } _, resp, err := op.c.doRequest(r) if err != nil { return nil, err diff --git a/api/operator_raft_test.go b/api/operator_raft_test.go index 6e3b7fc0e310..eefffec0775d 100644 --- a/api/operator_raft_test.go +++ b/api/operator_raft_test.go @@ -4,8 +4,9 @@ package api import ( - "strings" "testing" + + "github.com/hashicorp/consul/sdk/testutil" ) func TestAPI_OperatorRaftGetConfiguration(t *testing.T) { @@ -27,33 +28,181 @@ func TestAPI_OperatorRaftGetConfiguration(t *testing.T) { func TestAPI_OperatorRaftRemovePeerByAddress(t *testing.T) { t.Parallel() - c, s := makeClient(t) - defer s.Stop() + c1, s1 := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { + if conf.Autopilot == nil { + conf.Autopilot = &testutil.TestAutopilotConfig{} + } + conf.Autopilot.ServerStabilizationTime = "1ms" + }) + defer s1.Stop() - // If we get this error, it proves we sent the address all the way - // through. - operator := c.Operator() - err := operator.RaftRemovePeerByAddress("nope", nil) - if err == nil || !strings.Contains(err.Error(), - "address \"nope\" was not found in the Raft configuration") { + _, s2 := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { + conf.Server = true + conf.Bootstrap = false + conf.RetryJoin = []string{s1.LANAddr} + if conf.Autopilot == nil { + conf.Autopilot = &testutil.TestAutopilotConfig{} + } + conf.Autopilot.ServerStabilizationTime = "1ms" + }) + defer s2.Stop() + s2.WaitForVoting(t) + + operator := c1.Operator() + err := operator.RaftRemovePeerByAddress(s2.ServerAddr, nil) + if err != nil { t.Fatalf("err: %v", err) } + + cfg, err := c1.Operator().RaftGetConfiguration(nil) + if err != nil { + t.Fatalf("err: %v", err) + } + if len(cfg.Servers) != 1 { + t.Fatalf("more than 1 server left: %+v", cfg.Servers) + } +} + +func TestAPI_OperatorRaftRemovePeerByID(t *testing.T) { + t.Parallel() + c1, s1 := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { + if conf.Autopilot == nil { + conf.Autopilot = &testutil.TestAutopilotConfig{} + } + conf.Autopilot.ServerStabilizationTime = "1ms" + }) + defer s1.Stop() + + _, s2 := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { + conf.Server = true + conf.Bootstrap = false + conf.RetryJoin = []string{s1.LANAddr} + if conf.Autopilot == nil { + conf.Autopilot = &testutil.TestAutopilotConfig{} + } + conf.Autopilot.ServerStabilizationTime = "1ms" + }) + defer s2.Stop() + s2.WaitForVoting(t) + + operator := c1.Operator() + err := operator.RaftRemovePeerByID(s2.Config.NodeID, nil) + if err != nil { + t.Fatalf("err: %v", err) + } + + cfg, err := c1.Operator().RaftGetConfiguration(nil) + if err != nil { + t.Fatalf("err: %v", err) + } + if len(cfg.Servers) != 1 { + t.Fatalf("more than 1 server left: %+v", cfg.Servers) + } } func TestAPI_OperatorRaftLeaderTransfer(t *testing.T) { t.Parallel() - c, s := makeClient(t) - defer s.Stop() + c1, s1 := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { + if conf.Autopilot == nil { + conf.Autopilot = &testutil.TestAutopilotConfig{} + } + conf.Autopilot.ServerStabilizationTime = "1ms" + }) + defer s1.Stop() - // If we get this error, it proves we sent the address all the way - // through. - operator := c.Operator() - transfer, err := operator.RaftLeaderTransfer(nil) - if err == nil || !strings.Contains(err.Error(), - "cannot find peer") { + _, s2 := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { + conf.Server = true + conf.Bootstrap = false + conf.RetryJoin = []string{s1.LANAddr} + if conf.Autopilot == nil { + conf.Autopilot = &testutil.TestAutopilotConfig{} + } + conf.Autopilot.ServerStabilizationTime = "1ms" + }) + defer s2.Stop() + s2.WaitForVoting(t) + + cfg, err := c1.Operator().RaftGetConfiguration(nil) + if err != nil { t.Fatalf("err: %v", err) } - if transfer != nil { - t.Fatalf("err:%v", transfer) + if len(cfg.Servers) != 2 { + t.Fatalf("not 2 servers: %#v", cfg.Servers) + } + var leaderID string + for _, srv := range cfg.Servers { + if srv.Leader { + leaderID = srv.ID + } + } + if leaderID == "" { + t.Fatalf("no leader: %+v", cfg.Servers) + } + + transfer, err := c1.Operator().RaftLeaderTransfer("", nil) + if err != nil { + t.Fatalf("err: %v", err) + } + if !transfer.Success { + t.Fatal("unsuccessful transfer") + } + + s2.WaitForLeader(t) + + cfg, err = c1.Operator().RaftGetConfiguration(nil) + if err != nil { + t.Fatalf("err: %v", err) + } + var newLeaderID string + for _, srv := range cfg.Servers { + if srv.Leader { + newLeaderID = srv.ID + } + } + if newLeaderID == "" { + t.Fatalf("no leader: %#v", cfg.Servers) + } + if newLeaderID == leaderID { + t.Fatalf("leader did not change: %v == %v", newLeaderID, leaderID) + } + + _, s3 := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { + conf.Server = true + conf.Bootstrap = false + conf.RetryJoin = []string{s1.LANAddr, s2.LANAddr} + if conf.Autopilot == nil { + conf.Autopilot = &testutil.TestAutopilotConfig{} + } + conf.Autopilot.ServerStabilizationTime = "1ms" + }) + defer s3.Stop() + s3.WaitForVoting(t) + + // Transfer it to another member + transfer, err = c1.Operator().RaftLeaderTransfer(s3.Config.NodeID, nil) + if err != nil { + t.Fatalf("err: %v", err) + } + if !transfer.Success { + t.Fatal("unsuccessful transfer") + } + + s3.WaitForLeader(t) + + cfg, err = c1.Operator().RaftGetConfiguration(nil) + if err != nil { + t.Fatalf("err: %v", err) + } + newLeaderID = "" + for _, srv := range cfg.Servers { + if srv.Leader { + newLeaderID = srv.ID + } + } + if newLeaderID == "" { + t.Fatalf("no leader: %#v", cfg.Servers) + } + if newLeaderID != s3.Config.NodeID { + t.Fatalf("leader is not s3: %v != %v", newLeaderID, s3.Config.NodeID) } } diff --git a/command/operator/raft/transferleader/transfer_leader.go b/command/operator/raft/transferleader/transfer_leader.go index ebeb77dfc82c..c7694b65a6ed 100644 --- a/command/operator/raft/transferleader/transfer_leader.go +++ b/command/operator/raft/transferleader/transfer_leader.go @@ -53,7 +53,7 @@ func (c *cmd) Run(args []string) int { } // Fetch the current configuration. - result, err := raftTransferLeader(client, c.http.Stale()) + result, err := raftTransferLeader(client, c.http.Stale(), c.id) if err != nil { c.UI.Error(fmt.Sprintf("Error transfering leadership: %v", err)) return 1 @@ -63,11 +63,11 @@ func (c *cmd) Run(args []string) int { return 0 } -func raftTransferLeader(client *api.Client, stale bool) (string, error) { +func raftTransferLeader(client *api.Client, stale bool, id string) (string, error) { q := &api.QueryOptions{ AllowStale: stale, } - reply, err := client.Operator().RaftLeaderTransfer(q) + reply, err := client.Operator().RaftLeaderTransfer(id, q) if err != nil { return "", fmt.Errorf("Failed to transfer leadership %w", err) } diff --git a/sdk/testutil/server.go b/sdk/testutil/server.go index 148fb03d6a95..91ef5180e652 100644 --- a/sdk/testutil/server.go +++ b/sdk/testutil/server.go @@ -86,6 +86,11 @@ type Locality struct { Zone string `json:"zone"` } +// TestAutopilotConfig contains the configuration for autopilot. +type TestAutopilotConfig struct { + ServerStabilizationTime string `json:"server_stabilization_time,omitempty"` +} + // TestServerConfig is the main server configuration struct. type TestServerConfig struct { NodeName string `json:"node_name"` @@ -123,6 +128,7 @@ type TestServerConfig struct { EnableDebug bool `json:"enable_debug,omitempty"` SkipLeaveOnInt bool `json:"skip_leave_on_interrupt"` Peering *TestPeeringConfig `json:"peering,omitempty"` + Autopilot *TestAutopilotConfig `json:"autopilot,omitempty"` ReadyTimeout time.Duration `json:"-"` StopTimeout time.Duration `json:"-"` Stdout io.Writer `json:"-"` @@ -260,6 +266,7 @@ type TestServer struct { HTTPSAddr string LANAddr string WANAddr string + ServerAddr string GRPCAddr string GRPCTLSAddr string @@ -344,6 +351,7 @@ func NewTestServerConfigT(t TestingTB, cb ServerConfigCallback) (*TestServer, er HTTPSAddr: fmt.Sprintf("127.0.0.1:%d", cfg.Ports.HTTPS), LANAddr: fmt.Sprintf("127.0.0.1:%d", cfg.Ports.SerfLan), WANAddr: fmt.Sprintf("127.0.0.1:%d", cfg.Ports.SerfWan), + ServerAddr: fmt.Sprintf("127.0.0.1:%d", cfg.Ports.Server), GRPCAddr: fmt.Sprintf("127.0.0.1:%d", cfg.Ports.GRPC), GRPCTLSAddr: fmt.Sprintf("127.0.0.1:%d", cfg.Ports.GRPCTLS), @@ -442,7 +450,7 @@ func (s *TestServer) waitForAPI() error { return nil } -// waitForLeader waits for the Consul server's HTTP API to become +// WaitForLeader waits for the Consul server's HTTP API to become // available, and then waits for a known leader and an index of // 2 or more to be observed to confirm leader election is done. func (s *TestServer) WaitForLeader(t testing.TB) { @@ -472,6 +480,49 @@ func (s *TestServer) WaitForLeader(t testing.TB) { }) } +// WaitForVoting waits for the Consul server to become a voter in the current raft +// configuration. You probably want to adjust the ServerStablizationTime autopilot +// configuration otherwise this could take 10 seconds. +func (s *TestServer) WaitForVoting(t testing.TB) { + // don't need to fully decode the response + type raftServer struct { + ID string + Voter bool + } + type raftCfgResponse struct { + Servers []raftServer + } + + retry.Run(t, func(r *retry.R) { + // Query the API and get the current raft configuration. + url := s.url("/v1/operator/raft/configuration") + resp, err := s.privilegedGet(url) + if err != nil { + r.Fatalf("failed http get '%s': %v", url, err) + } + defer resp.Body.Close() + if err := s.requireOK(resp); err != nil { + r.Fatalf("failed OK response: %v", err) + } + + var cfg raftCfgResponse + dec := json.NewDecoder(resp.Body) + if err := dec.Decode(&cfg); err != nil { + r.Fatal(err) + } + + for _, srv := range cfg.Servers { + if srv.ID == s.Config.NodeID { + if srv.Voter { + return + } + break + } + } + r.Fatalf("Server is not voting: %#v", cfg.Servers) + }) +} + // WaitForActiveCARoot waits until the server can return a Connect CA meaning // connect has completed bootstrapping and is ready to use. func (s *TestServer) WaitForActiveCARoot(t testing.TB) { From 1fda2965e817f41fef2ef0365bed8f02d71ace80 Mon Sep 17 00:00:00 2001 From: skpratt Date: Fri, 15 Sep 2023 14:00:23 -0500 Subject: [PATCH 09/28] Allow empty data writes for resources (#18819) * allow nil data writes for resources * update demo to test valid type with no data --- .../grpc-external/services/resource/write.go | 4 +- .../services/resource/write_test.go | 19 ++- internal/resource/demo/demo.go | 33 +++++ proto/private/pbdemo/v1/demo.pb.binary.go | 10 ++ proto/private/pbdemo/v1/demo.pb.go | 124 +++++++++++++----- proto/private/pbdemo/v1/demo.proto | 2 + 6 files changed, 149 insertions(+), 43 deletions(-) diff --git a/agent/grpc-external/services/resource/write.go b/agent/grpc-external/services/resource/write.go index ec39f3a12a85..eb66f355ff1c 100644 --- a/agent/grpc-external/services/resource/write.go +++ b/agent/grpc-external/services/resource/write.go @@ -59,7 +59,7 @@ func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbre } // Check the user sent the correct type of data. - if !req.Resource.Data.MessageIs(reg.Proto) { + if req.Resource.Data != nil && !req.Resource.Data.MessageIs(reg.Proto) { got := strings.TrimPrefix(req.Resource.Data.TypeUrl, "type.googleapis.com/") return nil, status.Errorf( @@ -272,8 +272,6 @@ func (s *Server) validateWriteRequest(req *pbresource.WriteRequest) (*resource.R field = "resource" case req.Resource.Id == nil: field = "resource.id" - case req.Resource.Data == nil: - field = "resource.data" } if field != "" { diff --git a/agent/grpc-external/services/resource/write_test.go b/agent/grpc-external/services/resource/write_test.go index 49a526e63d2b..5a5e8d318098 100644 --- a/agent/grpc-external/services/resource/write_test.go +++ b/agent/grpc-external/services/resource/write_test.go @@ -46,10 +46,6 @@ func TestWrite_InputValidation(t *testing.T) { artist.Id.Name = "" return artist }, - "no data": func(artist, _ *pbresource.Resource) *pbresource.Resource { - artist.Data = nil - return artist - }, "wrong data type": func(artist, _ *pbresource.Resource) *pbresource.Resource { var err error artist.Data, err = anypb.New(&pbdemov2.Album{}) @@ -684,6 +680,21 @@ func TestWrite_NonCASUpdate_Retry(t *testing.T) { require.NoError(t, <-errCh) } +func TestWrite_NoData(t *testing.T) { + server := testServer(t) + client := testClient(t, server) + + demo.RegisterTypes(server.Registry) + + res, err := demo.GenerateV1Concept("jazz") + require.NoError(t, err) + + rsp, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: res}) + require.NoError(t, err) + require.NotEmpty(t, rsp.Resource.Version) + require.Equal(t, rsp.Resource.Id.Name, "jazz") +} + func TestWrite_Owner_Immutable(t *testing.T) { // Use of proto.Equal(..) in implementation covers all permutations // (nil -> non-nil, non-nil -> nil, owner1 -> owner2) so only the first one diff --git a/internal/resource/demo/demo.go b/internal/resource/demo/demo.go index 145b05e4710e..b193fd9e31af 100644 --- a/internal/resource/demo/demo.go +++ b/internal/resource/demo/demo.go @@ -44,6 +44,13 @@ var ( Kind: "Album", } + // TypeV1Concept represents an abstract concept that can be associated with any other resource. + TypeV1Concept = &pbresource.Type{ + Group: "demo", + GroupVersion: "v1", + Kind: "Concept", + } + // TypeV2Artist represents a musician or group of musicians. TypeV2Artist = &pbresource.Type{ Group: "demo", @@ -159,6 +166,17 @@ func RegisterTypes(r resource.Registry) { }, }) + r.Register(resource.Registration{ + Type: TypeV1Concept, + Proto: &pbdemov1.Concept{}, + Scope: resource.ScopeNamespace, + ACLs: &resource.ACLHooks{ + Read: readACL, + Write: writeACL, + List: makeListACL(TypeV1Concept), + }, + }) + r.Register(resource.Registration{ Type: TypeV2Artist, Proto: &pbdemov2.Artist{}, @@ -204,6 +222,21 @@ func GenerateV1RecordLabel(name string) (*pbresource.Resource, error) { }, nil } +// GenerateV1Concept generates a named concept resource. +func GenerateV1Concept(name string) (*pbresource.Resource, error) { + return &pbresource.Resource{ + Id: &pbresource.ID{ + Type: TypeV1Concept, + Tenancy: resource.DefaultPartitionedTenancy(), + Name: name, + }, + Data: nil, + Metadata: map[string]string{ + "generated_at": time.Now().Format(time.RFC3339), + }, + }, nil +} + // GenerateV2Artist generates a random Artist resource. func GenerateV2Artist() (*pbresource.Resource, error) { adjective := adjectives[rand.Intn(len(adjectives))] diff --git a/proto/private/pbdemo/v1/demo.pb.binary.go b/proto/private/pbdemo/v1/demo.pb.binary.go index b52d5f33496e..7f20c0ef008c 100644 --- a/proto/private/pbdemo/v1/demo.pb.binary.go +++ b/proto/private/pbdemo/v1/demo.pb.binary.go @@ -36,3 +36,13 @@ func (msg *Album) MarshalBinary() ([]byte, error) { func (msg *Album) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } + +// MarshalBinary implements encoding.BinaryMarshaler +func (msg *Concept) MarshalBinary() ([]byte, error) { + return proto.Marshal(msg) +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler +func (msg *Concept) UnmarshalBinary(b []byte) error { + return proto.Unmarshal(b, msg) +} diff --git a/proto/private/pbdemo/v1/demo.pb.go b/proto/private/pbdemo/v1/demo.pb.go index 0916da157ccf..34bfabd7ec9e 100644 --- a/proto/private/pbdemo/v1/demo.pb.go +++ b/proto/private/pbdemo/v1/demo.pb.go @@ -302,6 +302,44 @@ func (x *Album) GetTracks() []string { return nil } +type Concept struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Concept) Reset() { + *x = Concept{} + if protoimpl.UnsafeEnabled { + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Concept) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Concept) ProtoMessage() {} + +func (x *Concept) ProtoReflect() protoreflect.Message { + mi := &file_private_pbdemo_v1_demo_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Concept.ProtoReflect.Descriptor instead. +func (*Concept) Descriptor() ([]byte, []int) { + return file_private_pbdemo_v1_demo_proto_rawDescGZIP(), []int{3} +} + var File_private_pbdemo_v1_demo_proto protoreflect.FileDescriptor var file_private_pbdemo_v1_demo_proto_rawDesc = []byte{ @@ -332,40 +370,41 @@ var file_private_pbdemo_v1_demo_proto_rawDesc = []byte{ 0x5f, 0x61, 0x63, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x41, 0x63, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x2a, 0xe9, 0x01, - 0x0a, 0x05, 0x47, 0x65, 0x6e, 0x72, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x45, 0x4e, 0x52, 0x45, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, - 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4a, 0x41, 0x5a, 0x5a, 0x10, 0x01, 0x12, 0x0e, - 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x46, 0x4f, 0x4c, 0x4b, 0x10, 0x02, 0x12, 0x0d, - 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, 0x4f, 0x50, 0x10, 0x03, 0x12, 0x0f, 0x0a, - 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x10, 0x04, 0x12, 0x0e, - 0x0a, 0x0a, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x50, 0x55, 0x4e, 0x4b, 0x10, 0x05, 0x12, 0x0f, - 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x42, 0x4c, 0x55, 0x45, 0x53, 0x10, 0x06, 0x12, - 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x52, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x42, - 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, - 0x54, 0x52, 0x59, 0x10, 0x08, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x44, - 0x49, 0x53, 0x43, 0x4f, 0x10, 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, - 0x53, 0x4b, 0x41, 0x10, 0x0a, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x48, - 0x49, 0x50, 0x5f, 0x48, 0x4f, 0x50, 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, - 0x45, 0x5f, 0x49, 0x4e, 0x44, 0x49, 0x45, 0x10, 0x0c, 0x42, 0x97, 0x02, 0x0a, 0x25, 0x63, 0x6f, - 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, - 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, - 0x2e, 0x76, 0x31, 0x42, 0x09, 0x44, 0x65, 0x6d, 0x6f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x3a, 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, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x62, 0x64, 0x65, - 0x6d, 0x6f, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x65, 0x6d, 0x6f, 0x76, 0x31, 0xa2, 0x02, 0x04, 0x48, - 0x43, 0x49, 0x44, 0xaa, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, - 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, - 0x44, 0x65, 0x6d, 0x6f, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x2d, 0x48, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0x5c, - 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x25, 0x48, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, - 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x44, 0x65, 0x6d, 0x6f, 0x3a, - 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x22, 0x09, 0x0a, + 0x07, 0x43, 0x6f, 0x6e, 0x63, 0x65, 0x70, 0x74, 0x2a, 0xe9, 0x01, 0x0a, 0x05, 0x47, 0x65, 0x6e, + 0x72, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, + 0x52, 0x45, 0x5f, 0x4a, 0x41, 0x5a, 0x5a, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, + 0x52, 0x45, 0x5f, 0x46, 0x4f, 0x4c, 0x4b, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, + 0x52, 0x45, 0x5f, 0x50, 0x4f, 0x50, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, + 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x4c, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x47, 0x45, 0x4e, + 0x52, 0x45, 0x5f, 0x50, 0x55, 0x4e, 0x4b, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, + 0x52, 0x45, 0x5f, 0x42, 0x4c, 0x55, 0x45, 0x53, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, + 0x4e, 0x52, 0x45, 0x5f, 0x52, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x42, 0x10, 0x07, 0x12, 0x11, 0x0a, + 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x52, 0x59, 0x10, 0x08, + 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x10, + 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x53, 0x4b, 0x41, 0x10, 0x0a, + 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x48, 0x49, 0x50, 0x5f, 0x48, 0x4f, + 0x50, 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x44, + 0x49, 0x45, 0x10, 0x0c, 0x42, 0x97, 0x02, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x76, 0x31, 0x42, 0x09, + 0x44, 0x65, 0x6d, 0x6f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 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, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x62, 0x64, 0x65, 0x6d, 0x6f, 0x2f, 0x76, 0x31, + 0x3b, 0x64, 0x65, 0x6d, 0x6f, 0x76, 0x31, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x44, 0xaa, 0x02, + 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x44, 0x65, 0x6d, 0x6f, 0x2e, + 0x56, 0x31, 0xca, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, + 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x44, + 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x5c, 0x44, 0x65, 0x6d, 0x6f, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x25, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x44, 0x65, 0x6d, 0x6f, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -381,12 +420,13 @@ func file_private_pbdemo_v1_demo_proto_rawDescGZIP() []byte { } var file_private_pbdemo_v1_demo_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_private_pbdemo_v1_demo_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_private_pbdemo_v1_demo_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_private_pbdemo_v1_demo_proto_goTypes = []interface{}{ (Genre)(0), // 0: hashicorp.consul.internal.demo.v1.Genre (*RecordLabel)(nil), // 1: hashicorp.consul.internal.demo.v1.RecordLabel (*Artist)(nil), // 2: hashicorp.consul.internal.demo.v1.Artist (*Album)(nil), // 3: hashicorp.consul.internal.demo.v1.Album + (*Concept)(nil), // 4: hashicorp.consul.internal.demo.v1.Concept } var file_private_pbdemo_v1_demo_proto_depIdxs = []int32{ 0, // 0: hashicorp.consul.internal.demo.v1.Artist.genre:type_name -> hashicorp.consul.internal.demo.v1.Genre @@ -439,6 +479,18 @@ func file_private_pbdemo_v1_demo_proto_init() { return nil } } + file_private_pbdemo_v1_demo_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Concept); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -446,7 +498,7 @@ func file_private_pbdemo_v1_demo_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_private_pbdemo_v1_demo_proto_rawDesc, NumEnums: 1, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/private/pbdemo/v1/demo.proto b/proto/private/pbdemo/v1/demo.proto index b41122b904da..3b6e978477e5 100644 --- a/proto/private/pbdemo/v1/demo.proto +++ b/proto/private/pbdemo/v1/demo.proto @@ -41,3 +41,5 @@ message Album { bool critically_acclaimed = 3; repeated string tracks = 4; } + +message Concept {} From edf56ee970ba15c846f412a347f043d754d69179 Mon Sep 17 00:00:00 2001 From: "Chris S. Kim" Date: Fri, 15 Sep 2023 15:23:49 -0400 Subject: [PATCH 10/28] Fix nondeterministic test (#18828) --- command/operator/usage/instances/usage_instances.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/command/operator/usage/instances/usage_instances.go b/command/operator/usage/instances/usage_instances.go index 83231db0a1d4..4926f85ead9c 100644 --- a/command/operator/usage/instances/usage_instances.go +++ b/command/operator/usage/instances/usage_instances.go @@ -11,9 +11,11 @@ import ( "strings" "text/tabwriter" + "github.com/mitchellh/cli" + "golang.org/x/exp/maps" + "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/command/flags" - "github.com/mitchellh/cli" ) func New(ui cli.Ui) *cmd { @@ -135,9 +137,11 @@ func formatNodesCounts(usageStats map[string]api.ServiceUsage) (string, error) { fmt.Fprint(tw, "\t\n") - for dc, usage := range usageStats { - nodesTotal += usage.Nodes - fmt.Fprintf(tw, "%s\t%d\n", dc, usage.Nodes) + nodes := maps.Keys(usageStats) + sort.Strings(nodes) + for _, dc := range nodes { + nodesTotal += usageStats[dc].Nodes + fmt.Fprintf(tw, "%s\t%d\n", dc, usageStats[dc].Nodes) } fmt.Fprint(tw, "\t\n") From 5cde50dee7879f841d3c4aaeb28f038f029c9d66 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" <4903+rboyer@users.noreply.github.com> Date: Fri, 15 Sep 2023 15:13:01 -0500 Subject: [PATCH 11/28] mesh: prevent writing a ComputedRoutes with no ported configs (#18833) --- .../internal/controllers/routes/generate.go | 8 ++++++++ .../controllers/routes/generate_test.go | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/internal/mesh/internal/controllers/routes/generate.go b/internal/mesh/internal/controllers/routes/generate.go index 42ecc56ff701..afd66e2e4a18 100644 --- a/internal/mesh/internal/controllers/routes/generate.go +++ b/internal/mesh/internal/controllers/routes/generate.go @@ -314,6 +314,14 @@ func compile( computedRoutes.PortedConfigs[port] = mc } + if len(computedRoutes.PortedConfigs) == 0 { + // This service only exposes a "mesh" port, so it cannot be another service's upstream. + return &ComputedRoutesResult{ + ID: computedRoutesID, + Data: nil, // returning nil signals a delete is requested + } + } + return &ComputedRoutesResult{ ID: computedRoutesID, OwnerID: parentServiceID, diff --git a/internal/mesh/internal/controllers/routes/generate_test.go b/internal/mesh/internal/controllers/routes/generate_test.go index e46bc57f0119..ed9b565f6f26 100644 --- a/internal/mesh/internal/controllers/routes/generate_test.go +++ b/internal/mesh/internal/controllers/routes/generate_test.go @@ -160,6 +160,24 @@ func TestGenerateComputedRoutes(t *testing.T) { run(t, related, expect, nil) }) + t.Run("aligned service in mesh but no actual ports", func(t *testing.T) { + related := loader.NewRelatedResources(). + AddComputedRoutesIDs(apiComputedRoutesID). + AddResources(newService("api", &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Prefixes: []string{"api-"}, + }, + Ports: []*pbcatalog.ServicePort{ + {TargetPort: "mesh", Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + })) + expect := []*ComputedRoutesResult{{ + ID: apiComputedRoutesID, + Data: nil, + }} + run(t, related, expect, nil) + }) + t.Run("tcp service with default route", func(t *testing.T) { apiServiceData := &pbcatalog.Service{ Workloads: &pbcatalog.WorkloadSelector{ From a89938e0c1ca611d74bd25f8a23a7736a72efe06 Mon Sep 17 00:00:00 2001 From: Iryna Shustava Date: Fri, 15 Sep 2023 15:11:56 -0600 Subject: [PATCH 12/28] catalog: Default protocol to tcp in catalog.Service if unspecified (#18832) --- internal/catalog/internal/types/service.go | 25 ++++++++++++++ .../catalog/internal/types/service_test.go | 33 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/internal/catalog/internal/types/service.go b/internal/catalog/internal/types/service.go index 3ff89be38d3f..1ee9a2a5d823 100644 --- a/internal/catalog/internal/types/service.go +++ b/internal/catalog/internal/types/service.go @@ -33,9 +33,34 @@ func RegisterService(r resource.Registry) { Proto: &pbcatalog.Service{}, Scope: resource.ScopeNamespace, Validate: ValidateService, + Mutate: MutateService, }) } +func MutateService(res *pbresource.Resource) error { + var service pbcatalog.Service + + if err := res.Data.UnmarshalTo(&service); err != nil { + return err + } + + changed := false + + // Default service port protocols. + for _, port := range service.Ports { + if port.Protocol == pbcatalog.Protocol_PROTOCOL_UNSPECIFIED { + port.Protocol = pbcatalog.Protocol_PROTOCOL_TCP + changed = true + } + } + + if !changed { + return nil + } + + return res.Data.MarshalFrom(&service) +} + func ValidateService(res *pbresource.Resource) error { var service pbcatalog.Service diff --git a/internal/catalog/internal/types/service_test.go b/internal/catalog/internal/types/service_test.go index 4f53a5c0a62b..f97135a8ca22 100644 --- a/internal/catalog/internal/types/service_test.go +++ b/internal/catalog/internal/types/service_test.go @@ -11,6 +11,7 @@ import ( "google.golang.org/protobuf/types/known/anypb" "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" "github.com/hashicorp/consul/proto-public/pbresource" ) @@ -34,6 +35,38 @@ func createServiceResource(t *testing.T, data protoreflect.ProtoMessage) *pbreso return res } +func TestMutateServicePorts(t *testing.T) { + data := &pbcatalog.Service{ + Workloads: &pbcatalog.WorkloadSelector{ + Names: []string{"foo", "bar"}, + }, + Ports: []*pbcatalog.ServicePort{ + { + TargetPort: "tcp", + Protocol: pbcatalog.Protocol_PROTOCOL_UNSPECIFIED, + }, + { + TargetPort: "http", + Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, + }, + }, + VirtualIps: []string{"198.18.0.1"}, + } + + res := createServiceResource(t, data) + + err := MutateService(res) + require.NoError(t, err) + + got := resourcetest.MustDecode[*pbcatalog.Service](t, res) + + require.Len(t, got.Data.Ports, 2) + require.Equal(t, pbcatalog.Protocol_PROTOCOL_TCP, got.Data.Ports[0].Protocol) + + // Check that specified protocol is not mutated. + require.Equal(t, data.Ports[1].Protocol, got.Data.Ports[1].Protocol) +} + func TestValidateService_Ok(t *testing.T) { data := &pbcatalog.Service{ Workloads: &pbcatalog.WorkloadSelector{ From 461549e304e72fcd03f323ccd521f9bc0e5f1b48 Mon Sep 17 00:00:00 2001 From: "Chris S. Kim" Date: Fri, 15 Sep 2023 19:15:42 -0400 Subject: [PATCH 13/28] Adjust metrics test (#18837) --- agent/metrics_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/agent/metrics_test.go b/agent/metrics_test.go index 41013b3c7069..113699fa0ec3 100644 --- a/agent/metrics_test.go +++ b/agent/metrics_test.go @@ -35,9 +35,8 @@ func recordPromMetrics(t *testing.T, a *TestAgent, respRec *httptest.ResponseRec req, err := http.NewRequest("GET", "/v1/agent/metrics?format=prometheus", nil) require.NoError(t, err, "Failed to generate new http request.") - _, err = a.srv.AgentMetrics(respRec, req) - require.NoError(t, err, "Failed to serve agent metrics") - + a.srv.h.ServeHTTP(respRec, req) + require.Equalf(t, 200, respRec.Code, "expected 200, got %d, body: %s", respRec.Code, respRec.Body.String()) } func assertMetricExists(t *testing.T, respRec *httptest.ResponseRecorder, metric string) { From 5d99fb7bdf158b9b3771c4f9defc0fdf1cd60d8c Mon Sep 17 00:00:00 2001 From: Ashesh Vidyut <134911583+absolutelightning@users.noreply.github.com> Date: Sat, 16 Sep 2023 10:14:27 +0530 Subject: [PATCH 14/28] Audit log consul 1.17x changes (#18669) * audit log consul 1.17x changes * added some details * verbose --- website/content/docs/upgrading/upgrade-specific.mdx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/website/content/docs/upgrading/upgrade-specific.mdx b/website/content/docs/upgrading/upgrade-specific.mdx index ce09e9d1c441..ee1efacc8053 100644 --- a/website/content/docs/upgrading/upgrade-specific.mdx +++ b/website/content/docs/upgrading/upgrade-specific.mdx @@ -14,6 +14,11 @@ provided for their upgrades as a result of new features or changed behavior. This page is used to document those details separately from the standard upgrade flow. +## Consul 1.17.x +#### Audit Log naming changes (Enterprise) +Prior to Consul 1.17.0, audit logs contained timestamps on both the original log file names as well as rotated log file names. +After Consul 1.17.0, only timestamps will be included in rotated log file names. + ## Consul 1.16.x #### Known issues @@ -783,7 +788,7 @@ Starting with Consul 1.7.1 this is the new default. #### Removal of Deprecated Features -Managed proxies, which are deprecated since Consul v1.3.0, have now been +Managed proxies, which are deprecated since Consul v1.3.0, have now been [removed](/consul/docs/connect/proxies). Before upgrading, you must migrate any managed proxy usage to [sidecar service registrations](/consul/docs/connect/proxies/deploy-sidecar-services). From 0018b7e5a83e5f5434820a97b64fdff26525d51b Mon Sep 17 00:00:00 2001 From: Ashesh Vidyut <134911583+absolutelightning@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:45:22 +0530 Subject: [PATCH 15/28] Fixes for integration tests windows for ENT (#18839) * fixes for integration tests * fix runner size for enterprise * fix spacing * fix spacing * removed branch test run --- .github/scripts/get_runner_classes_windows.sh | 8 ++++---- .github/workflows/test-integrations-windows.yml | 5 +++++ build-support/windows/build-consul-dev-image.sh | 9 --------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/scripts/get_runner_classes_windows.sh b/.github/scripts/get_runner_classes_windows.sh index 47b1d15b0d5d..ae75625974cf 100755 --- a/.github/scripts/get_runner_classes_windows.sh +++ b/.github/scripts/get_runner_classes_windows.sh @@ -10,11 +10,11 @@ set -euo pipefail case "$GITHUB_REPOSITORY" in *-enterprise) # shellcheck disable=SC2129 - echo "compute-small=['self-hosted', 'ondemand', 'os=windows-2019', 'type=m6a.4xlarge']" >>"$GITHUB_OUTPUT" - echo "compute-medium=['self-hosted', 'ondemand', 'os=windows-2019', 'type=m6a.8xlarge']" >>"$GITHUB_OUTPUT" - echo "compute-large=['self-hosted', 'ondemand', 'os=windows-2019', 'type=m6a.12xlarge']" >>"$GITHUB_OUTPUT" + echo "compute-small=['self-hosted', 'ondemand', 'os=windows-2019', 'type=m6a.2xlarge']" >>"$GITHUB_OUTPUT" + echo "compute-medium=['self-hosted', 'ondemand', 'os=windows-2019', 'type=m6a.4xlarge']" >>"$GITHUB_OUTPUT" + echo "compute-large=['self-hosted', 'ondemand', 'os=windows-2019', 'type=m6a.8xlarge']" >>"$GITHUB_OUTPUT" # m5d.8xlarge is equivalent to our xl custom runner in CE - echo "compute-xl=['self-hosted', 'ondemand', 'os=windows-2019', 'type=m6a.16xlarge']" >>"$GITHUB_OUTPUT" + echo "compute-xl=['self-hosted', 'ondemand', 'os=windows-2019', 'type=m6a.12xlarge']" >>"$GITHUB_OUTPUT" ;; *) # shellcheck disable=SC2129 diff --git a/.github/workflows/test-integrations-windows.yml b/.github/workflows/test-integrations-windows.yml index 406fcb796b00..59889befe6a0 100644 --- a/.github/workflows/test-integrations-windows.yml +++ b/.github/workflows/test-integrations-windows.yml @@ -76,6 +76,11 @@ jobs: name: '${{ env.CONSUL_BINARY_UPLOAD_NAME }}' path: ${{ github.workspace }} + - name: Create dist folder and copy binary + run: | + mkdir dist + cp ${{ github.workspace }}\consul.exe dist\ + - name: Restore mode+x run: icacls ${{ github.workspace }}\consul.exe /grant:rx Everyone:RX diff --git a/build-support/windows/build-consul-dev-image.sh b/build-support/windows/build-consul-dev-image.sh index ddc5e521d11a..198a5c52fcea 100644 --- a/build-support/windows/build-consul-dev-image.sh +++ b/build-support/windows/build-consul-dev-image.sh @@ -4,14 +4,5 @@ cd ../../ -rm -rf dist - -export GOOS=windows GOARCH=amd64 VERSION=1.16.0 -CONSUL_BUILDDATE=$(date +"%Y-%m-%dT%H:%M:%SZ") -GIT_IMPORT=github.com/hashicorp/consul/version -GOLDFLAGS=" -X $GIT_IMPORT.Version=$VERSION -X $GIT_IMPORT.VersionPrerelease=dev -X $GIT_IMPORT.BuildDate=$CONSUL_BUILDDATE " - -go build -ldflags "$GOLDFLAGS" -o ./dist/ . - docker build -t windows/consul:${VERSION}-dev -f build-support/windows/Dockerfile-consul-dev-windows . --build-arg VERSION=${VERSION} From bf4e0b1aa9ee655b02f1e24dc09c347cf821d35f Mon Sep 17 00:00:00 2001 From: Poonam Jadhav Date: Mon, 18 Sep 2023 09:09:31 -0400 Subject: [PATCH 16/28] fix: provide meaningful error messages and add test (#18772) * fix: provide meaningful error messages and add test * fix: return error instead of warning when extra args are provided --- command/resource/apply/apply.go | 2 +- command/resource/apply/apply_test.go | 2 +- command/resource/delete/delete.go | 7 ++++--- command/resource/delete/delete_test.go | 14 +++++++------- command/resource/list/list.go | 14 +++++++------- command/resource/list/list_test.go | 21 ++++++++++++++++----- command/resource/read/read.go | 7 ++++--- command/resource/read/read_test.go | 14 +++++++------- 8 files changed, 47 insertions(+), 34 deletions(-) diff --git a/command/resource/apply/apply.go b/command/resource/apply/apply.go index ac0c79d6eaaa..38ea9c512525 100644 --- a/command/resource/apply/apply.go +++ b/command/resource/apply/apply.go @@ -85,7 +85,7 @@ func (c *cmd) Run(args []string) int { } parsedResource = data } else { - c.UI.Error("Flag -f is required") + c.UI.Error("Incorrect argument format: Flag -f with file path argument is required") return 1 } diff --git a/command/resource/apply/apply_test.go b/command/resource/apply/apply_test.go index f43701ae41ee..4d02c8c61deb 100644 --- a/command/resource/apply/apply_test.go +++ b/command/resource/apply/apply_test.go @@ -78,7 +78,7 @@ func TestResourceApplyInvalidArgs(t *testing.T) { "missing required flag": { args: []string{}, expectedCode: 1, - expectedErr: errors.New("Flag -f is required"), + expectedErr: errors.New("Incorrect argument format: Flag -f with file path argument is required"), }, "file parsing failure": { args: []string{"-f=../testdata/invalid.hcl"}, diff --git a/command/resource/delete/delete.go b/command/resource/delete/delete.go index 16904ffcfe26..af6b3310a2f8 100644 --- a/command/resource/delete/delete.go +++ b/command/resource/delete/delete.go @@ -84,13 +84,13 @@ func (c *cmd) Run(args []string) int { } } else { if len(args) < 2 { - c.UI.Error("Your argument format is incorrect: Must specify two arguments: resource type and resource name") + c.UI.Error("Incorrect argument format: Must specify two arguments: resource type and resource name") return 1 } var err error gvk, resourceName, err = resource.GetTypeAndResourceName(args) if err != nil { - c.UI.Error(fmt.Sprintf("Your argument format is incorrect: %s", err)) + c.UI.Error(fmt.Sprintf("Incorrect argument format: %s", err)) return 1 } @@ -101,7 +101,8 @@ func (c *cmd) Run(args []string) int { return 1 } if c.filePath != "" { - c.UI.Warn("We ignored the -f flag if you provide gvk and resource name") + c.UI.Error("Incorrect argument format: File argument is not needed when resource information is provided with the command") + return 1 } opts = &api.QueryOptions{ Namespace: c.http.Namespace(), diff --git a/command/resource/delete/delete_test.go b/command/resource/delete/delete_test.go index 9eec0225b42c..f888bb3c8fd5 100644 --- a/command/resource/delete/delete_test.go +++ b/command/resource/delete/delete_test.go @@ -27,12 +27,12 @@ func TestResourceDeleteInvalidArgs(t *testing.T) { "nil args": { args: nil, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must specify two arguments: resource type and resource name"), + expectedErr: errors.New("Incorrect argument format: Must specify two arguments: resource type and resource name"), }, "empty args": { args: []string{}, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must specify two arguments: resource type and resource name"), + expectedErr: errors.New("Incorrect argument format: Must specify two arguments: resource type and resource name"), }, "missing file path": { args: []string{"-f"}, @@ -47,27 +47,27 @@ func TestResourceDeleteInvalidArgs(t *testing.T) { "provide type and name": { args: []string{"a.b.c"}, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must specify two arguments: resource type and resource name"), + expectedErr: errors.New("Incorrect argument format: Must specify two arguments: resource type and resource name"), }, "provide type and name with -f": { args: []string{"a.b.c", "name", "-f", "test.hcl"}, expectedCode: 1, - expectedErr: errors.New("We ignored the -f flag if you provide gvk and resource name"), + expectedErr: errors.New("Incorrect argument format: File argument is not needed when resource information is provided with the command"), }, "provide type and name with -f and other flags": { args: []string{"a.b.c", "name", "-f", "test.hcl", "-namespace", "default"}, expectedCode: 1, - expectedErr: errors.New("We ignored the -f flag if you provide gvk and resource name"), + expectedErr: errors.New("Incorrect argument format: File argument is not needed when resource information is provided with the command"), }, "does not provide resource name after type": { args: []string{"a.b.c", "-namespace", "default"}, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must provide resource name right after type"), + expectedErr: errors.New("Incorrect argument format: Must provide resource name right after type"), }, "invalid resource type format": { args: []string{"a.", "name", "-namespace", "default"}, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must include resource type argument in group.verion.kind format"), + expectedErr: errors.New("Incorrect argument format: Must include resource type argument in group.verion.kind format"), }, } diff --git a/command/resource/list/list.go b/command/resource/list/list.go index 3c81f135cb33..77db388d6c25 100644 --- a/command/resource/list/list.go +++ b/command/resource/list/list.go @@ -47,11 +47,6 @@ func (c *cmd) Run(args []string) int { var gvk *api.GVK var opts *api.QueryOptions - if len(args) == 0 { - c.UI.Error("Please provide required arguments") - return 1 - } - if err := c.flags.Parse(args); err != nil { if !errors.Is(err, flag.ErrHelp) { c.UI.Error(fmt.Sprintf("Failed to parse args: %v", err)) @@ -93,7 +88,7 @@ func (c *cmd) Run(args []string) int { // extract resource type gvk, err = getResourceType(c.flags.Args()) if err != nil { - c.UI.Error(fmt.Sprintf("Your argument format is incorrect: %v", err)) + c.UI.Error(fmt.Sprintf("Incorrect argument format: %v", err)) return 1 } // skip resource type to parse remaining args @@ -104,7 +99,8 @@ func (c *cmd) Run(args []string) int { return 1 } if c.filePath != "" { - c.UI.Warn(fmt.Sprintf("File argument is ignored when resource definition is provided with the command")) + c.UI.Error("Incorrect argument format: File argument is not needed when resource information is provided with the command") + return 1 } opts = &api.QueryOptions{ @@ -142,6 +138,10 @@ func getResourceType(args []string) (gvk *api.GVK, e error) { if len(args) < 1 { return nil, fmt.Errorf("Must include resource type argument") } + // it should not have resource name + if len(args) > 1 && !strings.HasPrefix(args[1], "-") { + return nil, fmt.Errorf("Must include flag arguments after resource type") + } s := strings.Split(args[0], ".") if len(s) < 3 { diff --git a/command/resource/list/list_test.go b/command/resource/list/list_test.go index 377d00e72496..b8fc12556a14 100644 --- a/command/resource/list/list_test.go +++ b/command/resource/list/list_test.go @@ -99,12 +99,12 @@ func TestResourceListInvalidArgs(t *testing.T) { "nil args": { args: nil, expectedCode: 1, - expectedErr: errors.New("Please provide required arguments"), + expectedErr: errors.New("Incorrect argument format: Must include resource type argument"), }, "minimum args required": { args: []string{}, expectedCode: 1, - expectedErr: errors.New("Please provide required arguments"), + expectedErr: errors.New("Incorrect argument format: Must include resource type argument"), }, "no file path": { args: []string{ @@ -127,7 +127,7 @@ func TestResourceListInvalidArgs(t *testing.T) { expectedCode: 1, expectedErr: errors.New("Failed to decode resource from input file"), }, - "file argument is ignored": { + "file argument with resource type": { args: []string{ "demo.v2.artist", "-namespace=default", @@ -137,8 +137,8 @@ func TestResourceListInvalidArgs(t *testing.T) { "-token=root", "-f=demo.hcl", }, - expectedCode: 0, - expectedErr: errors.New("File argument is ignored when resource definition is provided with the command"), + expectedCode: 1, + expectedErr: errors.New("Incorrect argument format: File argument is not needed when resource information is provided with the command"), }, "resource type invalid": { args: []string{ @@ -150,6 +150,17 @@ func TestResourceListInvalidArgs(t *testing.T) { expectedCode: 1, expectedErr: errors.New("Must include resource type argument in group.verion.kind format"), }, + "resource name is provided": { + args: []string{ + "demo.v2.artist", + "test", + "-namespace=default", + "-peer=local", + "-partition=default", + }, + expectedCode: 1, + expectedErr: errors.New("Must include flag arguments after resource type"), + }, } for desc, tc := range cases { diff --git a/command/resource/read/read.go b/command/resource/read/read.go index 6bb47ac33d57..1964a0767b9e 100644 --- a/command/resource/read/read.go +++ b/command/resource/read/read.go @@ -86,13 +86,13 @@ func (c *cmd) Run(args []string) int { } } else { if len(args) < 2 { - c.UI.Error("Your argument format is incorrect: Must specify two arguments: resource type and resource name") + c.UI.Error("Incorrect argument format: Must specify two arguments: resource type and resource name") return 1 } var err error gvk, resourceName, err = resource.GetTypeAndResourceName(args) if err != nil { - c.UI.Error(fmt.Sprintf("Your argument format is incorrect: %s", err)) + c.UI.Error(fmt.Sprintf("Incorrect argument format: %s", err)) return 1 } @@ -103,7 +103,8 @@ func (c *cmd) Run(args []string) int { return 1 } if c.filePath != "" { - c.UI.Warn("We ignored the -f flag if you provide gvk and resource name") + c.UI.Error("Incorrect argument format: File argument is not needed when resource information is provided with the command") + return 1 } opts = &api.QueryOptions{ Namespace: c.http.Namespace(), diff --git a/command/resource/read/read_test.go b/command/resource/read/read_test.go index 30eecb9802b1..766f86b02cc1 100644 --- a/command/resource/read/read_test.go +++ b/command/resource/read/read_test.go @@ -27,12 +27,12 @@ func TestResourceReadInvalidArgs(t *testing.T) { "nil args": { args: nil, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must specify two arguments: resource type and resource name"), + expectedErr: errors.New("Incorrect argument format: Must specify two arguments: resource type and resource name"), }, "empty args": { args: []string{}, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must specify two arguments: resource type and resource name"), + expectedErr: errors.New("Incorrect argument format: Must specify two arguments: resource type and resource name"), }, "missing file path": { args: []string{"-f"}, @@ -47,27 +47,27 @@ func TestResourceReadInvalidArgs(t *testing.T) { "provide type and name": { args: []string{"a.b.c"}, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must specify two arguments: resource type and resource name"), + expectedErr: errors.New("Incorrect argument format: Must specify two arguments: resource type and resource name"), }, "provide type and name with -f": { args: []string{"a.b.c", "name", "-f", "test.hcl"}, expectedCode: 1, - expectedErr: errors.New("We ignored the -f flag if you provide gvk and resource name"), + expectedErr: errors.New("Incorrect argument format: File argument is not needed when resource information is provided with the command"), }, "provide type and name with -f and other flags": { args: []string{"a.b.c", "name", "-f", "test.hcl", "-namespace", "default"}, expectedCode: 1, - expectedErr: errors.New("We ignored the -f flag if you provide gvk and resource name"), + expectedErr: errors.New("Incorrect argument format: File argument is not needed when resource information is provided with the command"), }, "does not provide resource name after type": { args: []string{"a.b.c", "-namespace", "default"}, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must provide resource name right after type"), + expectedErr: errors.New("Incorrect argument format: Must provide resource name right after type"), }, "invalid resource type format": { args: []string{"a.", "name", "-namespace", "default"}, expectedCode: 1, - expectedErr: errors.New("Your argument format is incorrect: Must include resource type argument in group.verion.kind format"), + expectedErr: errors.New("Incorrect argument format: Must include resource type argument in group.verion.kind format"), }, } From 4435e4a420d5f5f7e6d8726e65b62596554846fe Mon Sep 17 00:00:00 2001 From: Dhia Ayachi Date: Mon, 18 Sep 2023 12:25:05 -0400 Subject: [PATCH 17/28] add v2 tenancy bridge Flag and v2 Tenancy Bridge initial implementation (#18830) * add v2 tenancy bridge and a feature flag for v2 tenancy * move tenancy bridge v2 under resource package --- agent/consul/server.go | 34 ++++++++++++------- agent/consul/tenancy_bridge.go | 4 ++- .../grpc-external/services/resource/delete.go | 2 +- .../services/resource/list_by_owner.go | 2 +- agent/grpc-external/services/resource/read.go | 2 +- .../grpc-external/services/resource/server.go | 4 +-- .../services/resource/server_test.go | 10 +++--- .../services/resource/testing/testing.go | 10 +++--- .../grpc-external/services/resource/write.go | 4 +-- .../services/resource/write_status.go | 2 +- .../services/resource/write_test.go | 2 +- internal/resource/tenancy.go | 16 +++++++++ internal/resource/tenancy_bridge_ce.go | 29 ++++++++++++++++ 13 files changed, 89 insertions(+), 32 deletions(-) create mode 100644 internal/resource/tenancy_bridge_ce.go diff --git a/agent/consul/server.go b/agent/consul/server.go index c515f5443cc3..d2fd0472b90b 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -137,6 +137,7 @@ const ( LeaderTransferMinVersion = "1.6.0" CatalogResourceExperimentName = "resource-apis" + V2TenancyExperimentName = "v2tenancy" ) const ( @@ -819,7 +820,7 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server, go s.reportingManager.Run(&lib.StopChannelContext{StopCh: s.shutdownCh}) // Setup insecure resource service client. - if err := s.setupInsecureResourceServiceClient(flat.Registry, logger); err != nil { + if err := s.setupInsecureResourceServiceClient(flat.Registry, logger, flat); err != nil { return nil, err } @@ -1393,29 +1394,38 @@ func (s *Server) setupExternalGRPC(config *Config, deps Deps, logger hclog.Logge }) s.peerStreamServer.Register(s.externalGRPCServer) + tenancyBridge := NewV1TenancyBridge(s) + if stringslice.Contains(deps.Experiments, V2TenancyExperimentName) { + tenancyBridge = resource.NewV2TenancyBridge() + } + s.resourceServiceServer = resourcegrpc.NewServer(resourcegrpc.Config{ - Registry: deps.Registry, - Backend: s.raftStorageBackend, - ACLResolver: s.ACLResolver, - Logger: logger.Named("grpc-api.resource"), - V1TenancyBridge: NewV1TenancyBridge(s), + Registry: deps.Registry, + Backend: s.raftStorageBackend, + ACLResolver: s.ACLResolver, + Logger: logger.Named("grpc-api.resource"), + TenancyBridge: tenancyBridge, }) s.resourceServiceServer.Register(s.externalGRPCServer) reflection.Register(s.externalGRPCServer) } -func (s *Server) setupInsecureResourceServiceClient(typeRegistry resource.Registry, logger hclog.Logger) error { +func (s *Server) setupInsecureResourceServiceClient(typeRegistry resource.Registry, logger hclog.Logger, deps Deps) error { if s.raftStorageBackend == nil { return fmt.Errorf("raft storage backend cannot be nil") } + tenancyBridge := NewV1TenancyBridge(s) + if stringslice.Contains(deps.Experiments, V2TenancyExperimentName) { + tenancyBridge = resource.NewV2TenancyBridge() + } server := resourcegrpc.NewServer(resourcegrpc.Config{ - Registry: typeRegistry, - Backend: s.raftStorageBackend, - ACLResolver: resolver.DANGER_NO_AUTH{}, - Logger: logger.Named("grpc-api.resource"), - V1TenancyBridge: NewV1TenancyBridge(s), + Registry: typeRegistry, + Backend: s.raftStorageBackend, + ACLResolver: resolver.DANGER_NO_AUTH{}, + Logger: logger.Named("grpc-api.resource"), + TenancyBridge: tenancyBridge, }) conn, err := s.runInProcessGRPCServer(server.Register) diff --git a/agent/consul/tenancy_bridge.go b/agent/consul/tenancy_bridge.go index 4573db2feb06..4e8daa0bc8de 100644 --- a/agent/consul/tenancy_bridge.go +++ b/agent/consul/tenancy_bridge.go @@ -3,6 +3,8 @@ package consul +import "github.com/hashicorp/consul/agent/grpc-external/services/resource" + // V1TenancyBridge is used by the resource service to access V1 implementations of // partitions and namespaces. This bridge will be removed when V2 implemenations // of partitions and namespaces are available. @@ -10,6 +12,6 @@ type V1TenancyBridge struct { server *Server } -func NewV1TenancyBridge(server *Server) *V1TenancyBridge { +func NewV1TenancyBridge(server *Server) resource.TenancyBridge { return &V1TenancyBridge{server: server} } diff --git a/agent/grpc-external/services/resource/delete.go b/agent/grpc-external/services/resource/delete.go index 123ffac35b38..2f30e27f983f 100644 --- a/agent/grpc-external/services/resource/delete.go +++ b/agent/grpc-external/services/resource/delete.go @@ -20,7 +20,7 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" ) -// Deletes a resource. +// Delete deletes a resource. // - To delete a resource regardless of the stored version, set Version = "" // - Supports deleting a resource by name, hence Id.Uid may be empty. // - Delete of a previously deleted or non-existent resource is a no-op to support idempotency. diff --git a/agent/grpc-external/services/resource/list_by_owner.go b/agent/grpc-external/services/resource/list_by_owner.go index 3c0ccbda65e8..dd5b1dae8510 100644 --- a/agent/grpc-external/services/resource/list_by_owner.go +++ b/agent/grpc-external/services/resource/list_by_owner.go @@ -43,7 +43,7 @@ func (s *Server) ListByOwner(ctx context.Context, req *pbresource.ListByOwnerReq } // Check v1 tenancy exists for the v2 resource. - if err = v1TenancyExists(reg, s.V1TenancyBridge, req.Owner.Tenancy, codes.InvalidArgument); err != nil { + if err = v1TenancyExists(reg, s.TenancyBridge, req.Owner.Tenancy, codes.InvalidArgument); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/read.go b/agent/grpc-external/services/resource/read.go index febfcb69f0fa..fcf0e38aa892 100644 --- a/agent/grpc-external/services/resource/read.go +++ b/agent/grpc-external/services/resource/read.go @@ -54,7 +54,7 @@ func (s *Server) Read(ctx context.Context, req *pbresource.ReadRequest) (*pbreso } // Check V1 tenancy exists for the V2 resource. - if err = v1TenancyExists(reg, s.V1TenancyBridge, req.Id.Tenancy, codes.NotFound); err != nil { + if err = v1TenancyExists(reg, s.TenancyBridge, req.Id.Tenancy, codes.NotFound); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/server.go b/agent/grpc-external/services/resource/server.go index 3ae839e69b4b..5fc5a01fafd7 100644 --- a/agent/grpc-external/services/resource/server.go +++ b/agent/grpc-external/services/resource/server.go @@ -31,9 +31,9 @@ type Config struct { // Backend is the storage backend that will be used for resource persistence. Backend Backend ACLResolver ACLResolver - // V1TenancyBridge temporarily allows us to use V1 implementations of + // TenancyBridge temporarily allows us to use V1 implementations of // partitions and namespaces until V2 implementations are available. - V1TenancyBridge TenancyBridge + TenancyBridge TenancyBridge } //go:generate mockery --name Registry --inpackage diff --git a/agent/grpc-external/services/resource/server_test.go b/agent/grpc-external/services/resource/server_test.go index 8f3967259a51..99add6497121 100644 --- a/agent/grpc-external/services/resource/server_test.go +++ b/agent/grpc-external/services/resource/server_test.go @@ -86,11 +86,11 @@ func testServer(t *testing.T) *Server { mockTenancyBridge.On("IsNamespaceMarkedForDeletion", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(false, nil) return NewServer(Config{ - Logger: testutil.Logger(t), - Registry: resource.NewRegistry(), - Backend: backend, - ACLResolver: mockACLResolver, - V1TenancyBridge: mockTenancyBridge, + Logger: testutil.Logger(t), + Registry: resource.NewRegistry(), + Backend: backend, + ACLResolver: mockACLResolver, + TenancyBridge: mockTenancyBridge, }) } diff --git a/agent/grpc-external/services/resource/testing/testing.go b/agent/grpc-external/services/resource/testing/testing.go index 4db4d6e58696..ea06526fd1f8 100644 --- a/agent/grpc-external/services/resource/testing/testing.go +++ b/agent/grpc-external/services/resource/testing/testing.go @@ -101,11 +101,11 @@ func RunResourceServiceWithACL(t *testing.T, aclResolver svc.ACLResolver, regist mockTenancyBridge.On("IsNamespaceMarkedForDeletion", resource.DefaultPartitionName, resource.DefaultNamespaceName).Return(false, nil) svc.NewServer(svc.Config{ - Backend: backend, - Registry: registry, - Logger: testutil.Logger(t), - ACLResolver: aclResolver, - V1TenancyBridge: mockTenancyBridge, + Backend: backend, + Registry: registry, + Logger: testutil.Logger(t), + ACLResolver: aclResolver, + TenancyBridge: mockTenancyBridge, }).Register(server) pipe := internal.NewPipeListener() diff --git a/agent/grpc-external/services/resource/write.go b/agent/grpc-external/services/resource/write.go index eb66f355ff1c..2511e2f5da22 100644 --- a/agent/grpc-external/services/resource/write.go +++ b/agent/grpc-external/services/resource/write.go @@ -71,12 +71,12 @@ func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbre } // Check V1 tenancy exists for the V2 resource - if err = v1TenancyExists(reg, s.V1TenancyBridge, req.Resource.Id.Tenancy, codes.InvalidArgument); err != nil { + if err = v1TenancyExists(reg, s.TenancyBridge, req.Resource.Id.Tenancy, codes.InvalidArgument); err != nil { return nil, err } // Check V1 tenancy not marked for deletion. - if err = v1TenancyMarkedForDeletion(reg, s.V1TenancyBridge, req.Resource.Id.Tenancy); err != nil { + if err = v1TenancyMarkedForDeletion(reg, s.TenancyBridge, req.Resource.Id.Tenancy); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/write_status.go b/agent/grpc-external/services/resource/write_status.go index 673f193da0b0..0d3b68bb0876 100644 --- a/agent/grpc-external/services/resource/write_status.go +++ b/agent/grpc-external/services/resource/write_status.go @@ -36,7 +36,7 @@ func (s *Server) WriteStatus(ctx context.Context, req *pbresource.WriteStatusReq // Check V1 tenancy exists for the V2 resource. Ignore "marked for deletion" since status updates // should still work regardless. - if err = v1TenancyExists(reg, s.V1TenancyBridge, req.Id.Tenancy, codes.InvalidArgument); err != nil { + if err = v1TenancyExists(reg, s.TenancyBridge, req.Id.Tenancy, codes.InvalidArgument); err != nil { return nil, err } diff --git a/agent/grpc-external/services/resource/write_test.go b/agent/grpc-external/services/resource/write_test.go index 5a5e8d318098..3828ff9753f2 100644 --- a/agent/grpc-external/services/resource/write_test.go +++ b/agent/grpc-external/services/resource/write_test.go @@ -416,7 +416,7 @@ func TestWrite_Tenancy_MarkedForDeletion(t *testing.T) { mockTenancyBridge := &MockTenancyBridge{} mockTenancyBridge.On("PartitionExists", "part1").Return(true, nil) mockTenancyBridge.On("NamespaceExists", "part1", "ns1").Return(true, nil) - server.V1TenancyBridge = mockTenancyBridge + server.TenancyBridge = mockTenancyBridge _, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: tc.modFn(artist, recordLabel, mockTenancyBridge)}) require.Error(t, err) diff --git a/internal/resource/tenancy.go b/internal/resource/tenancy.go index 2a377ca2f58a..c5e3d1b2b40b 100644 --- a/internal/resource/tenancy.go +++ b/internal/resource/tenancy.go @@ -12,11 +12,27 @@ import ( "github.com/hashicorp/consul/proto-public/pbresource" ) +type TenancyBridge interface { + PartitionExists(partition string) (bool, error) + IsPartitionMarkedForDeletion(partition string) (bool, error) + NamespaceExists(partition, namespace string) (bool, error) + IsNamespaceMarkedForDeletion(partition, namespace string) (bool, error) +} + const ( DefaultPartitionName = "default" DefaultNamespaceName = "default" ) +// V2TenancyBridge is used by the resource service to access V2 implementations of +// partitions and namespaces. +type V2TenancyBridge struct { +} + +func NewV2TenancyBridge() TenancyBridge { + return &V2TenancyBridge{} +} + // Scope describes the tenancy scope of a resource. type Scope int diff --git a/internal/resource/tenancy_bridge_ce.go b/internal/resource/tenancy_bridge_ce.go new file mode 100644 index 000000000000..07be8ab5083f --- /dev/null +++ b/internal/resource/tenancy_bridge_ce.go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +//go:build !consulent +// +build !consulent + +package resource + +func (b *V2TenancyBridge) PartitionExists(partition string) (bool, error) { + if partition == "default" { + return true, nil + } + return false, nil +} + +func (b *V2TenancyBridge) IsPartitionMarkedForDeletion(partition string) (bool, error) { + return false, nil +} + +func (b *V2TenancyBridge) NamespaceExists(partition, namespace string) (bool, error) { + if partition == "default" && namespace == "default" { + return true, nil + } + return false, nil +} + +func (b *V2TenancyBridge) IsNamespaceMarkedForDeletion(partition, namespace string) (bool, error) { + return false, nil +} From 17901acd4f619a2aa3ccdb5f600985a724337c17 Mon Sep 17 00:00:00 2001 From: Blake Covarrubias Date: Mon, 18 Sep 2023 10:14:41 -0700 Subject: [PATCH 18/28] docs: Fix typo in description for server_addresses (#18838) Change 'If not port' to 'If no port'. Resolves #18553 --- website/content/docs/agent/config/config-files.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/content/docs/agent/config/config-files.mdx b/website/content/docs/agent/config/config-files.mdx index f099bc303392..f2a4aad6ac9c 100644 --- a/website/content/docs/agent/config/config-files.mdx +++ b/website/content/docs/agent/config/config-files.mdx @@ -264,7 +264,7 @@ Refer to the [formatting specification](https://golang.org/pkg/time/#ParseDurati - `server_addresses` (Defaults to `[]`) This specifies the addresses of servers in the local datacenter to use for the initial RPC. These addresses support [Cloud Auto-Joining](/consul/docs/agent/config/cli-flags#cloud-auto-joining) and can optionally include a port to - use when making the outbound connection. If not port is provided the `server_port` + use when making the outbound connection. If no port is provided, the `server_port` will be used. - `dns_sans` (Defaults to `[]`) This is a list of extra DNS SANs to request in the From 4357362973eeca251408dab28a64e73d33ed1f90 Mon Sep 17 00:00:00 2001 From: cskh Date: Mon, 18 Sep 2023 13:23:02 -0400 Subject: [PATCH 19/28] grafana: display connected consul-dataplanes (#18842) --- .../consul-k8s-control-plane-monitoring.json | 161 +++++++++++------- 1 file changed, 103 insertions(+), 58 deletions(-) diff --git a/grafana/consul-k8s-control-plane-monitoring.json b/grafana/consul-k8s-control-plane-monitoring.json index e84fe8c6b963..f0ca249b3790 100644 --- a/grafana/consul-k8s-control-plane-monitoring.json +++ b/grafana/consul-k8s-control-plane-monitoring.json @@ -24,7 +24,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 1, + "id": 3, "links": [], "liveNow": false, "panels": [ @@ -113,7 +113,7 @@ "type": "prometheus", "uid": "PBFA97CFB590B2093" }, - "description": "No data in agentless mode", + "description": "", "fieldConfig": { "defaults": { "mappings": [], @@ -140,7 +140,7 @@ "x": 6, "y": 1 }, - "id": 29, + "id": 60, "options": { "colorMode": "value", "graphMode": "none", @@ -162,9 +162,9 @@ "type": "prometheus", "uid": "PBFA97CFB590B2093" }, - "editorMode": "code", + "editorMode": "builder", "exemplar": false, - "expr": "count(kube_pod_container_resource_limits{pod=~\"consul-client-.*\", container=\"consul\", resource=\"memory\"})", + "expr": "count(consul_dataplane_envoy_connected)", "hide": false, "instant": true, "legendFormat": "__auto", @@ -172,7 +172,7 @@ "refId": "A" } ], - "title": "Number of Consul Clients (No data in agentless mode)", + "title": "Number of Connected Consul-dataplanes", "type": "stat" }, { @@ -270,13 +270,80 @@ "title": "Number of Leader (1: Healthy)", "type": "timeseries" }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "No data in agentless mode", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 6, + "y": 9 + }, + "id": 29, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "exemplar": false, + "expr": "count(kube_pod_container_resource_limits{pod=~\"consul-client-.*\", container=\"consul\", resource=\"memory\"})", + "hide": false, + "instant": true, + "legendFormat": "__auto", + "range": false, + "refId": "A" + } + ], + "title": "Number of Consul Clients (No data in agentless mode)", + "type": "stat" + }, { "collapsed": true, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 9 + "y": 17 }, "id": 40, "panels": [ @@ -326,8 +393,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -431,8 +497,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -535,8 +600,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -639,8 +703,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -731,8 +794,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -824,8 +886,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -917,8 +978,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -974,7 +1034,7 @@ "h": 1, "w": 24, "x": 0, - "y": 10 + "y": 18 }, "id": 43, "panels": [ @@ -1172,7 +1232,7 @@ "h": 1, "w": 24, "x": 0, - "y": 11 + "y": 19 }, "id": 14, "panels": [ @@ -1221,8 +1281,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1297,8 +1356,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1393,8 +1451,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1458,8 +1515,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1555,8 +1611,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1614,7 +1669,7 @@ "h": 1, "w": 24, "x": 0, - "y": 12 + "y": 20 }, "id": 24, "panels": [ @@ -1957,7 +2012,7 @@ "h": 1, "w": 24, "x": 0, - "y": 13 + "y": 21 }, "id": 54, "panels": [ @@ -2006,8 +2061,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2082,8 +2136,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2178,8 +2231,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2243,8 +2295,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2304,7 +2355,7 @@ "h": 1, "w": 24, "x": 0, - "y": 14 + "y": 22 }, "id": 30, "panels": [ @@ -2500,7 +2551,7 @@ "h": 1, "w": 24, "x": 0, - "y": 15 + "y": 23 }, "id": 49, "panels": [ @@ -2880,7 +2931,7 @@ "h": 1, "w": 24, "x": 0, - "y": 16 + "y": 24 }, "id": 33, "panels": [ @@ -2930,8 +2981,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3023,8 +3073,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3116,8 +3165,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3209,8 +3257,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3302,8 +3349,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3395,8 +3441,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", From 132c1eaa87ea5765bcfb227300803654ef4b115c Mon Sep 17 00:00:00 2001 From: Gautam Date: Mon, 18 Sep 2023 11:23:11 -0700 Subject: [PATCH 20/28] Adding Apigee for ext_authz, minor fix in the default ext_authz docs (#18796) * adding apigee for ext_authz, minor fix * adding the Apigee docs to nav * Apply suggestions from code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> Co-authored-by: Blake Covarrubias * addressing feedback * Apply suggestions from code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> --------- Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> Co-authored-by: Blake Covarrubias --- .../usage/apigee-ext-authz.mdx | 199 ++++++++++++++++++ .../envoy-extensions/usage/ext-authz.mdx | 28 +-- website/data/docs-nav-data.json | 4 + 3 files changed, 218 insertions(+), 13 deletions(-) create mode 100644 website/content/docs/connect/proxies/envoy-extensions/usage/apigee-ext-authz.mdx diff --git a/website/content/docs/connect/proxies/envoy-extensions/usage/apigee-ext-authz.mdx b/website/content/docs/connect/proxies/envoy-extensions/usage/apigee-ext-authz.mdx new file mode 100644 index 000000000000..fff6bb28574f --- /dev/null +++ b/website/content/docs/connect/proxies/envoy-extensions/usage/apigee-ext-authz.mdx @@ -0,0 +1,199 @@ +--- +layout: docs +page_title: Delegate authorization to Apigee +description: Learn how to use the `ext-authz` Envoy extension to delegate data plane authorization requests to Apigee. +--- + +# Delegate authorization to Apigee + +This topic describes how to use the external authorization Envoy extension to delegate data plane authorization requests to Apigee. + +For more detailed guidance, refer to the [`learn-consul-apigee-external-authz` repo on GitHub](https://github.com/hashicorp-education/learn-consul-apigee-external-authz). + +## Workflow + +Complete the following steps to use the external authorization extension with Apigee: + +1. Deploy the Apigee Adapter for Envoy and register the service in Consul. +1. Configure the `EnvoyExtensions` block in a service defaults or proxy defaults configuration entry. +1. Apply the configuration entry. + +## Deploy the Apigee Adapter for Envoy + +The [Apigee Adapter for Envoy](https://cloud.google.com/apigee/docs/api-platform/envoy-adapter/v2.0.x/concepts) is an Apigee-managed API gateway that uses Envoy to proxy API traffic. + +To download and install Apigee Adapter for Envoy, refer to the [getting started documentation](https://cloud.google.com/apigee/docs/api-platform/envoy-adapter/v2.0.x/getting-started) or follow along with the [`learn-consul-apigee-external-authz` repo on GitHub](https://github.com/hashicorp-education/learn-consul-apigee-external-authz). + +After you deploy the service in your desired runtime, create a service defaults configuration entry for the service's gRPC protocol. + + + + + +```hcl +Kind = "service-defaults" +Name = "apigee-remote-service-envoy" +Protocol = "grpc" +``` + + + + + +```json +{ + "kind": "service-defaults", + "name": "apigee-remote-service-envoy", + "protocol": "grpc" +} +``` + + + + + + +```yaml +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceDefaults +metadata: + name: apigee-remote-service-envoy + namespace: apigee +spec: + protocol: grpc +``` + + + + +## Configure the `EnvoyExtensions` + +Add Envoy extension configurations to a proxy defaults or service defaults configuration entry. Place the extension configuration in an `EnvoyExtensions` block in the configuration entry. + +- When you configure Envoy extensions on proxy defaults, they apply to every service. +- When you configure Envoy extensions on service defaults, they apply to all instances of a service with that name. + + + Adding Envoy extensions default proxy configurations may have unintended consequences. We recommend configuring `EnvoyExtensions` in service defaults configuration entries in most cases. + + +Consul applies Envoy extensions configured in proxy defaults before it applies extensions in service defaults. As a result, the Envoy extension configuration in service defaults may override configurations in proxy defaults. + +The following example configures the default behavior for all services named `api` so that the Envoy proxies running as sidecars for those service instances target the apigee-remote-service-envoy service for gRPC authorization requests: + + + + + +```hcl +Kind = "service-defaults" +Name = "api" +EnvoyExtensions = [ + { + Name = "builtin/ext-authz" + Arguments = { + ProxyType = "connect-proxy" + Config = { + GrpcService = { + Target = { + Service = { + Name = "apigee-remote-service-envoy" + } + } + } + } + } + } +] +``` + + + + + + +```json +{ + "Kind": "service-defaults", + "Name": "api", + "EnvoyExtensions": [{ + "Name": "builtin/ext-authz", + "Arguments": { + "ProxyType": "connect-proxy", + "Config": { + "GrpcService": { + "Target": { + "Service": { + "Name": "apigee-remote-service-envoy" + } + } + } + } + } + } + ] +} +``` + + + + + + + +```yaml +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceDefaults +metadata: + name: api + namespace: default +spec: + envoyExtensions: + - name: builtin/ext-authz + arguments: + proxyType: connect-proxy + config: + grpcService: + target: + service: + name: apigee-remote-service-envoy + namespace: apigee +``` + + + + +Refer to the [external authorization extension configuration reference](/consul/docs/connect/proxies/envoy-extensions/configuration/ext-authz) for details on how to configure the extension. + +Refer to the [proxy defaults configuration entry reference](/consul/docs/connect/config-entries/proxy-defaults) and [service defaults configuration entry reference](/consul/docs/connect/config-entries/service-defaults) for details on how to define the configuration entries. + +## Apply the configuration entry + +On the CLI, you can use the `consul config write` command and specify the names of the configuration entries to apply them to Consul. For Kubernetes-orchestrated networks, use the `kubectl apply` command to update the relevant CRD. + + + + +```shell-session +$ consul config write apigee-remote-service-envoy.hcl +$ consul config write api-auth-service-defaults.hcl +``` + + + + +```shell-session +$ consul config write apigee-remote-service-envoy.json +$ consul config write api-auth-service-defaults.json +``` + + + + +```shell-session +$ kubectl apply -f apigee-remote-service-envoy.yaml +$ kubectl apply -f api-auth-service-defaults.yaml +``` + + + diff --git a/website/content/docs/connect/proxies/envoy-extensions/usage/ext-authz.mdx b/website/content/docs/connect/proxies/envoy-extensions/usage/ext-authz.mdx index 76852e6c1c8c..a0e6630b74d0 100644 --- a/website/content/docs/connect/proxies/envoy-extensions/usage/ext-authz.mdx +++ b/website/content/docs/connect/proxies/envoy-extensions/usage/ext-authz.mdx @@ -57,24 +57,26 @@ EnvoyExtensions = [ ```json -"Kind": "service-defaults", -"Name": "api", -"EnvoyExtensions": [{ - "Name": "builtin/ext-authz", - "Arguments": { - "ProxyType": "connect-proxy", - "Config": { - "GrpcService": { - "Target": { - "Service": { - "Name": "authz" +{ + "Kind": "service-defaults", + "Name": "api", + "EnvoyExtensions": [{ + "Name": "builtin/ext-authz", + "Arguments": { + "ProxyType": "connect-proxy", + "Config": { + "GrpcService": { + "Target": { + "Service": { + "Name": "authz" + } } } } } } - } -] + ] +} ``` diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index d72338edfe41..d3591ada7e0b 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -503,6 +503,10 @@ { "title": "Usage", "routes": [ + { + "title": "Delegate authorization to Apigee", + "path": "connect/proxies/envoy-extensions/usage/apigee-ext-authz" + }, { "title": "Delegate authorization to external services", "path": "connect/proxies/envoy-extensions/usage/ext-authz" From b4d5178e5c8fe04ed7bb3ac02bf3ac6541c9ae45 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" <4903+rboyer@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:59:08 -0500 Subject: [PATCH 21/28] catalog: normalize/default/validate tenancy components of FailoverPolicy internal References (#18825) FailoverPolicy resources contain inner Reference fields. We want to ensure that components of those reference Tenancy fields left unspecified are defaulted using the tenancy of the enclosing FailoverPolicy resource. As the underlying helper being used to do the normalization calls the function modified in #18822, it also means that the PeerName field will be set to "local" for now automatically to avoid "local" != "" issues downstream. --- internal/catalog/exports.go | 14 ++ .../catalog/internal/types/failover_policy.go | 148 ++++++++++++------ .../internal/types/failover_policy_test.go | 113 ++++++++++--- internal/catalog/internal/types/validators.go | 79 ++++++++++ 4 files changed, 283 insertions(+), 71 deletions(-) diff --git a/internal/catalog/exports.go b/internal/catalog/exports.go index 566c2e2b6edf..8de37587339c 100644 --- a/internal/catalog/exports.go +++ b/internal/catalog/exports.go @@ -126,3 +126,17 @@ type FailoverPolicyMapper interface { func NewFailoverPolicyMapper() FailoverPolicyMapper { return failovermapper.New() } + +// ValidateLocalServiceRefNoSection ensures the following: +// +// - ref is non-nil +// - type is ServiceType +// - section is empty +// - tenancy is set and partition/namespace are both non-empty +// - peer_name must be "local" +// +// Each possible validation error is wrapped in the wrapErr function before +// being collected in a multierror.Error. +func ValidateLocalServiceRefNoSection(ref *pbresource.Reference, wrapErr func(error) error) error { + return types.ValidateLocalServiceRefNoSection(ref, wrapErr) +} diff --git a/internal/catalog/internal/types/failover_policy.go b/internal/catalog/internal/types/failover_policy.go index 6f755e954087..9fe09a3c8c36 100644 --- a/internal/catalog/internal/types/failover_policy.go +++ b/internal/catalog/internal/types/failover_policy.go @@ -4,7 +4,6 @@ package types import ( - "errors" "fmt" "github.com/hashicorp/go-multierror" @@ -53,10 +52,21 @@ func MutateFailoverPolicy(res *pbresource.Resource) error { failover.Config = nil changed = true } + + if failover.Config != nil { + if mutateFailoverConfig(res.Id.Tenancy, failover.Config) { + changed = true + } + } + for port, pc := range failover.PortConfigs { if pc.IsEmpty() { delete(failover.PortConfigs, port) changed = true + } else { + if mutateFailoverConfig(res.Id.Tenancy, pc) { + changed = true + } } } if len(failover.PortConfigs) == 0 { @@ -64,8 +74,6 @@ func MutateFailoverPolicy(res *pbresource.Resource) error { changed = true } - // TODO(rb): normalize dest ref tenancies - if !changed { return nil } @@ -73,6 +81,42 @@ func MutateFailoverPolicy(res *pbresource.Resource) error { return res.Data.MarshalFrom(&failover) } +func mutateFailoverConfig(policyTenancy *pbresource.Tenancy, config *pbcatalog.FailoverConfig) (changed bool) { + if policyTenancy != nil && !isLocalPeer(policyTenancy.PeerName) { + // TODO(peering/v2): remove this bypass when we know what to do with + // non-local peer references. + return false + } + + for _, dest := range config.Destinations { + if dest.Ref == nil { + continue + } + if dest.Ref.Tenancy != nil && !isLocalPeer(dest.Ref.Tenancy.PeerName) { + // TODO(peering/v2): remove this bypass when we know what to do with + // non-local peer references. + continue + } + + orig := proto.Clone(dest.Ref).(*pbresource.Reference) + resource.DefaultReferenceTenancy( + dest.Ref, + policyTenancy, + resource.DefaultNamespacedTenancy(), // Services are all namespace scoped. + ) + + if !proto.Equal(orig, dest.Ref) { + changed = true + } + } + + return changed +} + +func isLocalPeer(p string) bool { + return p == "local" || p == "" +} + func ValidateFailoverPolicy(res *pbresource.Resource) error { var failover pbcatalog.FailoverPolicy @@ -90,15 +134,25 @@ func ValidateFailoverPolicy(res *pbresource.Resource) error { } if failover.Config != nil { - for _, err := range validateFailoverConfig(failover.Config, false) { - merr = multierror.Append(merr, resource.ErrInvalidField{ + wrapConfigErr := func(err error) error { + return resource.ErrInvalidField{ Name: "config", Wrapped: err, - }) + } + } + if cfgErr := validateFailoverConfig(failover.Config, false, wrapConfigErr); cfgErr != nil { + merr = multierror.Append(merr, cfgErr) } } for portName, pc := range failover.PortConfigs { + wrapConfigErr := func(err error) error { + return resource.ErrInvalidMapValue{ + Map: "port_configs", + Key: portName, + Wrapped: err, + } + } if portNameErr := validatePortName(portName); portNameErr != nil { merr = multierror.Append(merr, resource.ErrInvalidMapKey{ Map: "port_configs", @@ -107,12 +161,8 @@ func ValidateFailoverPolicy(res *pbresource.Resource) error { }) } - for _, err := range validateFailoverConfig(pc, true) { - merr = multierror.Append(merr, resource.ErrInvalidMapValue{ - Map: "port_configs", - Key: portName, - Wrapped: err, - }) + if cfgErr := validateFailoverConfig(pc, true, wrapConfigErr); cfgErr != nil { + merr = multierror.Append(merr, cfgErr) } // TODO: should sameness group be a ref once that's a resource? @@ -121,23 +171,29 @@ func ValidateFailoverPolicy(res *pbresource.Resource) error { return merr } -func validateFailoverConfig(config *pbcatalog.FailoverConfig, ported bool) []error { - var errs []error +func validateFailoverConfig(config *pbcatalog.FailoverConfig, ported bool, wrapErr func(error) error) error { + var merr error if (len(config.Destinations) > 0) == (config.SamenessGroup != "") { - errs = append(errs, resource.ErrInvalidField{ - Name: "destinations", - Wrapped: fmt.Errorf("exactly one of destinations or sameness_group should be set"), - }) + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ + Name: "destinations", + // Wrapped: fmt.Errorf("exactly one of destinations or sameness_group should be set"), + Wrapped: fmt.Errorf("exactly one of destinations or sameness_group should be set: %v || %v", + (len(config.Destinations) > 0), (config.SamenessGroup != ""), + ), + })) } for i, dest := range config.Destinations { - for _, err := range validateFailoverPolicyDestination(dest, ported) { - errs = append(errs, resource.ErrInvalidListElement{ + wrapDestErr := func(err error) error { + return wrapErr(resource.ErrInvalidListElement{ Name: "destinations", Index: i, Wrapped: err, }) } + if destErr := validateFailoverPolicyDestination(dest, ported, wrapDestErr); destErr != nil { + merr = multierror.Append(merr, destErr) + } } switch config.Mode { @@ -146,72 +202,62 @@ func validateFailoverConfig(config *pbcatalog.FailoverConfig, ported bool) []err case pbcatalog.FailoverMode_FAILOVER_MODE_SEQUENTIAL: case pbcatalog.FailoverMode_FAILOVER_MODE_ORDER_BY_LOCALITY: default: - errs = append(errs, resource.ErrInvalidField{ + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ Name: "mode", Wrapped: fmt.Errorf("not a supported enum value: %v", config.Mode), - }) + })) } // TODO: validate sameness group requirements - return errs + return merr } -func validateFailoverPolicyDestination(dest *pbcatalog.FailoverDestination, ported bool) []error { - var errs []error - if dest.Ref == nil { - errs = append(errs, resource.ErrInvalidField{ +func validateFailoverPolicyDestination(dest *pbcatalog.FailoverDestination, ported bool, wrapErr func(error) error) error { + var merr error + + wrapRefErr := func(err error) error { + return wrapErr(resource.ErrInvalidField{ Name: "ref", - Wrapped: resource.ErrMissing, - }) - } else if !resource.EqualType(dest.Ref.Type, ServiceType) { - errs = append(errs, resource.ErrInvalidField{ - Name: "ref", - Wrapped: resource.ErrInvalidReferenceType{ - AllowedType: ServiceType, - }, - }) - } else if dest.Ref.Section != "" { - errs = append(errs, resource.ErrInvalidField{ - Name: "ref", - Wrapped: resource.ErrInvalidField{ - Name: "section", - Wrapped: errors.New("section not supported for failover policy dest refs"), - }, + Wrapped: err, }) } + if refErr := ValidateLocalServiceRefNoSection(dest.Ref, wrapRefErr); refErr != nil { + merr = multierror.Append(merr, refErr) + } + // NOTE: Destinations here cannot define ports. Port equality is // assumed and will be reconciled. if dest.Port != "" { if ported { if portNameErr := validatePortName(dest.Port); portNameErr != nil { - errs = append(errs, resource.ErrInvalidField{ + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ Name: "port", Wrapped: portNameErr, - }) + })) } } else { - errs = append(errs, resource.ErrInvalidField{ + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ Name: "port", Wrapped: fmt.Errorf("ports cannot be specified explicitly for the general failover section since it relies upon port alignment"), - }) + })) } } hasPeer := false if dest.Ref != nil { - hasPeer = dest.Ref.Tenancy.PeerName != "local" + hasPeer = dest.Ref.Tenancy.PeerName != "" && dest.Ref.Tenancy.PeerName != "local" } if hasPeer && dest.Datacenter != "" { - errs = append(errs, resource.ErrInvalidField{ + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ Name: "datacenter", Wrapped: fmt.Errorf("ref.tenancy.peer_name and datacenter are mutually exclusive fields"), - }) + })) } - return errs + return merr } // SimplifyFailoverPolicy fully populates the PortConfigs map and clears the diff --git a/internal/catalog/internal/types/failover_policy_test.go b/internal/catalog/internal/types/failover_policy_test.go index d1c5cc13ec80..b07d90fddb33 100644 --- a/internal/catalog/internal/types/failover_policy_test.go +++ b/internal/catalog/internal/types/failover_policy_test.go @@ -4,6 +4,7 @@ package types import ( + "strings" "testing" "github.com/stretchr/testify/require" @@ -19,13 +20,15 @@ import ( func TestMutateFailoverPolicy(t *testing.T) { type testcase struct { - failover *pbcatalog.FailoverPolicy - expect *pbcatalog.FailoverPolicy - expectErr string + policyTenancy *pbresource.Tenancy + failover *pbcatalog.FailoverPolicy + expect *pbcatalog.FailoverPolicy + expectErr string } run := func(t *testing.T, tc testcase) { res := resourcetest.Resource(FailoverPolicyType, "api"). + WithTenancy(tc.policyTenancy). WithData(t, tc.failover). Build() @@ -134,6 +137,49 @@ func TestMutateFailoverPolicy(t *testing.T) { }, }, }, + "dest ref tenancy defaulting": { + policyTenancy: newTestTenancy("foo.bar"), + failover: &pbcatalog.FailoverPolicy{ + Config: &pbcatalog.FailoverConfig{ + Mode: pbcatalog.FailoverMode_FAILOVER_MODE_SEQUENTIAL, + Regions: []string{"foo", "bar"}, + Destinations: []*pbcatalog.FailoverDestination{ + {Ref: newRefWithTenancy(ServiceType, "", "api")}, + {Ref: newRefWithTenancy(ServiceType, ".zim", "api")}, + {Ref: newRefWithTenancy(ServiceType, "gir.zim", "api")}, + }, + }, + PortConfigs: map[string]*pbcatalog.FailoverConfig{ + "http": { + Destinations: []*pbcatalog.FailoverDestination{ + {Ref: newRefWithTenancy(ServiceType, "", "api")}, + {Ref: newRefWithTenancy(ServiceType, ".luthor", "api")}, + {Ref: newRefWithTenancy(ServiceType, "lex.luthor", "api")}, + }, + }, + }, + }, + expect: &pbcatalog.FailoverPolicy{ + Config: &pbcatalog.FailoverConfig{ + Mode: pbcatalog.FailoverMode_FAILOVER_MODE_SEQUENTIAL, + Regions: []string{"foo", "bar"}, + Destinations: []*pbcatalog.FailoverDestination{ + {Ref: newRefWithTenancy(ServiceType, "foo.bar", "api")}, + {Ref: newRefWithTenancy(ServiceType, "foo.zim", "api")}, + {Ref: newRefWithTenancy(ServiceType, "gir.zim", "api")}, + }, + }, + PortConfigs: map[string]*pbcatalog.FailoverConfig{ + "http": { + Destinations: []*pbcatalog.FailoverDestination{ + {Ref: newRefWithTenancy(ServiceType, "foo.bar", "api")}, + {Ref: newRefWithTenancy(ServiceType, "foo.luthor", "api")}, + {Ref: newRefWithTenancy(ServiceType, "lex.luthor", "api")}, + }, + }, + }, + }, + }, } for name, tc := range cases { @@ -224,7 +270,7 @@ func TestValidateFailoverPolicy(t *testing.T) { {Ref: newRef(WorkloadType, "api-backup")}, }, }, - expectErr: `invalid element at index 0 of list "destinations": invalid "ref" field: reference must have type catalog.v1alpha1.Service`, + expectErr: `invalid element at index 0 of list "destinations": invalid "ref" field: invalid "type" field: reference must have type catalog.v1alpha1.Service`, }, "dest: ref with section": { config: &pbcatalog.FailoverConfig{ @@ -232,23 +278,25 @@ func TestValidateFailoverPolicy(t *testing.T) { {Ref: resourcetest.Resource(ServiceType, "api").WithTenancy(resource.DefaultNamespacedTenancy()).Reference("blah")}, }, }, - expectErr: `invalid element at index 0 of list "destinations": invalid "ref" field: invalid "section" field: section not supported for failover policy dest refs`, - }, - "dest: ref peer and datacenter": { - config: &pbcatalog.FailoverConfig{ - Destinations: []*pbcatalog.FailoverDestination{ - {Ref: newRefWithPeer(ServiceType, "api", "peer1"), Datacenter: "dc2"}, - }, - }, - expectErr: `invalid element at index 0 of list "destinations": invalid "datacenter" field: ref.tenancy.peer_name and datacenter are mutually exclusive fields`, - }, - "dest: ref peer without datacenter": { - config: &pbcatalog.FailoverConfig{ - Destinations: []*pbcatalog.FailoverDestination{ - {Ref: newRefWithPeer(ServiceType, "api", "peer1")}, - }, - }, + expectErr: `invalid element at index 0 of list "destinations": invalid "ref" field: invalid "section" field: section cannot be set here`, }, + // TODO(v2/peering): re-enable when peering can exist + // "dest: ref peer and datacenter": { + // config: &pbcatalog.FailoverConfig{ + // Destinations: []*pbcatalog.FailoverDestination{ + // {Ref: newRefWithPeer(ServiceType, "api", "peer1"), Datacenter: "dc2"}, + // }, + // }, + // expectErr: `invalid element at index 0 of list "destinations": invalid "datacenter" field: ref.tenancy.peer_name and datacenter are mutually exclusive fields`, + // }, + // TODO(v2/peering): re-enable when peering can exist + // "dest: ref peer without datacenter": { + // config: &pbcatalog.FailoverConfig{ + // Destinations: []*pbcatalog.FailoverDestination{ + // {Ref: newRefWithPeer(ServiceType, "api", "peer1")}, + // }, + // }, + // }, "dest: ref datacenter without peer": { config: &pbcatalog.FailoverConfig{ Destinations: []*pbcatalog.FailoverDestination{ @@ -595,8 +643,33 @@ func newRef(typ *pbresource.Type, name string) *pbresource.Reference { Reference("") } +func newRefWithTenancy(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference { + return resourcetest.Resource(typ, name). + WithTenancy(newTestTenancy(tenancyStr)). + Reference("") +} + func newRefWithPeer(typ *pbresource.Type, name string, peer string) *pbresource.Reference { ref := newRef(typ, name) ref.Tenancy.PeerName = peer return ref } + +func newTestTenancy(s string) *pbresource.Tenancy { + parts := strings.Split(s, ".") + switch len(parts) { + case 0: + return resource.DefaultClusteredTenancy() + case 1: + v := resource.DefaultPartitionedTenancy() + v.Partition = parts[0] + return v + case 2: + v := resource.DefaultNamespacedTenancy() + v.Partition = parts[0] + v.Namespace = parts[1] + return v + default: + return &pbresource.Tenancy{Partition: "BAD", Namespace: "BAD", PeerName: "BAD"} + } +} diff --git a/internal/catalog/internal/types/validators.go b/internal/catalog/internal/types/validators.go index a9934156e298..a136ce069884 100644 --- a/internal/catalog/internal/types/validators.go +++ b/internal/catalog/internal/types/validators.go @@ -4,6 +4,7 @@ package types import ( + "errors" "fmt" "net" "regexp" @@ -237,3 +238,81 @@ func validateHealth(health pbcatalog.Health) error { return resource.NewConstError(fmt.Sprintf("not a supported enum value: %v", health)) } } + +// ValidateLocalServiceRefNoSection ensures the following: +// +// - ref is non-nil +// - type is ServiceType +// - section is empty +// - tenancy is set and partition/namespace are both non-empty +// - peer_name must be "local" +// +// Each possible validation error is wrapped in the wrapErr function before +// being collected in a multierror.Error. +func ValidateLocalServiceRefNoSection(ref *pbresource.Reference, wrapErr func(error) error) error { + if ref == nil { + return wrapErr(resource.ErrMissing) + } + + if !resource.EqualType(ref.Type, ServiceType) { + return wrapErr(resource.ErrInvalidField{ + Name: "type", + Wrapped: resource.ErrInvalidReferenceType{ + AllowedType: ServiceType, + }, + }) + } + + var merr error + if ref.Section != "" { + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ + Name: "section", + Wrapped: errors.New("section cannot be set here"), + })) + } + + if ref.Tenancy == nil { + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ + Name: "tenancy", + Wrapped: resource.ErrMissing, + })) + } else { + // NOTE: these are Service specific, since that's a Namespace-scoped type. + if ref.Tenancy.Partition == "" { + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ + Name: "tenancy", + Wrapped: resource.ErrInvalidField{ + Name: "partition", + Wrapped: resource.ErrEmpty, + }, + })) + } + if ref.Tenancy.Namespace == "" { + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ + Name: "tenancy", + Wrapped: resource.ErrInvalidField{ + Name: "namespace", + Wrapped: resource.ErrEmpty, + }, + })) + } + if ref.Tenancy.PeerName != "local" { + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ + Name: "tenancy", + Wrapped: resource.ErrInvalidField{ + Name: "peer_name", + Wrapped: errors.New(`must be set to "local"`), + }, + })) + } + } + + if ref.Name == "" { + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ + Name: "name", + Wrapped: resource.ErrMissing, + })) + } + + return merr +} From 087539fc7bee0ce94a78e8238042dce46c7f46ba Mon Sep 17 00:00:00 2001 From: Andrew Stucki Date: Mon, 18 Sep 2023 16:19:17 -0400 Subject: [PATCH 22/28] Fix gateway services cleanup where proxy deregistration happens after service deregistration (#18831) * Fix gateway services cleanup where proxy deregistration happens after service deregistration * Add test * Add changelog * Fix comment --- .changelog/18831.txt | 3 ++ agent/consul/state/catalog.go | 6 +++ agent/consul/state/catalog_test.go | 79 ++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 .changelog/18831.txt diff --git a/.changelog/18831.txt b/.changelog/18831.txt new file mode 100644 index 000000000000..0981fb6a368b --- /dev/null +++ b/.changelog/18831.txt @@ -0,0 +1,3 @@ +```release-note:bug +gateways: Fix a bug where gateway to service mappings weren't being cleaned up properly when externally registered proxies were being deregistered. +``` diff --git a/agent/consul/state/catalog.go b/agent/consul/state/catalog.go index f5007a893fd6..00be35e34bc9 100644 --- a/agent/consul/state/catalog.go +++ b/agent/consul/state/catalog.go @@ -2065,6 +2065,12 @@ func (s *Store) deleteServiceTxn(tx WriteTxn, idx uint64, nodeName, serviceID st if err := cleanupKindServiceName(tx, idx, sn, structs.ServiceKindConnectEnabled); err != nil { return fmt.Errorf("failed to cleanup connect-enabled service name: %v", err) } + // we need to do this if the proxy is deleted after the service itself + // as the guard after this might not be 1-1 between proxy and service + // names. + if err := cleanupGatewayWildcards(tx, idx, sn, false); err != nil { + return fmt.Errorf("failed to clean up gateway-service associations for %q: %v", psn.String(), err) + } } } diff --git a/agent/consul/state/catalog_test.go b/agent/consul/state/catalog_test.go index 6fc79a5a7c0f..5be9fd579d08 100644 --- a/agent/consul/state/catalog_test.go +++ b/agent/consul/state/catalog_test.go @@ -8311,6 +8311,85 @@ func TestCatalog_cleanupGatewayWildcards_panic(t *testing.T) { require.NoError(t, s.DeleteNode(6, "foo", nil, "")) } +func TestCatalog_cleanupGatewayWildcards_proxy(t *testing.T) { + s := testStateStore(t) + + require.NoError(t, s.EnsureNode(0, &structs.Node{ + ID: "c73b8fdf-4ef8-4e43-9aa2-59e85cc6a70c", + Node: "foo", + })) + require.NoError(t, s.EnsureConfigEntry(1, &structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "protocol": "http", + }, + })) + + defaultMeta := structs.DefaultEnterpriseMetaInDefaultPartition() + + require.NoError(t, s.EnsureConfigEntry(3, &structs.IngressGatewayConfigEntry{ + Kind: "ingress-gateway", + Name: "my-gateway-2-ingress", + Listeners: []structs.IngressListener{ + { + Port: 1111, + Protocol: "http", + Services: []structs.IngressService{ + { + Name: "*", + EnterpriseMeta: *defaultMeta, + }, + }, + }, + }, + })) + + // Register two services, a regular service, and a sidecar proxy for it + api := structs.NodeService{ + ID: "api", + Service: "api", + Address: "127.0.0.2", + Port: 443, + EnterpriseMeta: *defaultMeta, + } + require.NoError(t, s.EnsureService(4, "foo", &api)) + proxy := structs.NodeService{ + Kind: structs.ServiceKindConnectProxy, + ID: "api-proxy", + Service: "api-proxy", + Address: "127.0.0.3", + Port: 443, + Proxy: structs.ConnectProxyConfig{ + DestinationServiceName: "api", + DestinationServiceID: "api", + }, + EnterpriseMeta: *defaultMeta, + } + require.NoError(t, s.EnsureService(5, "foo", &proxy)) + + // make sure we have only one gateway service + _, services, err := s.GatewayServices(nil, "my-gateway-2-ingress", defaultMeta) + require.NoError(t, err) + require.Len(t, services, 1) + + // now delete the target service + require.NoError(t, s.DeleteService(6, "foo", "api", nil, "")) + + // at this point we still have the gateway services because we have a connect proxy still + _, services, err = s.GatewayServices(nil, "my-gateway-2-ingress", defaultMeta) + require.NoError(t, err) + require.Len(t, services, 1) + + // now delete the connect proxy + require.NoError(t, s.DeleteService(7, "foo", "api-proxy", nil, "")) + + // make sure we no longer have any services + _, services, err = s.GatewayServices(nil, "my-gateway-2-ingress", defaultMeta) + require.NoError(t, err) + require.Len(t, services, 0) +} + func TestCatalog_DownstreamsForService(t *testing.T) { defaultMeta := structs.DefaultEnterpriseMetaInDefaultPartition() From 49cb84297ffbae9adfe8cd5de3be0163dd6f08ae Mon Sep 17 00:00:00 2001 From: Ronald Date: Mon, 18 Sep 2023 17:10:35 -0400 Subject: [PATCH 23/28] Move ACL templated policies to hcl files (#18853) --- .copywrite.hcl | 3 ++ agent/acl_endpoint_test.go | 2 +- agent/structs/acl_templated_policy.go | 5 ++- agent/structs/acl_templated_policy_ce.go | 40 ++++--------------- .../acltemplatedpolicy/policies/ce/dns.hcl | 10 +++++ .../acltemplatedpolicy/policies/ce/node.hcl | 7 ++++ .../policies/ce/service.hcl | 13 ++++++ command/acl/templatedpolicy/formatter_test.go | 4 +- .../list/templated_policy_list_test.go | 2 +- 9 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 agent/structs/acltemplatedpolicy/policies/ce/dns.hcl create mode 100644 agent/structs/acltemplatedpolicy/policies/ce/node.hcl create mode 100644 agent/structs/acltemplatedpolicy/policies/ce/service.hcl diff --git a/.copywrite.hcl b/.copywrite.hcl index dbe52115c27e..243a0a730722 100644 --- a/.copywrite.hcl +++ b/.copywrite.hcl @@ -27,6 +27,9 @@ project { "agent/grpc-middleware/rate_limit_mappings.gen.go", "agent/uiserver/dist/**", + # ignoring policy embedded files + "agent/structs/acltemplatedpolicy/policies/ce/**", + # licensed under MPL - ignoring for now until the copywrite tool can support # multiple licenses per repo. "sdk/**", diff --git a/agent/acl_endpoint_test.go b/agent/acl_endpoint_test.go index ca4fb3668d80..e3cc5e4143cc 100644 --- a/agent/acl_endpoint_test.go +++ b/agent/acl_endpoint_test.go @@ -1401,7 +1401,7 @@ func TestACL_HTTP(t *testing.T) { var templatedPolicy api.ACLTemplatedPolicyResponse require.NoError(t, json.NewDecoder(resp.Body).Decode(&templatedPolicy)) - require.Equal(t, structs.ACLTemplatedPolicyDNSSchema, templatedPolicy.Schema) + require.Equal(t, structs.ACLTemplatedPolicyNoRequiredVariablesSchema, templatedPolicy.Schema) require.Equal(t, api.ACLTemplatedPolicyDNSName, templatedPolicy.TemplateName) require.Equal(t, structs.ACLTemplatedPolicyDNS, templatedPolicy.Template) }) diff --git a/agent/structs/acl_templated_policy.go b/agent/structs/acl_templated_policy.go index fcb50032915d..ad5c5f849c86 100644 --- a/agent/structs/acl_templated_policy.go +++ b/agent/structs/acl_templated_policy.go @@ -31,7 +31,8 @@ const ( ACLTemplatedPolicyServiceID = "00000000-0000-0000-0000-000000000003" ACLTemplatedPolicyNodeID = "00000000-0000-0000-0000-000000000004" ACLTemplatedPolicyDNSID = "00000000-0000-0000-0000-000000000005" - ACLTemplatedPolicyDNSSchema = "" // empty schema as it does not require variables + + ACLTemplatedPolicyNoRequiredVariablesSchema = "" // catch-all schema for all templated policy that don't require a schema ) // ACLTemplatedPolicyBase contains basic information about builtin templated policies @@ -63,7 +64,7 @@ var ( api.ACLTemplatedPolicyDNSName: { TemplateID: ACLTemplatedPolicyDNSID, TemplateName: api.ACLTemplatedPolicyDNSName, - Schema: ACLTemplatedPolicyDNSSchema, + Schema: ACLTemplatedPolicyNoRequiredVariablesSchema, Template: ACLTemplatedPolicyDNS, }, } diff --git a/agent/structs/acl_templated_policy_ce.go b/agent/structs/acl_templated_policy_ce.go index 5645f00281f1..f4a857b5c69a 100644 --- a/agent/structs/acl_templated_policy_ce.go +++ b/agent/structs/acl_templated_policy_ce.go @@ -5,40 +5,16 @@ package structs -const ( - ACLTemplatedPolicyService = ` -service "{{.Name}}" { - policy = "write" -} -service "{{.Name}}-sidecar-proxy" { - policy = "write" -} -service_prefix "" { - policy = "read" -} -node_prefix "" { - policy = "read" -}` +import _ "embed" - ACLTemplatedPolicyNode = ` -node "{{.Name}}" { - policy = "write" -} -service_prefix "" { - policy = "read" -}` +//go:embed acltemplatedpolicy/policies/ce/service.hcl +var ACLTemplatedPolicyService string - ACLTemplatedPolicyDNS = ` -node_prefix "" { - policy = "read" -} -service_prefix "" { - policy = "read" -} -query_prefix "" { - policy = "read" -}` -) +//go:embed acltemplatedpolicy/policies/ce/node.hcl +var ACLTemplatedPolicyNode string + +//go:embed acltemplatedpolicy/policies/ce/dns.hcl +var ACLTemplatedPolicyDNS string func (t *ACLToken) TemplatedPolicyList() []*ACLTemplatedPolicy { if len(t.TemplatedPolicies) == 0 { diff --git a/agent/structs/acltemplatedpolicy/policies/ce/dns.hcl b/agent/structs/acltemplatedpolicy/policies/ce/dns.hcl new file mode 100644 index 000000000000..6627f1c96a5f --- /dev/null +++ b/agent/structs/acltemplatedpolicy/policies/ce/dns.hcl @@ -0,0 +1,10 @@ + +node_prefix "" { + policy = "read" +} +service_prefix "" { + policy = "read" +} +query_prefix "" { + policy = "read" +} \ No newline at end of file diff --git a/agent/structs/acltemplatedpolicy/policies/ce/node.hcl b/agent/structs/acltemplatedpolicy/policies/ce/node.hcl new file mode 100644 index 000000000000..b6b03a2250f0 --- /dev/null +++ b/agent/structs/acltemplatedpolicy/policies/ce/node.hcl @@ -0,0 +1,7 @@ + +node "{{.Name}}" { + policy = "write" +} +service_prefix "" { + policy = "read" +} \ No newline at end of file diff --git a/agent/structs/acltemplatedpolicy/policies/ce/service.hcl b/agent/structs/acltemplatedpolicy/policies/ce/service.hcl new file mode 100644 index 000000000000..a8d2faf2791d --- /dev/null +++ b/agent/structs/acltemplatedpolicy/policies/ce/service.hcl @@ -0,0 +1,13 @@ + +service "{{.Name}}" { + policy = "write" +} +service "{{.Name}}-sidecar-proxy" { + policy = "write" +} +service_prefix "" { + policy = "read" +} +node_prefix "" { + policy = "read" +} \ No newline at end of file diff --git a/command/acl/templatedpolicy/formatter_test.go b/command/acl/templatedpolicy/formatter_test.go index 887e518ea0bd..71a3ca17246b 100644 --- a/command/acl/templatedpolicy/formatter_test.go +++ b/command/acl/templatedpolicy/formatter_test.go @@ -42,7 +42,7 @@ func testFormatTemplatedPolicy(t *testing.T, dirPath string) { "dns-templated-policy": { templatedPolicy: api.ACLTemplatedPolicyResponse{ TemplateName: api.ACLTemplatedPolicyDNSName, - Schema: structs.ACLTemplatedPolicyDNSSchema, + Schema: structs.ACLTemplatedPolicyNoRequiredVariablesSchema, Template: structs.ACLTemplatedPolicyDNS, }, }, @@ -94,7 +94,7 @@ func testFormatTemplatedPolicyList(t *testing.T, dirPath string) { }, "builtin/dns": { TemplateName: api.ACLTemplatedPolicyDNSName, - Schema: structs.ACLTemplatedPolicyDNSSchema, + Schema: structs.ACLTemplatedPolicyNoRequiredVariablesSchema, Template: structs.ACLTemplatedPolicyDNS, }, "builtin/service": { diff --git a/command/acl/templatedpolicy/list/templated_policy_list_test.go b/command/acl/templatedpolicy/list/templated_policy_list_test.go index bdde3782d76d..5f548918e7af 100644 --- a/command/acl/templatedpolicy/list/templated_policy_list_test.go +++ b/command/acl/templatedpolicy/list/templated_policy_list_test.go @@ -98,5 +98,5 @@ func TestTemplatedPolicyListCommand_JSON(t *testing.T) { err := json.Unmarshal([]byte(output), &jsonOutput) assert.NoError(t, err) outputTemplate := jsonOutput[api.ACLTemplatedPolicyDNSName] - assert.Equal(t, structs.ACLTemplatedPolicyDNSSchema, outputTemplate.Schema) + assert.Equal(t, structs.ACLTemplatedPolicyNoRequiredVariablesSchema, outputTemplate.Schema) } From 696aa1bbd20f5b9b6557a8db9259e2995843fd50 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" <4903+rboyer@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:19:54 -0500 Subject: [PATCH 24/28] mesh: update xds controller to synthesize empty endpoints when no endpoints ref is found (#18835) --- .../internal/controllers/xds/controller.go | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/internal/mesh/internal/controllers/xds/controller.go b/internal/mesh/internal/controllers/xds/controller.go index f2aac840c1e1..604181a49e18 100644 --- a/internal/mesh/internal/controllers/xds/controller.go +++ b/internal/mesh/internal/controllers/xds/controller.go @@ -24,6 +24,7 @@ import ( ) const ControllerName = "consul.io/xds-controller" + const defaultTenancy = "default" func Controller(endpointsMapper *bimapper.Mapper, updater ProxyUpdater, fetcher TrustBundleFetcher, leafCertManager *leafcert.Manager, leafMapper *LeafMapper, leafCancels *LeafCancels, datacenter string) controller.Controller { @@ -158,39 +159,52 @@ func (r *xdsReconciler) Reconcile(ctx context.Context, rt controller.Runtime, re // Step 1: Resolve the reference by looking up the ServiceEndpoints. // serviceEndpoints will not be nil unless there is an error. - serviceEndpoints, err := getServiceEndpoints(ctx, rt, endpointRef.Id) - if err != nil { - rt.Logger.Error("error reading service endpoint", "id", endpointRef.Id, "error", err) - // Set the status. - statusCondition = status.ConditionRejectedErrorReadingEndpoints(status.KeyFromID(endpointRef.Id), err.Error()) - status.WriteStatusIfChanged(ctx, rt, pstResource, statusCondition) + // + // TODO(rb/v2): note we should expose a flag on the endpointRef indicating if the user + // wants the absence of an Endpoints to imply returning a slice of no data, vs failing outright. + // In xdsv1 we call this the "allowEmpty" semantic. Here we are assuming "allowEmpty=true" + var psEndpoints *pbproxystate.Endpoints + if endpointRef.Id != nil { + serviceEndpoints, err := getServiceEndpoints(ctx, rt, endpointRef.Id) + if err != nil { + rt.Logger.Error("error reading service endpoint", "id", endpointRef.Id, "error", err) + // Set the status. + statusCondition = status.ConditionRejectedErrorReadingEndpoints(status.KeyFromID(endpointRef.Id), err.Error()) + status.WriteStatusIfChanged(ctx, rt, pstResource, statusCondition) - return err - } + return err + } - // Step 2: Translate it into pbproxystate.Endpoints. - psEndpoints, err := generateProxyStateEndpoints(serviceEndpoints, endpointRef.Port) - if err != nil { - rt.Logger.Error("error translating service endpoints to proxy state endpoints", "endpoint", endpointRef.Id, "error", err) + // Step 2: Translate it into pbproxystate.Endpoints. + psEndpoints, err = generateProxyStateEndpoints(serviceEndpoints, endpointRef.Port) + if err != nil { + rt.Logger.Error("error translating service endpoints to proxy state endpoints", "endpoint", endpointRef.Id, "error", err) - // Set the status. - statusCondition = status.ConditionRejectedCreatingProxyStateEndpoints(status.KeyFromID(endpointRef.Id), err.Error()) - status.WriteStatusIfChanged(ctx, rt, pstResource, statusCondition) + // Set the status. + statusCondition = status.ConditionRejectedCreatingProxyStateEndpoints(status.KeyFromID(endpointRef.Id), err.Error()) + status.WriteStatusIfChanged(ctx, rt, pstResource, statusCondition) - return err + return err + } + } else { + psEndpoints = &pbproxystate.Endpoints{} } // Step 3: Add the endpoints to ProxyState. proxyStateTemplate.Template.ProxyState.Endpoints[xdsClusterName] = psEndpoints - // Track all the endpoints that are used by this ProxyStateTemplate, so we can use this for step 4. - endpointResourceRef := resource.Reference(endpointRef.Id, "") - endpointsInProxyStateTemplate = append(endpointsInProxyStateTemplate, endpointResourceRef) - + if endpointRef.Id != nil { + // Track all the endpoints that are used by this ProxyStateTemplate, so we can use this for step 4. + endpointResourceRef := resource.Reference(endpointRef.Id, "") + endpointsInProxyStateTemplate = append(endpointsInProxyStateTemplate, endpointResourceRef) + } } // Step 4: Track relationships between ProxyStateTemplates and ServiceEndpoints. r.endpointsMapper.TrackItem(req.ID, endpointsInProxyStateTemplate) + if len(endpointsInProxyStateTemplate) == 0 { + r.endpointsMapper.UntrackItem(req.ID) + } // Iterate through leaf certificate references. // For each leaf certificate reference, the controller should: From dabbc9627bcf8f7b722bdc2a5fbf6e7c8da66850 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" <4903+rboyer@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:02:13 -0500 Subject: [PATCH 25/28] mesh: normalize/default/validate tenancy components of mesh internal References (#18827) HTTPRoute, GRPCRoute, TCPRoute, and Upstreams resources contain inner Reference fields. We want to ensure that components of those reference Tenancy fields left unspecified are defaulted using the tenancy of the enclosing resource. As the underlying helper being used to do the normalization calls the function modified in #18822, it also means that the PeerName field will be set to "local" for now automatically to avoid "local" != "" issues downstream. --- .../destinations_mapper_test.go | 31 ++- internal/mesh/internal/types/grpc_route.go | 63 ++++-- .../mesh/internal/types/grpc_route_test.go | 95 ++++++++- internal/mesh/internal/types/http_route.go | 37 ++-- .../mesh/internal/types/http_route_test.go | 187 +++++++++++++++-- internal/mesh/internal/types/tcp_route.go | 63 ++++-- .../mesh/internal/types/tcp_route_test.go | 95 ++++++++- internal/mesh/internal/types/upstreams.go | 86 +++++++- .../mesh/internal/types/upstreams_test.go | 198 ++++++++++++++++++ internal/mesh/internal/types/xroute.go | 146 ++++++------- 10 files changed, 837 insertions(+), 164 deletions(-) create mode 100644 internal/mesh/internal/types/upstreams_test.go diff --git a/internal/mesh/internal/mappers/sidecarproxymapper/destinations_mapper_test.go b/internal/mesh/internal/mappers/sidecarproxymapper/destinations_mapper_test.go index 69937fb34d41..5e5babc8c79a 100644 --- a/internal/mesh/internal/mappers/sidecarproxymapper/destinations_mapper_test.go +++ b/internal/mesh/internal/mappers/sidecarproxymapper/destinations_mapper_test.go @@ -25,24 +25,36 @@ import ( func TestMapDestinationsToProxyStateTemplate(t *testing.T) { client := svctest.RunResourceService(t, types.Register, catalog.RegisterTypes) webWorkload1 := resourcetest.Resource(catalog.WorkloadType, "web-abc"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, &pbcatalog.Workload{ Addresses: []*pbcatalog.WorkloadAddress{{Host: "10.0.0.1"}}, Ports: map[string]*pbcatalog.WorkloadPort{"tcp": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}}, }). Write(t, client) webWorkload2 := resourcetest.Resource(catalog.WorkloadType, "web-def"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, &pbcatalog.Workload{ Addresses: []*pbcatalog.WorkloadAddress{{Host: "10.0.0.2"}}, Ports: map[string]*pbcatalog.WorkloadPort{"tcp": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}}, }). Write(t, client) webWorkload3 := resourcetest.Resource(catalog.WorkloadType, "non-prefix-web"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, &pbcatalog.Workload{ Addresses: []*pbcatalog.WorkloadAddress{{Host: "10.0.0.3"}}, Ports: map[string]*pbcatalog.WorkloadPort{"tcp": {Port: 8081, Protocol: pbcatalog.Protocol_PROTOCOL_TCP}}, }). Write(t, client) + var ( + api1ServiceRef = resourcetest.Resource(catalog.ServiceType, "api-1"). + WithTenancy(resource.DefaultNamespacedTenancy()). + ReferenceNoSection() + api2ServiceRef = resourcetest.Resource(catalog.ServiceType, "api-2"). + WithTenancy(resource.DefaultNamespacedTenancy()). + ReferenceNoSection() + ) + webDestinationsData := &pbmesh.Upstreams{ Workloads: &pbcatalog.WorkloadSelector{ Names: []string{"non-prefix-web"}, @@ -50,21 +62,22 @@ func TestMapDestinationsToProxyStateTemplate(t *testing.T) { }, Upstreams: []*pbmesh.Upstream{ { - DestinationRef: resourcetest.Resource(catalog.ServiceType, "api-1").ReferenceNoSection(), + DestinationRef: api1ServiceRef, DestinationPort: "tcp", }, { - DestinationRef: resourcetest.Resource(catalog.ServiceType, "api-2").ReferenceNoSection(), + DestinationRef: api2ServiceRef, DestinationPort: "tcp1", }, { - DestinationRef: resourcetest.Resource(catalog.ServiceType, "api-2").ReferenceNoSection(), + DestinationRef: api2ServiceRef, DestinationPort: "tcp2", }, }, } webDestinations := resourcetest.Resource(types.UpstreamsType, "web-destinations"). + WithTenancy(resource.DefaultNamespacedTenancy()). WithData(t, webDestinationsData). Write(t, client) @@ -81,10 +94,14 @@ func TestMapDestinationsToProxyStateTemplate(t *testing.T) { require.NoError(t, err) prototest.AssertElementsMatch(t, expRequests, requests) - //var expDestinations []*intermediate.CombinedDestinationRef - proxy1ID := resourcetest.Resource(types.ProxyStateTemplateType, webWorkload1.Id.Name).ID() - proxy2ID := resourcetest.Resource(types.ProxyStateTemplateType, webWorkload2.Id.Name).ID() - proxy3ID := resourcetest.Resource(types.ProxyStateTemplateType, webWorkload3.Id.Name).ID() + var ( + proxy1ID = resourcetest.Resource(types.ProxyStateTemplateType, webWorkload1.Id.Name). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + proxy2ID = resourcetest.Resource(types.ProxyStateTemplateType, webWorkload2.Id.Name). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + proxy3ID = resourcetest.Resource(types.ProxyStateTemplateType, webWorkload3.Id.Name). + WithTenancy(resource.DefaultNamespacedTenancy()).ID() + ) for _, u := range webDestinationsData.Upstreams { expDestination := intermediate.CombinedDestinationRef{ ServiceRef: u.DestinationRef, diff --git a/internal/mesh/internal/types/grpc_route.go b/internal/mesh/internal/types/grpc_route.go index a7a839f35902..a5cb8de30b30 100644 --- a/internal/mesh/internal/types/grpc_route.go +++ b/internal/mesh/internal/types/grpc_route.go @@ -30,14 +30,45 @@ var ( func RegisterGRPCRoute(r resource.Registry) { r.Register(resource.Registration{ - Type: GRPCRouteV1Alpha1Type, - Proto: &pbmesh.GRPCRoute{}, - Scope: resource.ScopeNamespace, - // TODO(rb): normalize parent/backend ref tenancies in a Mutate hook + Type: GRPCRouteV1Alpha1Type, + Proto: &pbmesh.GRPCRoute{}, + Scope: resource.ScopeNamespace, + Mutate: MutateGRPCRoute, Validate: ValidateGRPCRoute, }) } +func MutateGRPCRoute(res *pbresource.Resource) error { + var route pbmesh.GRPCRoute + + if err := res.Data.UnmarshalTo(&route); err != nil { + return resource.NewErrDataParse(&route, err) + } + + changed := false + + if mutateParentRefs(res.Id.Tenancy, route.ParentRefs) { + changed = true + } + + for _, rule := range route.Rules { + for _, backend := range rule.BackendRefs { + if backend.BackendRef == nil || backend.BackendRef.Ref == nil { + continue + } + if mutateXRouteRef(res.Id.Tenancy, backend.BackendRef.Ref) { + changed = true + } + } + } + + if !changed { + return nil + } + + return res.Data.MarshalFrom(&route) +} + func ValidateGRPCRoute(res *pbresource.Resource) error { var route pbmesh.GRPCRoute @@ -170,14 +201,6 @@ func ValidateGRPCRoute(res *pbresource.Resource) error { } if len(rule.BackendRefs) == 0 { - /* - BackendRefs (optional)ΒΆ - - BackendRefs defines API objects where matching requests should be - sent. If unspecified, the rule performs no forwarding. If - unspecified and no filters are specified that would result in a - response being sent, a 404 error code is returned. - */ merr = multierror.Append(merr, wrapRuleErr( resource.ErrInvalidField{ Name: "backend_refs", @@ -193,13 +216,15 @@ func ValidateGRPCRoute(res *pbresource.Resource) error { Wrapped: err, }) } - for _, err := range validateBackendRef(hbref.BackendRef) { - merr = multierror.Append(merr, wrapBackendRefErr( - resource.ErrInvalidField{ - Name: "backend_ref", - Wrapped: err, - }, - )) + + wrapBackendRefFieldErr := func(err error) error { + return wrapBackendRefErr(resource.ErrInvalidField{ + Name: "backend_ref", + Wrapped: err, + }) + } + if err := validateBackendRef(hbref.BackendRef, wrapBackendRefFieldErr); err != nil { + merr = multierror.Append(merr, err) } if len(hbref.Filters) > 0 { diff --git a/internal/mesh/internal/types/grpc_route_test.go b/internal/mesh/internal/types/grpc_route_test.go index e9abc118c654..24ab8a17fe27 100644 --- a/internal/mesh/internal/types/grpc_route_test.go +++ b/internal/mesh/internal/types/grpc_route_test.go @@ -7,14 +7,99 @@ import ( "testing" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/resource/resourcetest" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" "github.com/hashicorp/consul/proto/private/prototest" "github.com/hashicorp/consul/sdk/testutil" ) +func TestMutateGRPCRoute(t *testing.T) { + type testcase struct { + routeTenancy *pbresource.Tenancy + route *pbmesh.GRPCRoute + expect *pbmesh.GRPCRoute + } + + cases := map[string]testcase{} + + // Add common parent refs test cases. + for name, parentTC := range getXRouteParentRefMutateTestCases() { + cases["parent-ref: "+name] = testcase{ + routeTenancy: parentTC.routeTenancy, + route: &pbmesh.GRPCRoute{ + ParentRefs: parentTC.refs, + }, + expect: &pbmesh.GRPCRoute{ + ParentRefs: parentTC.expect, + }, + } + } + // add common backend ref test cases. + for name, backendTC := range getXRouteBackendRefMutateTestCases() { + var ( + refs []*pbmesh.GRPCBackendRef + expect []*pbmesh.GRPCBackendRef + ) + for _, br := range backendTC.refs { + refs = append(refs, &pbmesh.GRPCBackendRef{ + BackendRef: br, + }) + } + for _, br := range backendTC.expect { + expect = append(expect, &pbmesh.GRPCBackendRef{ + BackendRef: br, + }) + } + cases["backend-ref: "+name] = testcase{ + routeTenancy: backendTC.routeTenancy, + route: &pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(catalog.ServiceType, "web", ""), + }, + Rules: []*pbmesh.GRPCRouteRule{ + {BackendRefs: refs}, + }, + }, + expect: &pbmesh.GRPCRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(catalog.ServiceType, "web", ""), + }, + Rules: []*pbmesh.GRPCRouteRule{ + {BackendRefs: expect}, + }, + }, + } + } + + run := func(t *testing.T, tc testcase) { + res := resourcetest.Resource(GRPCRouteType, "api"). + WithTenancy(tc.routeTenancy). + WithData(t, tc.route). + Build() + + err := MutateGRPCRoute(res) + require.NoError(t, err) + + got := resourcetest.MustDecode[*pbmesh.GRPCRoute](t, res) + + if tc.expect == nil { + tc.expect = proto.Clone(tc.route).(*pbmesh.GRPCRoute) + } + + prototest.AssertDeepEqual(t, tc.expect, got.Data) + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + run(t, tc) + }) + } +} + func TestValidateGRPCRoute(t *testing.T) { type testcase struct { route *pbmesh.GRPCRoute @@ -26,7 +111,15 @@ func TestValidateGRPCRoute(t *testing.T) { WithData(t, tc.route). Build() - err := ValidateGRPCRoute(res) + // Ensure things are properly mutated and updated in the inputs. + err := MutateGRPCRoute(res) + require.NoError(t, err) + { + mutated := resourcetest.MustDecode[*pbmesh.GRPCRoute](t, res) + tc.route = mutated.Data + } + + err = ValidateGRPCRoute(res) // Verify that validate didn't actually change the object. got := resourcetest.MustDecode[*pbmesh.GRPCRoute](t, res) diff --git a/internal/mesh/internal/types/http_route.go b/internal/mesh/internal/types/http_route.go index 9d94128eb864..a2d55631cfb9 100644 --- a/internal/mesh/internal/types/http_route.go +++ b/internal/mesh/internal/types/http_route.go @@ -49,6 +49,10 @@ func MutateHTTPRoute(res *pbresource.Resource) error { changed := false + if mutateParentRefs(res.Id.Tenancy, route.ParentRefs) { + changed = true + } + for _, rule := range route.Rules { for _, match := range rule.Matches { if match.Method != "" { @@ -59,10 +63,16 @@ func MutateHTTPRoute(res *pbresource.Resource) error { } } } + for _, backend := range rule.BackendRefs { + if backend.BackendRef == nil || backend.BackendRef.Ref == nil { + continue + } + if mutateXRouteRef(res.Id.Tenancy, backend.BackendRef.Ref) { + changed = true + } + } } - // TODO(rb): normalize parent/backend ref tenancies - if !changed { return nil } @@ -264,14 +274,6 @@ func ValidateHTTPRoute(res *pbresource.Resource) error { } if len(rule.BackendRefs) == 0 { - /* - BackendRefs (optional)ΒΆ - - BackendRefs defines API objects where matching requests should be - sent. If unspecified, the rule performs no forwarding. If - unspecified and no filters are specified that would result in a - response being sent, a 404 error code is returned. - */ merr = multierror.Append(merr, wrapRuleErr( resource.ErrInvalidField{ Name: "backend_refs", @@ -288,13 +290,14 @@ func ValidateHTTPRoute(res *pbresource.Resource) error { }) } - for _, err := range validateBackendRef(hbref.BackendRef) { - merr = multierror.Append(merr, wrapBackendRefErr( - resource.ErrInvalidField{ - Name: "backend_ref", - Wrapped: err, - }, - )) + wrapBackendRefFieldErr := func(err error) error { + return wrapBackendRefErr(resource.ErrInvalidField{ + Name: "backend_ref", + Wrapped: err, + }) + } + if err := validateBackendRef(hbref.BackendRef, wrapBackendRefFieldErr); err != nil { + merr = multierror.Append(merr, err) } if len(hbref.Filters) > 0 { diff --git a/internal/mesh/internal/types/http_route_test.go b/internal/mesh/internal/types/http_route_test.go index 5b9a0ee42c85..49ab8ee83ba4 100644 --- a/internal/mesh/internal/types/http_route_test.go +++ b/internal/mesh/internal/types/http_route_test.go @@ -4,6 +4,7 @@ package types import ( + "strings" "testing" "time" @@ -23,13 +24,15 @@ import ( func TestMutateHTTPRoute(t *testing.T) { type testcase struct { - route *pbmesh.HTTPRoute - expect *pbmesh.HTTPRoute - expectErr string + routeTenancy *pbresource.Tenancy + route *pbmesh.HTTPRoute + expect *pbmesh.HTTPRoute + expectErr string } run := func(t *testing.T, tc testcase) { res := resourcetest.Resource(HTTPRouteType, "api"). + WithTenancy(tc.routeTenancy). WithData(t, tc.route). Build() @@ -139,6 +142,55 @@ func TestMutateHTTPRoute(t *testing.T) { }, } + // Add common parent refs test cases. + for name, parentTC := range getXRouteParentRefMutateTestCases() { + cases["parent-ref: "+name] = testcase{ + routeTenancy: parentTC.routeTenancy, + route: &pbmesh.HTTPRoute{ + ParentRefs: parentTC.refs, + }, + expect: &pbmesh.HTTPRoute{ + ParentRefs: parentTC.expect, + }, + } + } + // add common backend ref test cases. + for name, backendTC := range getXRouteBackendRefMutateTestCases() { + var ( + refs []*pbmesh.HTTPBackendRef + expect []*pbmesh.HTTPBackendRef + ) + for _, br := range backendTC.refs { + refs = append(refs, &pbmesh.HTTPBackendRef{ + BackendRef: br, + }) + } + for _, br := range backendTC.expect { + expect = append(expect, &pbmesh.HTTPBackendRef{ + BackendRef: br, + }) + } + cases["backend-ref: "+name] = testcase{ + routeTenancy: backendTC.routeTenancy, + route: &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(catalog.ServiceType, "web", ""), + }, + Rules: []*pbmesh.HTTPRouteRule{ + {BackendRefs: refs}, + }, + }, + expect: &pbmesh.HTTPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(catalog.ServiceType, "web", ""), + }, + Rules: []*pbmesh.HTTPRouteRule{ + {BackendRefs: expect}, + }, + }, + } + } + for name, tc := range cases { t.Run(name, func(t *testing.T) { run(t, tc) @@ -157,8 +209,13 @@ func TestValidateHTTPRoute(t *testing.T) { WithData(t, tc.route). Build() + // Ensure things are properly mutated and updated in the inputs. err := MutateHTTPRoute(res) require.NoError(t, err) + { + mutated := resourcetest.MustDecode[*pbmesh.HTTPRoute](t, res) + tc.route = mutated.Data + } err = ValidateHTTPRoute(res) @@ -761,6 +818,80 @@ func TestValidateHTTPRoute(t *testing.T) { } } +type xRouteParentRefMutateTestcase struct { + routeTenancy *pbresource.Tenancy + refs []*pbmesh.ParentReference + expect []*pbmesh.ParentReference +} + +func getXRouteParentRefMutateTestCases() map[string]xRouteParentRefMutateTestcase { + newRef := func(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference { + return resourcetest.Resource(typ, name). + WithTenancy(newTestTenancy(tenancyStr)). + Reference("") + } + + newParentRef := func(typ *pbresource.Type, tenancyStr, name, port string) *pbmesh.ParentReference { + return &pbmesh.ParentReference{ + Ref: newRef(typ, tenancyStr, name), + Port: port, + } + } + + return map[string]xRouteParentRefMutateTestcase{ + "parent ref tenancies defaulted": { + routeTenancy: newTestTenancy("foo.bar"), + refs: []*pbmesh.ParentReference{ + newParentRef(catalog.ServiceType, "", "api", ""), + newParentRef(catalog.ServiceType, ".zim", "api", ""), + newParentRef(catalog.ServiceType, "gir.zim", "api", ""), + }, + expect: []*pbmesh.ParentReference{ + newParentRef(catalog.ServiceType, "foo.bar", "api", ""), + newParentRef(catalog.ServiceType, "foo.zim", "api", ""), + newParentRef(catalog.ServiceType, "gir.zim", "api", ""), + }, + }, + } +} + +type xRouteBackendRefMutateTestcase struct { + routeTenancy *pbresource.Tenancy + refs []*pbmesh.BackendReference + expect []*pbmesh.BackendReference +} + +func getXRouteBackendRefMutateTestCases() map[string]xRouteBackendRefMutateTestcase { + newRef := func(typ *pbresource.Type, tenancyStr, name string) *pbresource.Reference { + return resourcetest.Resource(typ, name). + WithTenancy(newTestTenancy(tenancyStr)). + Reference("") + } + + newBackendRef := func(typ *pbresource.Type, tenancyStr, name, port string) *pbmesh.BackendReference { + return &pbmesh.BackendReference{ + Ref: newRef(typ, tenancyStr, name), + Port: port, + } + } + + return map[string]xRouteBackendRefMutateTestcase{ + "backend ref tenancies defaulted": { + routeTenancy: newTestTenancy("foo.bar"), + refs: []*pbmesh.BackendReference{ + newBackendRef(catalog.ServiceType, "", "api", ""), + newBackendRef(catalog.ServiceType, ".zim", "api", ""), + newBackendRef(catalog.ServiceType, "gir.zim", "api", ""), + }, + expect: []*pbmesh.BackendReference{ + newBackendRef(catalog.ServiceType, "foo.bar", "api", ""), + newBackendRef(catalog.ServiceType, "foo.zim", "api", ""), + newBackendRef(catalog.ServiceType, "gir.zim", "api", ""), + }, + }, + } +} + type xRouteParentRefTestcase struct { refs []*pbmesh.ParentReference expectErr string @@ -786,7 +917,7 @@ func getXRouteParentRefTestCases() map[string]xRouteParentRefTestcase { newParentRef(catalog.ServiceType, "api", ""), newParentRef(catalog.WorkloadType, "api", ""), }, - expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: reference must have type catalog.v1alpha1.Service`, + expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: invalid "type" field: reference must have type catalog.v1alpha1.Service`, }, "parent ref with section": { refs: []*pbmesh.ParentReference{ @@ -796,35 +927,35 @@ func getXRouteParentRefTestCases() map[string]xRouteParentRefTestcase { Port: "http", }, }, - expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: invalid "section" field: section not supported for service parent refs`, + expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: invalid "section" field: section cannot be set here`, }, "duplicate exact parents": { refs: []*pbmesh.ParentReference{ newParentRef(catalog.ServiceType, "api", "http"), newParentRef(catalog.ServiceType, "api", "http"), }, - expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: parent ref "catalog.v1alpha1.Service/default.local.default/api" for port "http" exists twice`, + expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v1alpha1.Service/default.local.default/api" for port "http" exists twice`, }, "duplicate wild parents": { refs: []*pbmesh.ParentReference{ newParentRef(catalog.ServiceType, "api", ""), newParentRef(catalog.ServiceType, "api", ""), }, - expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: parent ref "catalog.v1alpha1.Service/default.local.default/api" for wildcard port exists twice`, + expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v1alpha1.Service/default.local.default/api" for wildcard port exists twice`, }, "duplicate parents via exact+wild overlap": { refs: []*pbmesh.ParentReference{ newParentRef(catalog.ServiceType, "api", "http"), newParentRef(catalog.ServiceType, "api", ""), }, - expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: parent ref "catalog.v1alpha1.Service/default.local.default/api" for ports [http] covered by wildcard port already`, + expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v1alpha1.Service/default.local.default/api" for ports [http] covered by wildcard port already`, }, "duplicate parents via exact+wild overlap (reversed)": { refs: []*pbmesh.ParentReference{ newParentRef(catalog.ServiceType, "api", ""), newParentRef(catalog.ServiceType, "api", "http"), }, - expectErr: `invalid element at index 1 of list "parent_refs": invalid "ref" field: parent ref "catalog.v1alpha1.Service/default.local.default/api" for port "http" covered by wildcard port already`, + expectErr: `invalid element at index 1 of list "parent_refs": invalid "port" field: parent ref "catalog.v1alpha1.Service/default.local.default/api" for port "http" covered by wildcard port already`, }, "good single parent ref": { refs: []*pbmesh.ParentReference{ @@ -865,7 +996,7 @@ func getXRouteBackendRefTestCases() map[string]xRouteBackendRefTestcase { newBackendRef(catalog.ServiceType, "api", ""), newBackendRef(catalog.WorkloadType, "api", ""), }, - expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: reference must have type catalog.v1alpha1.Service`, + expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: invalid "type" field: reference must have type catalog.v1alpha1.Service`, }, "backend ref with section": { refs: []*pbmesh.BackendReference{ @@ -875,7 +1006,7 @@ func getXRouteBackendRefTestCases() map[string]xRouteBackendRefTestcase { Port: "http", }, }, - expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: invalid "section" field: section not supported for service backend refs`, + expectErr: `invalid element at index 0 of list "rules": invalid element at index 1 of list "backend_refs": invalid "backend_ref" field: invalid "ref" field: invalid "section" field: section cannot be set here`, }, "backend ref with datacenter": { refs: []*pbmesh.BackendReference{ @@ -958,8 +1089,15 @@ func getXRouteRetriesTestCases() map[string]xRouteRetriesTestcase { } func newRef(typ *pbresource.Type, name string) *pbresource.Reference { + return newRefWithTenancy(typ, nil, name) +} + +func newRefWithTenancy(typ *pbresource.Type, tenancy *pbresource.Tenancy, name string) *pbresource.Reference { + if tenancy == nil { + tenancy = resource.DefaultNamespacedTenancy() + } return resourcetest.Resource(typ, name). - WithTenancy(resource.DefaultNamespacedTenancy()). + WithTenancy(tenancy). Reference("") } @@ -971,8 +1109,31 @@ func newBackendRef(typ *pbresource.Type, name, port string) *pbmesh.BackendRefer } func newParentRef(typ *pbresource.Type, name, port string) *pbmesh.ParentReference { + return newParentRefWithTenancy(typ, nil, name, port) +} + +func newParentRefWithTenancy(typ *pbresource.Type, tenancy *pbresource.Tenancy, name, port string) *pbmesh.ParentReference { return &pbmesh.ParentReference{ - Ref: newRef(typ, name), + Ref: newRefWithTenancy(typ, tenancy, name), Port: port, } } + +func newTestTenancy(s string) *pbresource.Tenancy { + parts := strings.Split(s, ".") + switch len(parts) { + case 0: + return resource.DefaultClusteredTenancy() + case 1: + v := resource.DefaultPartitionedTenancy() + v.Partition = parts[0] + return v + case 2: + v := resource.DefaultNamespacedTenancy() + v.Partition = parts[0] + v.Namespace = parts[1] + return v + default: + return &pbresource.Tenancy{Partition: "BAD", Namespace: "BAD", PeerName: "BAD"} + } +} diff --git a/internal/mesh/internal/types/tcp_route.go b/internal/mesh/internal/types/tcp_route.go index 8549b1c33f37..bfd3d5235d59 100644 --- a/internal/mesh/internal/types/tcp_route.go +++ b/internal/mesh/internal/types/tcp_route.go @@ -29,14 +29,45 @@ var ( func RegisterTCPRoute(r resource.Registry) { r.Register(resource.Registration{ - Type: TCPRouteV1Alpha1Type, - Proto: &pbmesh.TCPRoute{}, - Scope: resource.ScopeNamespace, - // TODO(rb): normalize parent/backend ref tenancies in a Mutate hook + Type: TCPRouteV1Alpha1Type, + Proto: &pbmesh.TCPRoute{}, + Scope: resource.ScopeNamespace, + Mutate: MutateTCPRoute, Validate: ValidateTCPRoute, }) } +func MutateTCPRoute(res *pbresource.Resource) error { + var route pbmesh.TCPRoute + + if err := res.Data.UnmarshalTo(&route); err != nil { + return resource.NewErrDataParse(&route, err) + } + + changed := false + + if mutateParentRefs(res.Id.Tenancy, route.ParentRefs) { + changed = true + } + + for _, rule := range route.Rules { + for _, backend := range rule.BackendRefs { + if backend.BackendRef == nil || backend.BackendRef.Ref == nil { + continue + } + if mutateXRouteRef(res.Id.Tenancy, backend.BackendRef.Ref) { + changed = true + } + } + } + + if !changed { + return nil + } + + return res.Data.MarshalFrom(&route) +} + func ValidateTCPRoute(res *pbresource.Resource) error { var route pbmesh.TCPRoute @@ -67,14 +98,6 @@ func ValidateTCPRoute(res *pbresource.Resource) error { } if len(rule.BackendRefs) == 0 { - /* - BackendRefs (optional)ΒΆ - - BackendRefs defines API objects where matching requests should be - sent. If unspecified, the rule performs no forwarding. If - unspecified and no filters are specified that would result in a - response being sent, a 404 error code is returned. - */ merr = multierror.Append(merr, wrapRuleErr( resource.ErrInvalidField{ Name: "backend_refs", @@ -90,13 +113,15 @@ func ValidateTCPRoute(res *pbresource.Resource) error { Wrapped: err, }) } - for _, err := range validateBackendRef(hbref.BackendRef) { - merr = multierror.Append(merr, wrapBackendRefErr( - resource.ErrInvalidField{ - Name: "backend_ref", - Wrapped: err, - }, - )) + + wrapBackendRefFieldErr := func(err error) error { + return wrapBackendRefErr(resource.ErrInvalidField{ + Name: "backend_ref", + Wrapped: err, + }) + } + if err := validateBackendRef(hbref.BackendRef, wrapBackendRefFieldErr); err != nil { + merr = multierror.Append(merr, err) } } } diff --git a/internal/mesh/internal/types/tcp_route_test.go b/internal/mesh/internal/types/tcp_route_test.go index 6469ada83b50..1f667adde191 100644 --- a/internal/mesh/internal/types/tcp_route_test.go +++ b/internal/mesh/internal/types/tcp_route_test.go @@ -7,15 +7,100 @@ import ( "testing" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/internal/resource/resourcetest" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" "github.com/hashicorp/consul/proto/private/prototest" "github.com/hashicorp/consul/sdk/testutil" ) +func TestMutateTCPRoute(t *testing.T) { + type testcase struct { + routeTenancy *pbresource.Tenancy + route *pbmesh.TCPRoute + expect *pbmesh.TCPRoute + } + + cases := map[string]testcase{} + + // Add common parent refs test cases. + for name, parentTC := range getXRouteParentRefMutateTestCases() { + cases["parent-ref: "+name] = testcase{ + routeTenancy: parentTC.routeTenancy, + route: &pbmesh.TCPRoute{ + ParentRefs: parentTC.refs, + }, + expect: &pbmesh.TCPRoute{ + ParentRefs: parentTC.expect, + }, + } + } + // add common backend ref test cases. + for name, backendTC := range getXRouteBackendRefMutateTestCases() { + var ( + refs []*pbmesh.TCPBackendRef + expect []*pbmesh.TCPBackendRef + ) + for _, br := range backendTC.refs { + refs = append(refs, &pbmesh.TCPBackendRef{ + BackendRef: br, + }) + } + for _, br := range backendTC.expect { + expect = append(expect, &pbmesh.TCPBackendRef{ + BackendRef: br, + }) + } + cases["backend-ref: "+name] = testcase{ + routeTenancy: backendTC.routeTenancy, + route: &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(catalog.ServiceType, "web", ""), + }, + Rules: []*pbmesh.TCPRouteRule{ + {BackendRefs: refs}, + }, + }, + expect: &pbmesh.TCPRoute{ + ParentRefs: []*pbmesh.ParentReference{ + newParentRef(catalog.ServiceType, "web", ""), + }, + Rules: []*pbmesh.TCPRouteRule{ + {BackendRefs: expect}, + }, + }, + } + } + + run := func(t *testing.T, tc testcase) { + res := resourcetest.Resource(TCPRouteType, "api"). + WithTenancy(tc.routeTenancy). + WithData(t, tc.route). + Build() + + err := MutateTCPRoute(res) + require.NoError(t, err) + + got := resourcetest.MustDecode[*pbmesh.TCPRoute](t, res) + + if tc.expect == nil { + tc.expect = proto.Clone(tc.route).(*pbmesh.TCPRoute) + } + + prototest.AssertDeepEqual(t, tc.expect, got.Data) + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + run(t, tc) + }) + } +} + func TestValidateTCPRoute(t *testing.T) { type testcase struct { route *pbmesh.TCPRoute @@ -28,7 +113,15 @@ func TestValidateTCPRoute(t *testing.T) { WithData(t, tc.route). Build() - err := ValidateTCPRoute(res) + // Ensure things are properly mutated and updated in the inputs. + err := MutateTCPRoute(res) + require.NoError(t, err) + { + mutated := resourcetest.MustDecode[*pbmesh.TCPRoute](t, res) + tc.route = mutated.Data + } + + err = ValidateTCPRoute(res) // Verify that validate didn't actually change the object. got := resourcetest.MustDecode[*pbmesh.TCPRoute](t, res) diff --git a/internal/mesh/internal/types/upstreams.go b/internal/mesh/internal/types/upstreams.go index 1ef73e181e01..54baeceebff7 100644 --- a/internal/mesh/internal/types/upstreams.go +++ b/internal/mesh/internal/types/upstreams.go @@ -4,6 +4,10 @@ package types import ( + "github.com/hashicorp/go-multierror" + "google.golang.org/protobuf/proto" + + "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/resource" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" "github.com/hashicorp/consul/proto-public/pbresource" @@ -28,6 +32,86 @@ func RegisterUpstreams(r resource.Registry) { Type: UpstreamsV1Alpha1Type, Proto: &pbmesh.Upstreams{}, Scope: resource.ScopeNamespace, - Validate: nil, + Mutate: MutateUpstreams, + Validate: ValidateUpstreams, }) } + +func MutateUpstreams(res *pbresource.Resource) error { + var destinations pbmesh.Upstreams + + if err := res.Data.UnmarshalTo(&destinations); err != nil { + return resource.NewErrDataParse(&destinations, err) + } + + changed := false + + for _, dest := range destinations.Upstreams { + if dest.DestinationRef == nil { + continue // skip; let the validation hook error out instead + } + if dest.DestinationRef.Tenancy != nil && !isLocalPeer(dest.DestinationRef.Tenancy.PeerName) { + // TODO(peering/v2): remove this bypass when we know what to do with + // non-local peer references. + continue + } + orig := proto.Clone(dest.DestinationRef).(*pbresource.Reference) + resource.DefaultReferenceTenancy( + dest.DestinationRef, + res.Id.GetTenancy(), + resource.DefaultNamespacedTenancy(), // Services are all namespace scoped. + ) + + if !proto.Equal(orig, dest.DestinationRef) { + changed = true + } + } + + if !changed { + return nil + } + + return res.Data.MarshalFrom(&destinations) +} + +func isLocalPeer(p string) bool { + return p == "local" || p == "" +} + +func ValidateUpstreams(res *pbresource.Resource) error { + var destinations pbmesh.Upstreams + + if err := res.Data.UnmarshalTo(&destinations); err != nil { + return resource.NewErrDataParse(&destinations, err) + } + + var merr error + + for i, dest := range destinations.Upstreams { + wrapDestErr := func(err error) error { + return resource.ErrInvalidListElement{ + Name: "upstreams", + Index: i, + Wrapped: err, + } + } + + wrapRefErr := func(err error) error { + return wrapDestErr(resource.ErrInvalidField{ + Name: "destination_ref", + Wrapped: err, + }) + } + + if refErr := catalog.ValidateLocalServiceRefNoSection(dest.DestinationRef, wrapRefErr); refErr != nil { + merr = multierror.Append(merr, refErr) + } + + // TODO(v2): validate port name using catalog validator + // TODO(v2): validate ListenAddr + } + + // TODO(v2): validate workload selectors + + return merr +} diff --git a/internal/mesh/internal/types/upstreams_test.go b/internal/mesh/internal/types/upstreams_test.go new file mode 100644 index 000000000000..ca4b2ce9b4f1 --- /dev/null +++ b/internal/mesh/internal/types/upstreams_test.go @@ -0,0 +1,198 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/internal/catalog" + "github.com/hashicorp/consul/internal/resource" + "github.com/hashicorp/consul/internal/resource/resourcetest" + pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" + "github.com/hashicorp/consul/proto/private/prototest" + "github.com/hashicorp/consul/sdk/testutil" +) + +func TestMutateUpstreams(t *testing.T) { + type testcase struct { + tenancy *pbresource.Tenancy + data *pbmesh.Upstreams + expect *pbmesh.Upstreams + expectErr string + } + + run := func(t *testing.T, tc testcase) { + res := resourcetest.Resource(UpstreamsType, "api"). + WithTenancy(tc.tenancy). + WithData(t, tc.data). + Build() + + err := MutateUpstreams(res) + + got := resourcetest.MustDecode[*pbmesh.Upstreams](t, res) + + if tc.expectErr == "" { + require.NoError(t, err) + prototest.AssertDeepEqual(t, tc.expect, got.Data) + } else { + testutil.RequireErrorContains(t, err, tc.expectErr) + } + } + + cases := map[string]testcase{ + "empty-1": { + data: &pbmesh.Upstreams{}, + expect: &pbmesh.Upstreams{}, + }, + "invalid/nil dest ref": { + data: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: nil}, + }, + }, + expect: &pbmesh.Upstreams{ // untouched + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: nil}, + }, + }, + }, + "dest ref tenancy defaulting": { + tenancy: newTestTenancy("foo.bar"), + data: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy(""), "api")}, + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy(".zim"), "api")}, + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy("gir.zim"), "api")}, + }, + }, + expect: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy("foo.bar"), "api")}, + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy("foo.zim"), "api")}, + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy("gir.zim"), "api")}, + }, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + run(t, tc) + }) + } +} + +func TestValidateUpstreams(t *testing.T) { + type testcase struct { + data *pbmesh.Upstreams + skipMutate bool + expectErr string + } + + run := func(t *testing.T, tc testcase) { + res := resourcetest.Resource(UpstreamsType, "api"). + WithTenancy(resource.DefaultNamespacedTenancy()). + WithData(t, tc.data). + Build() + + if !tc.skipMutate { + require.NoError(t, MutateUpstreams(res)) + + // Verify that mutate didn't actually change the object. + got := resourcetest.MustDecode[*pbmesh.Upstreams](t, res) + prototest.AssertDeepEqual(t, tc.data, got.Data) + } + + err := ValidateUpstreams(res) + + // Verify that validate didn't actually change the object. + got := resourcetest.MustDecode[*pbmesh.Upstreams](t, res) + prototest.AssertDeepEqual(t, tc.data, got.Data) + + if tc.expectErr == "" { + require.NoError(t, err) + } else { + testutil.RequireErrorContains(t, err, tc.expectErr) + } + } + + cases := map[string]testcase{ + // emptiness + "empty": { + data: &pbmesh.Upstreams{}, + }, + "dest/nil ref": { + skipMutate: true, + data: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: nil}, + }, + }, + expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: missing required field`, + }, + "dest/bad type": { + skipMutate: true, + data: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: newRefWithTenancy(catalog.WorkloadType, nil, "api")}, + }, + }, + expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: invalid "type" field: reference must have type catalog.v1alpha1.Service`, + }, + "dest/nil tenancy": { + skipMutate: true, + data: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: &pbresource.Reference{Type: catalog.ServiceType, Name: "api"}}, + }, + }, + expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: invalid "tenancy" field: missing required field`, + }, + "dest/bad dest tenancy/partition": { + skipMutate: true, + data: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy(".bar"), "api")}, + }, + }, + expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: invalid "tenancy" field: invalid "partition" field: cannot be empty`, + }, + "dest/bad dest tenancy/namespace": { + skipMutate: true, + data: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy("foo"), "api")}, + }, + }, + expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: invalid "tenancy" field: invalid "namespace" field: cannot be empty`, + }, + "dest/bad dest tenancy/peer_name": { + skipMutate: true, + data: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: newRefWithTenancy(catalog.ServiceType, &pbresource.Tenancy{Partition: "foo", Namespace: "bar"}, "api")}, + }, + }, + expectErr: `invalid element at index 0 of list "upstreams": invalid "destination_ref" field: invalid "tenancy" field: invalid "peer_name" field: must be set to "local"`, + }, + "normal": { + data: &pbmesh.Upstreams{ + Upstreams: []*pbmesh.Upstream{ + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy("foo.bar"), "api")}, + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy("foo.zim"), "api")}, + {DestinationRef: newRefWithTenancy(catalog.ServiceType, newTestTenancy("gir.zim"), "api")}, + }, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + run(t, tc) + }) + } +} diff --git a/internal/mesh/internal/types/xroute.go b/internal/mesh/internal/types/xroute.go index b29f9a61064b..dec2290179a2 100644 --- a/internal/mesh/internal/types/xroute.go +++ b/internal/mesh/internal/types/xroute.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/resource" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v1alpha1" + "github.com/hashicorp/consul/proto-public/pbresource" ) type XRouteData interface { @@ -30,6 +31,33 @@ type portedRefKey struct { Port string } +func mutateParentRefs(xrouteTenancy *pbresource.Tenancy, parentRefs []*pbmesh.ParentReference) (changed bool) { + for _, parent := range parentRefs { + if parent.Ref == nil { + continue + } + changedThis := mutateXRouteRef(xrouteTenancy, parent.Ref) + if changedThis { + changed = true + } + } + return changed +} + +func mutateXRouteRef(xrouteTenancy *pbresource.Tenancy, ref *pbresource.Reference) (changed bool) { + if ref == nil { + return false + } + orig := proto.Clone(ref).(*pbresource.Reference) + resource.DefaultReferenceTenancy( + ref, + xrouteTenancy, + resource.DefaultNamespacedTenancy(), // All xRoutes are namespace scoped. + ) + + return !proto.Equal(orig, ref) +} + func validateParentRefs(parentRefs []*pbmesh.ParentReference) error { var merr error if len(parentRefs) == 0 { @@ -51,46 +79,17 @@ func validateParentRefs(parentRefs []*pbmesh.ParentReference) error { Wrapped: err, } } - if parent.Ref == nil { - merr = multierror.Append(merr, wrapErr( - resource.ErrInvalidField{ - Name: "ref", - Wrapped: resource.ErrMissing, - }, - )) - } else { - if !IsServiceType(parent.Ref.Type) { - merr = multierror.Append(merr, wrapErr( - resource.ErrInvalidField{ - Name: "ref", - Wrapped: resource.ErrInvalidReferenceType{ - AllowedType: catalog.ServiceType, - }, - }, - )) - } - if parent.Ref.Section != "" { - merr = multierror.Append(merr, wrapErr( - resource.ErrInvalidField{ - Name: "ref", - Wrapped: resource.ErrInvalidField{ - Name: "section", - Wrapped: errors.New("section not supported for service parent refs"), - }, - }, - )) - } - if parent.Ref.Name == "" { - merr = multierror.Append(merr, resource.ErrInvalidField{ - Name: "ref", - Wrapped: resource.ErrInvalidField{ - Name: "name", - Wrapped: resource.ErrMissing, - }, - }) - } + wrapRefErr := func(err error) error { + return wrapErr(resource.ErrInvalidField{ + Name: "ref", + Wrapped: err, + }) + } + if err := catalog.ValidateLocalServiceRefNoSection(parent.Ref, wrapRefErr); err != nil { + merr = multierror.Append(merr, err) + } else { prk := portedRefKey{ Key: resource.NewReferenceKey(parent.Ref), Port: parent.Port, @@ -104,7 +103,7 @@ func validateParentRefs(parentRefs []*pbmesh.ParentReference) error { if portExist { // check for duplicate wild merr = multierror.Append(merr, wrapErr( resource.ErrInvalidField{ - Name: "ref", + Name: "port", Wrapped: fmt.Errorf( "parent ref %q for wildcard port exists twice", resource.ReferenceToString(parent.Ref), @@ -114,7 +113,7 @@ func validateParentRefs(parentRefs []*pbmesh.ParentReference) error { } else if exactExists { // check for existing exact merr = multierror.Append(merr, wrapErr( resource.ErrInvalidField{ - Name: "ref", + Name: "port", Wrapped: fmt.Errorf( "parent ref %q for ports %v covered by wildcard port already", resource.ReferenceToString(parent.Ref), @@ -134,7 +133,7 @@ func validateParentRefs(parentRefs []*pbmesh.ParentReference) error { if portExist { // check for duplicate exact merr = multierror.Append(merr, wrapErr( resource.ErrInvalidField{ - Name: "ref", + Name: "port", Wrapped: fmt.Errorf( "parent ref %q for port %q exists twice", resource.ReferenceToString(parent.Ref), @@ -145,7 +144,7 @@ func validateParentRefs(parentRefs []*pbmesh.ParentReference) error { } else if wildExist { // check for existing wild merr = multierror.Append(merr, wrapErr( resource.ErrInvalidField{ - Name: "ref", + Name: "port", Wrapped: fmt.Errorf( "parent ref %q for port %q covered by wildcard port already", resource.ReferenceToString(parent.Ref), @@ -164,55 +163,30 @@ func validateParentRefs(parentRefs []*pbmesh.ParentReference) error { return merr } -func validateBackendRef(backendRef *pbmesh.BackendReference) []error { - var errs []error +func validateBackendRef(backendRef *pbmesh.BackendReference, wrapErr func(error) error) error { if backendRef == nil { - errs = append(errs, resource.ErrMissing) + return wrapErr(resource.ErrMissing) + } + + var merr error - } else if backendRef.Ref == nil { - errs = append(errs, resource.ErrInvalidField{ + wrapRefErr := func(err error) error { + return wrapErr(resource.ErrInvalidField{ Name: "ref", - Wrapped: resource.ErrMissing, + Wrapped: err, }) - - } else { - if !IsServiceType(backendRef.Ref.Type) { - errs = append(errs, resource.ErrInvalidField{ - Name: "ref", - Wrapped: resource.ErrInvalidReferenceType{ - AllowedType: catalog.ServiceType, - }, - }) - } - - if backendRef.Ref.Name == "" { - errs = append(errs, resource.ErrInvalidField{ - Name: "ref", - Wrapped: resource.ErrInvalidField{ - Name: "name", - Wrapped: resource.ErrMissing, - }, - }) - } - - if backendRef.Ref.Section != "" { - errs = append(errs, resource.ErrInvalidField{ - Name: "ref", - Wrapped: resource.ErrInvalidField{ - Name: "section", - Wrapped: errors.New("section not supported for service backend refs"), - }, - }) - } - - if backendRef.Datacenter != "" { - errs = append(errs, resource.ErrInvalidField{ - Name: "datacenter", - Wrapped: errors.New("datacenter is not yet supported on backend refs"), - }) - } } - return errs + if err := catalog.ValidateLocalServiceRefNoSection(backendRef.Ref, wrapRefErr); err != nil { + merr = multierror.Append(merr, err) + } + if backendRef.Datacenter != "" { + merr = multierror.Append(merr, wrapErr(resource.ErrInvalidField{ + Name: "datacenter", + Wrapped: errors.New("datacenter is not yet supported on backend refs"), + })) + } + + return merr } func validateHeaderMatchType(typ pbmesh.HeaderMatchType) error { From 62796a14544edc918ac1503bc241f22c46060d46 Mon Sep 17 00:00:00 2001 From: Semir Patel Date: Mon, 18 Sep 2023 17:04:29 -0500 Subject: [PATCH 26/28] resource: mutate and validate before acls on write (#18868) --- .../grpc-external/services/resource/write.go | 34 +++++++++---------- internal/resource/registry.go | 8 +++-- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/agent/grpc-external/services/resource/write.go b/agent/grpc-external/services/resource/write.go index 2511e2f5da22..7110122313fa 100644 --- a/agent/grpc-external/services/resource/write.go +++ b/agent/grpc-external/services/resource/write.go @@ -49,15 +49,6 @@ func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbre } v1EntMetaToV2Tenancy(reg, v1EntMeta, req.Resource.Id.Tenancy) - // ACL check comes before tenancy existence checks to not leak tenancy "existence". - err = reg.ACLs.Write(authz, authzContext, req.Resource) - switch { - case acl.IsErrPermissionDenied(err): - return nil, status.Error(codes.PermissionDenied, err.Error()) - case err != nil: - return nil, status.Errorf(codes.Internal, "failed write acl: %v", err) - } - // Check the user sent the correct type of data. if req.Resource.Data != nil && !req.Resource.Data.MessageIs(reg.Proto) { got := strings.TrimPrefix(req.Resource.Data.TypeUrl, "type.googleapis.com/") @@ -70,6 +61,23 @@ func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbre ) } + if err = reg.Mutate(req.Resource); err != nil { + return nil, status.Errorf(codes.Internal, "failed mutate hook: %v", err.Error()) + } + + if err = reg.Validate(req.Resource); err != nil { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + + // ACL check comes before tenancy existence checks to not leak tenancy "existence". + err = reg.ACLs.Write(authz, authzContext, req.Resource) + switch { + case acl.IsErrPermissionDenied(err): + return nil, status.Error(codes.PermissionDenied, err.Error()) + case err != nil: + return nil, status.Errorf(codes.Internal, "failed write acl: %v", err) + } + // Check V1 tenancy exists for the V2 resource if err = v1TenancyExists(reg, s.TenancyBridge, req.Resource.Id.Tenancy, codes.InvalidArgument); err != nil { return nil, err @@ -80,14 +88,6 @@ func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbre return nil, err } - if err = reg.Mutate(req.Resource); err != nil { - return nil, status.Errorf(codes.Internal, "failed mutate hook: %v", err.Error()) - } - - if err = reg.Validate(req.Resource); err != nil { - return nil, status.Error(codes.InvalidArgument, err.Error()) - } - // At the storage backend layer, all writes are CAS operations. // // This makes it possible to *safely* do things like keeping the Uid stable diff --git a/internal/resource/registry.go b/internal/resource/registry.go index db3e4d3d0c32..b8afa6840501 100644 --- a/internal/resource/registry.go +++ b/internal/resource/registry.go @@ -49,14 +49,18 @@ type Registration struct { Proto proto.Message // ACLs are hooks called to perform authorization on RPCs. + // The hooks can assume that Validate has been called. ACLs *ACLHooks // Validate is called to structurally validate the resource (e.g. - // check for required fields). + // check for required fields). Validate can assume that Mutate + // has been called. Validate func(*pbresource.Resource) error // Mutate is called to fill out any autogenerated fields (e.g. UUIDs) or - // apply defaults before validation. + // apply defaults before validation. Mutate can assume that + // Resource.ID is populated and has non-empty tenancy fields. This does + // not mean those tenancy fields actually exist. Mutate func(*pbresource.Resource) error // Scope describes the tenancy scope of a resource. From 91e6c3a82f195677483c2880a9b9182689c05349 Mon Sep 17 00:00:00 2001 From: "Chris S. Kim" Date: Mon, 18 Sep 2023 18:56:23 -0400 Subject: [PATCH 27/28] Remove flaky test assertions (#18870) --- agent/agent_endpoint_test.go | 5 +---- agent/consul/leader_connect_test.go | 17 ++++++----------- agent/health_endpoint_test.go | 1 + command/acl/token/update/token_update_test.go | 5 +---- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/agent/agent_endpoint_test.go b/agent/agent_endpoint_test.go index 48cee77def81..c56957558988 100644 --- a/agent/agent_endpoint_test.go +++ b/agent/agent_endpoint_test.go @@ -737,9 +737,6 @@ func TestAgent_Service(t *testing.T) { if tt.wantWait != 0 { assert.True(t, elapsed >= tt.wantWait, "should have waited at least %s, "+ "took %s", tt.wantWait, elapsed) - } else { - assert.True(t, elapsed < 10*time.Millisecond, "should not have waited, "+ - "took %s", elapsed) } if tt.wantResp != nil { @@ -4448,7 +4445,7 @@ func testAgent_RegisterServiceDeregisterService_Sidecar(t *testing.T, extraHCL s } `, enableACL: true, - policies: ``, // No policy means no valid token + policies: ``, // No policies means no valid token wantNS: nil, wantErr: "Permission denied", }, diff --git a/agent/consul/leader_connect_test.go b/agent/consul/leader_connect_test.go index d9150c3a4545..dbee808de99e 100644 --- a/agent/consul/leader_connect_test.go +++ b/agent/consul/leader_connect_test.go @@ -52,7 +52,9 @@ func TestConnectCA_ConfigurationSet_ChangeKeyConfig_Primary(t *testing.T) { src := src dst := dst t.Run(fmt.Sprintf("%s-%d to %s-%d", src.keyType, src.keyBits, dst.keyType, dst.keyBits), func(t *testing.T) { - t.Parallel() + // TODO(flaky): making this test parallel seems to create performance problems + // in CI. Until we spend time optimizing this test, it's best to take the runtime hit. + // t.Parallel() providerState := map[string]string{"foo": "dc1-value"} @@ -60,14 +62,13 @@ func TestConnectCA_ConfigurationSet_ChangeKeyConfig_Primary(t *testing.T) { _, srv := testServerWithConfig(t, func(c *Config) { c.Datacenter = "dc1" c.PrimaryDatacenter = "dc1" - c.Build = "1.6.0" c.CAConfig.Config["PrivateKeyType"] = src.keyType c.CAConfig.Config["PrivateKeyBits"] = src.keyBits c.CAConfig.Config["test_state"] = providerState }) codec := rpcClient(t, srv) - waitForLeaderEstablishment(t, srv) + testrpc.WaitForLeader(t, srv.RPC, "dc1") testrpc.WaitForActiveCARoot(t, srv.RPC, "dc1", nil) var ( @@ -557,23 +558,17 @@ func TestConnectCA_ConfigurationSet_RootRotation_Secondary(t *testing.T) { t.Parallel() - dir1, s1 := testServerWithConfig(t, func(c *Config) { - c.Build = "1.6.0" + _, s1 := testServerWithConfig(t, func(c *Config) { c.PrimaryDatacenter = "dc1" }) - defer os.RemoveAll(dir1) - defer s1.Shutdown() testrpc.WaitForLeader(t, s1.RPC, "dc1") // dc2 as a secondary DC - dir2, s2 := testServerWithConfig(t, func(c *Config) { + _, s2 := testServerWithConfig(t, func(c *Config) { c.Datacenter = "dc2" c.PrimaryDatacenter = "dc1" - c.Build = "1.6.0" }) - defer os.RemoveAll(dir2) - defer s2.Shutdown() // Create the WAN link joinWAN(t, s2, s1) diff --git a/agent/health_endpoint_test.go b/agent/health_endpoint_test.go index 021a269b8a09..2be4f8b1abd3 100644 --- a/agent/health_endpoint_test.go +++ b/agent/health_endpoint_test.go @@ -1201,6 +1201,7 @@ func TestHealthServiceNodes_NodeMetaFilter(t *testing.T) { require.NoError(t, err) assertIndex(t, resp) + assert.Equal(t, "MISS", resp.Header().Get("X-Cache")) // Should be a non-nil empty list for checks nodes := obj.(structs.CheckServiceNodes) diff --git a/command/acl/token/update/token_update_test.go b/command/acl/token/update/token_update_test.go index 7867b0d87c62..91f630e8d2f2 100644 --- a/command/acl/token/update/token_update_test.go +++ b/command/acl/token/update/token_update_test.go @@ -254,7 +254,7 @@ func TestTokenUpdateCommandWithAppend(t *testing.T) { ) require.NoError(t, err) - //secondary policy + // secondary policy secondPolicy, _, policyErr := client.ACL().PolicyCreate( &api.ACLPolicy{Name: "secondary-policy"}, &api.WriteOptions{Token: "root"}, @@ -330,8 +330,6 @@ func TestTokenUpdateCommandWithAppend(t *testing.T) { }) require.Len(t, responseToken.NodeIdentities, 2) - require.Equal(t, "third", responseToken.NodeIdentities[1].NodeName) - require.Equal(t, "node", responseToken.NodeIdentities[1].Datacenter) }) // update with append-service-identity @@ -354,7 +352,6 @@ func TestTokenUpdateCommandWithAppend(t *testing.T) { }) require.Len(t, responseToken.ServiceIdentities, 2) - require.Equal(t, "web", responseToken.ServiceIdentities[1].ServiceName) }) } From 212793a4ee65e2a4f0fef68e5fa9b2c4ef8e037b Mon Sep 17 00:00:00 2001 From: Iryna Shustava Date: Mon, 18 Sep 2023 18:26:13 -0600 Subject: [PATCH 28/28] mesh: only build tproxy outbound listener once per destination (#18836) Previously, when using implicit upstreams, we'd build outbound listener per destination instead of one for all destinations. This will result in port conflicts when trying to send this config to envoy. This PR also makes sure that leaf and root references are always added (before we would only add it if there are inbound non-mesh ports). Also, black-hole traffic when there are no inbound ports other than mesh --- agent/xdsv2/cluster_resources.go | 11 +- envoyextensions/xdscommon/xdscommon.go | 4 + .../sidecarproxy/builder/builder.go | 11 + .../builder/destination_builder.go | 12 +- .../sidecarproxy/builder/local_app.go | 52 +++- .../builder/local_app_multiport_test.go | 15 +- ...it-and-explicit-destinations-tproxy.golden | 48 ++-- .../destination/l4-multi-destination.golden | 12 + ...ltiple-implicit-destinations-tproxy.golden | 63 +---- ...le-destination-ip-port-bind-address.golden | 12 + ...estination-unix-socket-bind-address.golden | 12 + ...-single-implicit-destination-tproxy.golden | 12 + .../mixed-multi-destination.golden | 12 + ...ltiple-implicit-destinations-tproxy.golden | 261 +----------------- ...-single-implicit-destination-tproxy.golden | 56 +--- ...tion-with-multiple-workloads-tproxy.golden | 56 +--- ...ort-l4-workload-with-only-mesh-port.golden | 51 ++++ 17 files changed, 276 insertions(+), 424 deletions(-) create mode 100644 internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-workload-with-only-mesh-port.golden diff --git a/agent/xdsv2/cluster_resources.go b/agent/xdsv2/cluster_resources.go index 99209d0c343c..b0a79fd0aa13 100644 --- a/agent/xdsv2/cluster_resources.go +++ b/agent/xdsv2/cluster_resources.go @@ -154,14 +154,15 @@ func (pr *ProxyResources) makeEnvoyDynamicCluster(name string, protocol string, } func (pr *ProxyResources) makeEnvoyStaticCluster(name string, protocol string, static *pbproxystate.StaticEndpointGroup) (*envoy_cluster_v3.Cluster, error) { - endpointList, ok := pr.proxyState.Endpoints[name] - if !ok || endpointList == nil { - return nil, fmt.Errorf("static cluster %q is missing endpoints", name) - } cluster := &envoy_cluster_v3.Cluster{ Name: name, ClusterDiscoveryType: &envoy_cluster_v3.Cluster_Type{Type: envoy_cluster_v3.Cluster_STATIC}, - LoadAssignment: makeEnvoyClusterLoadAssignment(name, endpointList.Endpoints), + } + + // todo (ishustava/v2): we need to be able to handle the case when empty endpoints are allowed on a cluster. + endpointList, ok := pr.proxyState.Endpoints[name] + if ok { + cluster.LoadAssignment = makeEnvoyClusterLoadAssignment(name, endpointList.Endpoints) } err := addHttpProtocolOptions(protocol, cluster) if err != nil { diff --git a/envoyextensions/xdscommon/xdscommon.go b/envoyextensions/xdscommon/xdscommon.go index efc2c5a87dd5..ae40f070210f 100644 --- a/envoyextensions/xdscommon/xdscommon.go +++ b/envoyextensions/xdscommon/xdscommon.go @@ -54,6 +54,10 @@ const ( SecretType = apiTypePrefix + "envoy.extensions.transport_sockets.tls.v3.Secret" FailoverClusterNamePrefix = "failover-target~" + + // BlackHoleClusterName is the cluster we use for black-holing traffic for cases when a workload + // has no inbound ports to route to. + BlackHoleClusterName = "black-hole-cluster" ) type IndexedResources struct { diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/builder.go b/internal/mesh/internal/controllers/sidecarproxy/builder/builder.go index a76af3b653b3..0c0cd0661d17 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/builder.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/builder.go @@ -45,6 +45,17 @@ func New( } func (b *Builder) Build() *pbmesh.ProxyStateTemplate { + workloadIdentity := b.proxyStateTemplate.ProxyState.Identity.Name + b.proxyStateTemplate.RequiredLeafCertificates[workloadIdentity] = &pbproxystate.LeafCertificateRef{ + Name: workloadIdentity, + Namespace: b.id.Tenancy.Namespace, + Partition: b.id.Tenancy.Partition, + } + + b.proxyStateTemplate.RequiredTrustBundles[b.id.Tenancy.PeerName] = &pbproxystate.TrustBundleRef{ + Peer: b.id.Tenancy.PeerName, + } + return b.proxyStateTemplate } diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/destination_builder.go b/internal/mesh/internal/controllers/sidecarproxy/builder/destination_builder.go index f0b02031d9a0..147d6a7b5452 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/destination_builder.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/destination_builder.go @@ -25,8 +25,7 @@ import ( // and adds them to the proxyState. func (b *Builder) BuildDestinations(destinations []*intermediate.Destination) *Builder { var lb *ListenerBuilder - if b.proxyCfg.GetDynamicConfig() != nil && - b.proxyCfg.DynamicConfig.Mode == pbmesh.ProxyMode_PROXY_MODE_TRANSPARENT { + if b.proxyCfg.IsTransparentProxy() { lb = b.addTransparentProxyOutboundListener(b.proxyCfg.DynamicConfig.TransparentProxy.OutboundListenerPort) } @@ -34,6 +33,10 @@ func (b *Builder) BuildDestinations(destinations []*intermediate.Destination) *B b.buildDestination(lb, destination) } + if b.proxyCfg.IsTransparentProxy() { + lb.buildListener() + } + return b } @@ -248,7 +251,10 @@ func (b *Builder) buildDestination( } } - lb.buildListener() + // Build outbound listener if the destination is explicit. + if destination.Explicit != nil { + lb.buildListener() + } if needsNullRouteCluster { b.addNullRouteCluster() diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go index bfb3f0b066c0..61ccccfbf7ba 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app.go @@ -25,10 +25,12 @@ func (b *Builder) BuildLocalApp(workload *pbcatalog.Workload, ctp *pbauth.Comput // Note that the order of ports is non-deterministic here but the xds generation // code should make sure to send it in the same order to Envoy to avoid unnecessary // updates. + foundInboundNonMeshPorts := false for portName, port := range workload.Ports { clusterName := fmt.Sprintf("%s:%s", xdscommon.LocalAppClusterName, portName) if port.Protocol != pbcatalog.Protocol_PROTOCOL_MESH { + foundInboundNonMeshPorts = true lb.addInboundRouter(clusterName, port, portName, trafficPermissions[portName]). addInboundTLS() @@ -37,6 +39,12 @@ func (b *Builder) BuildLocalApp(workload *pbcatalog.Workload, ctp *pbauth.Comput } } + // If there are no inbound ports other than the mesh port, we black-hole all inbound traffic. + if !foundInboundNonMeshPorts { + lb.addBlackHoleRouter() + b.addBlackHoleCluster() + } + return b } @@ -265,6 +273,28 @@ func (l *ListenerBuilder) addInboundRouter(clusterName string, port *pbcatalog.W return l } +func (l *ListenerBuilder) addBlackHoleRouter() *ListenerBuilder { + if l.listener == nil { + return l + } + + r := &pbproxystate.Router{ + Destination: &pbproxystate.Router_L4{ + L4: &pbproxystate.L4Destination{ + Destination: &pbproxystate.L4Destination_Cluster{ + Cluster: &pbproxystate.DestinationCluster{ + Name: xdscommon.BlackHoleClusterName, + }, + }, + StatPrefix: l.listener.Name, + }, + }, + } + l.listener.Routers = append(l.listener.Routers, r) + + return l +} + func getAlpnProtocolFromPortName(portName string) string { return fmt.Sprintf("consul~%s", portName) } @@ -283,6 +313,19 @@ func (b *Builder) addLocalAppCluster(clusterName string) *Builder { return b } +func (b *Builder) addBlackHoleCluster() *Builder { + b.proxyStateTemplate.ProxyState.Clusters[xdscommon.BlackHoleClusterName] = &pbproxystate.Cluster{ + Group: &pbproxystate.Cluster_EndpointGroup{ + EndpointGroup: &pbproxystate.EndpointGroup{ + Group: &pbproxystate.EndpointGroup_Static{ + Static: &pbproxystate.StaticEndpointGroup{}, + }, + }, + }, + } + return b +} + func (b *Builder) addLocalAppStaticEndpoints(clusterName string, port *pbcatalog.WorkloadPort) *Builder { // We're adding endpoints statically as opposed to creating an endpoint ref // because this endpoint is less likely to change as we're not tracking the health. @@ -319,15 +362,6 @@ func (l *ListenerBuilder) addInboundTLS() *ListenerBuilder { }, }, } - l.builder.proxyStateTemplate.RequiredLeafCertificates[workloadIdentity] = &pbproxystate.LeafCertificateRef{ - Name: workloadIdentity, - Namespace: l.builder.id.Tenancy.Namespace, - Partition: l.builder.id.Tenancy.Partition, - } - - l.builder.proxyStateTemplate.RequiredTrustBundles[l.builder.id.Tenancy.PeerName] = &pbproxystate.TrustBundleRef{ - Peer: l.builder.id.Tenancy.PeerName, - } for i := range l.listener.Routers { l.listener.Routers[i].InboundTls = inboundTLS diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go index 816e01236a2d..96b795109f53 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/local_app_multiport_test.go @@ -4,10 +4,11 @@ package builder import ( - "github.com/hashicorp/consul/internal/testing/golden" "sort" "testing" + "github.com/hashicorp/consul/internal/testing/golden" + "github.com/stretchr/testify/require" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v1alpha1" @@ -71,6 +72,18 @@ func TestBuildLocalApp_Multiport(t *testing.T) { }, }, }, + "source/multiport-l4-workload-with-only-mesh-port": { + workload: &pbcatalog.Workload{ + Addresses: []*pbcatalog.WorkloadAddress{ + { + Host: "10.0.0.1", + }, + }, + Ports: map[string]*pbcatalog.WorkloadPort{ + "mesh": {Port: 20000, Protocol: pbcatalog.Protocol_PROTOCOL_MESH}, + }, + }, + }, } for name, c := range cases { diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-implicit-and-explicit-destinations-tproxy.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-implicit-and-explicit-destinations-tproxy.golden index f18e8bcace03..aee378f1902f 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-implicit-and-explicit-destinations-tproxy.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-implicit-and-explicit-destinations-tproxy.golden @@ -61,6 +61,24 @@ } }, "listeners": [ + { + "direction": "DIRECTION_OUTBOUND", + "hostPort": { + "host": "1.1.1.1", + "port": 1234 + }, + "name": "api-1:tcp:1.1.1.1:1234", + "routers": [ + { + "l4": { + "cluster": { + "name": "tcp.api-1.default.dc1.internal.foo.consul" + }, + "statPrefix": "upstream.tcp.api-1.default.default.dc1" + } + } + ] + }, { "capabilities": [ "CAPABILITY_TRANSPARENT" @@ -94,24 +112,6 @@ } } ] - }, - { - "direction": "DIRECTION_OUTBOUND", - "hostPort": { - "host": "1.1.1.1", - "port": 1234 - }, - "name": "api-1:tcp:1.1.1.1:1234", - "routers": [ - { - "l4": { - "cluster": { - "name": "tcp.api-1.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-1.default.default.dc1" - } - } - ] } ] }, @@ -148,5 +148,17 @@ }, "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-multi-destination.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-multi-destination.golden index 0ed79095ea56..1c393ec7dc0f 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-multi-destination.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-multi-destination.golden @@ -155,5 +155,17 @@ }, "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-multiple-implicit-destinations-tproxy.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-multiple-implicit-destinations-tproxy.golden index de512dd1e585..d23c1ff1c74f 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-multiple-implicit-destinations-tproxy.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-multiple-implicit-destinations-tproxy.golden @@ -61,57 +61,6 @@ } }, "listeners": [ - { - "capabilities": [ - "CAPABILITY_TRANSPARENT" - ], - "direction": "DIRECTION_OUTBOUND", - "hostPort": { - "host": "127.0.0.1", - "port": 15001 - }, - "name": "outbound_listener", - "routers": [ - { - "l4": { - "cluster": { - "name": "tcp.api-1.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-1.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - }, - { - "l4": { - "cluster": { - "name": "tcp.api-2.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-2.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "2.2.2.2", - "prefixLen": 32 - }, - { - "addressPrefix": "3.3.3.3", - "prefixLen": 32 - } - ] - } - } - ] - }, { "capabilities": [ "CAPABILITY_TRANSPARENT" @@ -198,5 +147,17 @@ }, "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-destination-ip-port-bind-address.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-destination-ip-port-bind-address.golden index 09555899916c..3ac00f37a719 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-destination-ip-port-bind-address.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-destination-ip-port-bind-address.golden @@ -120,5 +120,17 @@ "tcp.api-2.default.dc1.internal.foo.consul": { "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-destination-unix-socket-bind-address.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-destination-unix-socket-bind-address.golden index 8e1309690224..69e075a3493e 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-destination-unix-socket-bind-address.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-destination-unix-socket-bind-address.golden @@ -73,5 +73,17 @@ }, "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-implicit-destination-tproxy.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-implicit-destination-tproxy.golden index 549211a135d5..8941ab072835 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-implicit-destination-tproxy.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/l4-single-implicit-destination-tproxy.golden @@ -85,5 +85,17 @@ }, "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/mixed-multi-destination.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/mixed-multi-destination.golden index a273cd4bc335..c0394a25bcff 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/mixed-multi-destination.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/mixed-multi-destination.golden @@ -284,5 +284,17 @@ }, "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-multiple-implicit-destinations-tproxy.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-multiple-implicit-destinations-tproxy.golden index d7e81fd8b0b3..f7f3c9ffa7ee 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-multiple-implicit-destinations-tproxy.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-multiple-implicit-destinations-tproxy.golden @@ -111,255 +111,6 @@ } }, "listeners": [ - { - "capabilities": [ - "CAPABILITY_TRANSPARENT" - ], - "direction": "DIRECTION_OUTBOUND", - "hostPort": { - "host": "127.0.0.1", - "port": 15001 - }, - "name": "outbound_listener", - "routers": [ - { - "l4": { - "cluster": { - "name": "tcp.api-app.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-app.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - }, - { - "l4": { - "cluster": { - "name": "tcp.api-app2.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-app2.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "2.2.2.2", - "prefixLen": 32 - }, - { - "addressPrefix": "3.3.3.3", - "prefixLen": 32 - } - ] - } - }, - { - "l7": { - "name": "outbound_listener", - "statPrefix": "upstream." - }, - "match": { - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - }, - { - "l7": { - "name": "outbound_listener", - "statPrefix": "upstream." - }, - "match": { - "prefixRanges": [ - { - "addressPrefix": "2.2.2.2", - "prefixLen": 32 - }, - { - "addressPrefix": "3.3.3.3", - "prefixLen": 32 - } - ] - } - } - ] - }, - { - "capabilities": [ - "CAPABILITY_TRANSPARENT" - ], - "direction": "DIRECTION_OUTBOUND", - "hostPort": { - "host": "127.0.0.1", - "port": 15001 - }, - "name": "outbound_listener", - "routers": [ - { - "l4": { - "cluster": { - "name": "tcp.api-app.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-app.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - }, - { - "l4": { - "cluster": { - "name": "tcp.api-app2.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-app2.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "2.2.2.2", - "prefixLen": 32 - }, - { - "addressPrefix": "3.3.3.3", - "prefixLen": 32 - } - ] - } - }, - { - "l7": { - "name": "outbound_listener", - "statPrefix": "upstream." - }, - "match": { - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - }, - { - "l7": { - "name": "outbound_listener", - "statPrefix": "upstream." - }, - "match": { - "prefixRanges": [ - { - "addressPrefix": "2.2.2.2", - "prefixLen": 32 - }, - { - "addressPrefix": "3.3.3.3", - "prefixLen": 32 - } - ] - } - } - ] - }, - { - "capabilities": [ - "CAPABILITY_TRANSPARENT" - ], - "direction": "DIRECTION_OUTBOUND", - "hostPort": { - "host": "127.0.0.1", - "port": 15001 - }, - "name": "outbound_listener", - "routers": [ - { - "l4": { - "cluster": { - "name": "tcp.api-app.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-app.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - }, - { - "l4": { - "cluster": { - "name": "tcp.api-app2.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-app2.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "2.2.2.2", - "prefixLen": 32 - }, - { - "addressPrefix": "3.3.3.3", - "prefixLen": 32 - } - ] - } - }, - { - "l7": { - "name": "outbound_listener", - "statPrefix": "upstream." - }, - "match": { - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - }, - { - "l7": { - "name": "outbound_listener", - "statPrefix": "upstream." - }, - "match": { - "prefixRanges": [ - { - "addressPrefix": "2.2.2.2", - "prefixLen": 32 - }, - { - "addressPrefix": "3.3.3.3", - "prefixLen": 32 - } - ] - } - } - ] - }, { "capabilities": [ "CAPABILITY_TRANSPARENT" @@ -533,5 +284,17 @@ }, "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-single-implicit-destination-tproxy.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-single-implicit-destination-tproxy.golden index 10a41c437007..61ffc42206ea 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-single-implicit-destination-tproxy.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-single-implicit-destination-tproxy.golden @@ -61,50 +61,6 @@ } }, "listeners": [ - { - "capabilities": [ - "CAPABILITY_TRANSPARENT" - ], - "direction": "DIRECTION_OUTBOUND", - "hostPort": { - "host": "127.0.0.1", - "port": 15001 - }, - "name": "outbound_listener", - "routers": [ - { - "l4": { - "cluster": { - "name": "tcp.api-app.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-app.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - }, - { - "l7": { - "name": "outbound_listener", - "statPrefix": "upstream." - }, - "match": { - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - } - ] - }, { "capabilities": [ "CAPABILITY_TRANSPARENT" @@ -207,5 +163,17 @@ }, "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-single-implicit-destination-with-multiple-workloads-tproxy.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-single-implicit-destination-with-multiple-workloads-tproxy.golden index 10a41c437007..61ffc42206ea 100644 --- a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-single-implicit-destination-with-multiple-workloads-tproxy.golden +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/destination/multiport-l4-single-implicit-destination-with-multiple-workloads-tproxy.golden @@ -61,50 +61,6 @@ } }, "listeners": [ - { - "capabilities": [ - "CAPABILITY_TRANSPARENT" - ], - "direction": "DIRECTION_OUTBOUND", - "hostPort": { - "host": "127.0.0.1", - "port": 15001 - }, - "name": "outbound_listener", - "routers": [ - { - "l4": { - "cluster": { - "name": "tcp.api-app.default.dc1.internal.foo.consul" - }, - "statPrefix": "upstream.tcp.api-app.default.default.dc1" - }, - "match": { - "destinationPort": 8080, - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - }, - { - "l7": { - "name": "outbound_listener", - "statPrefix": "upstream." - }, - "match": { - "prefixRanges": [ - { - "addressPrefix": "1.1.1.1", - "prefixLen": 32 - } - ] - } - } - ] - }, { "capabilities": [ "CAPABILITY_TRANSPARENT" @@ -207,5 +163,17 @@ }, "port": "mesh" } + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } } } \ No newline at end of file diff --git a/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-workload-with-only-mesh-port.golden b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-workload-with-only-mesh-port.golden new file mode 100644 index 000000000000..da29255b87ab --- /dev/null +++ b/internal/mesh/internal/controllers/sidecarproxy/builder/testdata/source/multiport-l4-workload-with-only-mesh-port.golden @@ -0,0 +1,51 @@ +{ + "proxyState": { + "clusters": { + "black-hole-cluster": { + "endpointGroup": { + "static": {} + } + } + }, + "identity": { + "name": "test-identity", + "tenancy": { + "namespace": "default", + "partition": "default", + "peerName": "local" + } + }, + "listeners": [ + { + "direction": "DIRECTION_INBOUND", + "hostPort": { + "host": "10.0.0.1", + "port": 20000 + }, + "name": "public_listener", + "routers": [ + { + "l4": { + "cluster": { + "name": "black-hole-cluster" + }, + "statPrefix": "public_listener" + } + } + ] + } + ] + }, + "requiredLeafCertificates": { + "test-identity": { + "name": "test-identity", + "namespace": "default", + "partition": "default" + } + }, + "requiredTrustBundles": { + "local": { + "peer": "local" + } + } +} \ No newline at end of file