open-consul/agent/structs/config_entry_discoverychain...

2832 lines
72 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package structs
import (
"bytes"
"fmt"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2021-08-20 22:11:01 +00:00
"github.com/hashicorp/consul/acl"
)
func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
// This test tests both of these because they are related functions.
2021-08-20 22:11:01 +00:00
newAuthz := func(t *testing.T, src string) acl.Authorizer {
policy, err := acl.NewPolicyFromSource(src, nil, nil)
2021-08-20 22:11:01 +00:00
require.NoError(t, err)
authorizer, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(t, err)
return authorizer
}
newServiceACL := func(t *testing.T, canRead, canWrite []string) acl.Authorizer {
var buf bytes.Buffer
for _, s := range canRead {
buf.WriteString(fmt.Sprintf("service %q { policy = %q }\n", s, "read"))
}
for _, s := range canWrite {
buf.WriteString(fmt.Sprintf("service %q { policy = %q }\n", s, "write"))
}
policy, err := acl.NewPolicyFromSource(buf.String(), nil, nil)
require.NoError(t, err)
authorizer, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
require.NoError(t, err)
return authorizer
}
2021-08-20 22:11:01 +00:00
newServiceAndOperatorACL := func(t *testing.T, service, operator string) acl.Authorizer {
switch {
case service != "" && operator != "":
return newAuthz(t, fmt.Sprintf(`service "test" { policy = %q } operator = %q`, service, operator))
case service == "" && operator != "":
return newAuthz(t, fmt.Sprintf(`operator = %q`, operator))
case service != "" && operator == "":
return newAuthz(t, fmt.Sprintf(`service "test" { policy = %q }`, service))
default:
t.Fatalf("one of these should be set")
return nil
}
}
2021-08-20 22:11:01 +00:00
newServiceAndMeshACL := func(t *testing.T, service, mesh string) acl.Authorizer {
switch {
case service != "" && mesh != "":
return newAuthz(t, fmt.Sprintf(`service "test" { policy = %q } mesh = %q`, service, mesh))
case service == "" && mesh != "":
return newAuthz(t, fmt.Sprintf(`mesh = %q`, mesh))
case service != "" && mesh == "":
return newAuthz(t, fmt.Sprintf(`service "test" { policy = %q }`, service))
default:
t.Fatalf("one of these should be set")
return nil
}
}
type testACL = configEntryTestACL
type testcase = configEntryACLTestCase
defaultDenyCase := testACL{
name: "deny",
authorizer: newServiceACL(t, nil, nil),
canRead: false,
canWrite: false,
}
readTestCase := testACL{
name: "can read test",
authorizer: newServiceACL(t, []string{"test"}, nil),
canRead: true,
canWrite: false,
}
writeTestCase := testACL{
name: "can write test",
authorizer: newServiceACL(t, nil, []string{"test"}),
canRead: true,
canWrite: true,
}
writeTestCaseDenied := testACL{
name: "cannot write test",
authorizer: newServiceACL(t, nil, []string{"test"}),
canRead: true,
canWrite: false,
}
2021-08-20 22:11:01 +00:00
cases := []testcase{
{
name: "resolver: self",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
},
expectServices: nil,
expectACLs: []testACL{
defaultDenyCase,
readTestCase,
writeTestCase,
},
},
{
name: "resolver: redirect",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{
Service: "other",
},
},
expectServices: []ServiceID{NewServiceID("other", nil)},
expectACLs: []testACL{
defaultDenyCase,
readTestCase,
writeTestCaseDenied,
{
name: "can write test (with other:read)",
authorizer: newServiceACL(t, []string{"other"}, []string{"test"}),
canRead: true,
canWrite: true,
},
},
},
{
name: "resolver: failover",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Subsets: map[string]ServiceResolverSubset{
"foo": {OnlyPassing: true},
"bar": {OnlyPassing: true},
},
Failover: map[string]ServiceResolverFailover{
"foo": {
Service: "other1",
},
"bar": {
Service: "other2",
},
},
},
expectServices: []ServiceID{NewServiceID("other1", nil), NewServiceID("other2", nil)},
expectACLs: []testACL{
defaultDenyCase,
readTestCase,
writeTestCaseDenied,
{
name: "can write test (with other1:read and other2:read)",
authorizer: newServiceACL(t, []string{"other1", "other2"}, []string{"test"}),
canRead: true,
canWrite: true,
},
},
},
{
name: "resolver: failover with targets",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
Targets: []ServiceResolverFailoverTarget{
{Service: "other1"},
{Datacenter: "dc2"},
{Peer: "cluster-01"},
},
},
},
},
expectServices: []ServiceID{NewServiceID("other1", nil)},
expectACLs: []testACL{
defaultDenyCase,
readTestCase,
writeTestCaseDenied,
{
name: "can write test (with other1:read)",
authorizer: newServiceACL(t, []string{"other1"}, []string{"test"}),
canRead: true,
canWrite: true,
},
},
},
{
name: "splitter: self",
entry: &ServiceSplitterConfigEntry{
Kind: ServiceSplitter,
Name: "test",
Splits: []ServiceSplit{
{Weight: 100},
},
},
expectServices: nil,
expectACLs: []testACL{
defaultDenyCase,
readTestCase,
writeTestCase,
},
},
{
name: "splitter: some",
entry: &ServiceSplitterConfigEntry{
Kind: ServiceSplitter,
Name: "test",
Splits: []ServiceSplit{
{Weight: 25, Service: "b"},
{Weight: 25, Service: "a"},
{Weight: 50, Service: "c"},
},
},
expectServices: []ServiceID{NewServiceID("a", nil), NewServiceID("b", nil), NewServiceID("c", nil)},
expectACLs: []testACL{
defaultDenyCase,
readTestCase,
writeTestCaseDenied,
{
name: "can write test (with a:read, b:read, and c:read)",
authorizer: newServiceACL(t, []string{"a", "b", "c"}, []string{"test"}),
canRead: true,
canWrite: true,
},
},
},
{
name: "router: self",
entry: &ServiceRouterConfigEntry{
Kind: ServiceRouter,
Name: "test",
},
expectServices: []ServiceID{NewServiceID("test", nil)},
expectACLs: []testACL{
defaultDenyCase,
readTestCase,
writeTestCase,
},
},
{
name: "router: some",
entry: &ServiceRouterConfigEntry{
Kind: ServiceRouter,
Name: "test",
Routes: []ServiceRoute{
{
Match: &ServiceRouteMatch{HTTP: &ServiceRouteHTTPMatch{
PathPrefix: "/foo",
}},
Destination: &ServiceRouteDestination{
Service: "foo",
},
},
{
Match: &ServiceRouteMatch{HTTP: &ServiceRouteHTTPMatch{
PathPrefix: "/bar",
}},
Destination: &ServiceRouteDestination{
Service: "bar",
},
},
},
},
expectServices: []ServiceID{NewServiceID("bar", nil), NewServiceID("foo", nil), NewServiceID("test", nil)},
expectACLs: []testACL{
defaultDenyCase,
readTestCase,
writeTestCaseDenied,
{
name: "can write test (with foo:read and bar:read)",
authorizer: newServiceACL(t, []string{"foo", "bar"}, []string{"test"}),
canRead: true,
canWrite: true,
},
},
},
2021-08-20 22:11:01 +00:00
{
name: "ingress-gateway",
entry: &IngressGatewayConfigEntry{Name: "test"},
expectACLs: []testACL{
{
name: "no-authz",
authorizer: newAuthz(t, ``),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator deny",
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator deny",
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator deny",
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh deny",
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh deny",
authorizer: newServiceAndMeshACL(t, "read", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh deny",
authorizer: newServiceAndMeshACL(t, "write", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator read",
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator read",
authorizer: newServiceAndOperatorACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator read",
authorizer: newServiceAndOperatorACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator write",
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
canRead: false,
canWrite: true,
},
{
name: "service read and operator write",
authorizer: newServiceAndOperatorACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and operator write",
authorizer: newServiceAndOperatorACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
{
name: "service deny and mesh read",
authorizer: newServiceAndMeshACL(t, "deny", "read"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh read",
authorizer: newServiceAndMeshACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh read",
authorizer: newServiceAndMeshACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh write",
authorizer: newServiceAndMeshACL(t, "deny", "write"),
canRead: false,
canWrite: true,
},
{
name: "service read and mesh write",
authorizer: newServiceAndMeshACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and mesh write",
authorizer: newServiceAndMeshACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
},
},
Native API Gateway Config Entries (#15897) * Stub Config Entries for Consul Native API Gateway (#15644) * Add empty InlineCertificate struct and protobuf * apigateway stubs * Stub HTTPRoute in api pkg * Stub HTTPRoute in structs pkg * Simplify api.APIGatewayConfigEntry to be consistent w/ other entries * Update makeConfigEntry switch, add docstring for HTTPRouteConfigEntry * Add TCPRoute to MakeConfigEntry, return unique Kind * Stub BoundAPIGatewayConfigEntry in agent * Add RaftIndex to APIGatewayConfigEntry stub * Add new config entry kinds to validation allow-list * Add RaftIndex to other added config entry stubs * Update usage metrics assertions to include new cfg entries * Add Meta and acl.EnterpriseMeta to all new ConfigEntry types * Remove unnecessary Services field from added config entry types * Implement GetMeta(), GetEnterpriseMeta() for added config entry types * Add meta field to proto, name consistently w/ existing config entries * Format config_entry.proto * Add initial implementation of CanRead + CanWrite for new config entry types * Add unit tests for decoding of new config entry types * Add unit tests for parsing of new config entry types * Add unit tests for API Gateway config entry ACLs * Return typed PermissionDeniedError on BoundAPIGateway CanWrite * Add unit tests for added config entry ACLs * Add BoundAPIGateway type to AllConfigEntryKinds * Return proper kind from BoundAPIGateway * Add docstrings for new config entry types * Add missing config entry kinds to proto def * Update usagemetrics_oss_test.go * Use utility func for returning PermissionDeniedError * EventPublisher subscriptions for Consul Native API Gateway (#15757) * Create new event topics in subscribe proto * Add tests for PBSubscribe func * Make configs singular, add all configs to PBToStreamSubscribeRequest * Add snapshot methods * Add config_entry_events tests * Add config entry kind to topic for new configs * Add unit tests for snapshot methods * Start adding integration test * Test using the new controller code * Update agent/consul/state/config_entry_events.go * Check value of error * Add controller stubs for API Gateway (#15837) * update initial stub implementation * move files, clean up mutex references * Remove embed, use idiomatic names for constructors * Remove stray file introduced in merge * Add APIGateway validation (#15847) * Add APIGateway validation * Add additional validations * Add cert ref validation * Add protobuf definitions * Fix up field types * Add API structs * Move struct fields around a bit * APIGateway InlineCertificate validation (#15856) * Add APIGateway validation * Add additional validations * Add protobuf definitions * Tabs to spaces * Add API structs * Move struct fields around a bit * Add validation for InlineCertificate * Fix ACL test * APIGateway BoundAPIGateway validation (#15858) * Add APIGateway validation * Add additional validations * Add cert ref validation * Add protobuf definitions * Fix up field types * Add API structs * Move struct fields around a bit * Add validation for BoundAPIGateway * APIGateway TCPRoute validation (#15855) * Add APIGateway validation * Add additional validations * Add cert ref validation * Add protobuf definitions * Fix up field types * Add API structs * Add TCPRoute normalization and validation * Add forgotten Status * Add some more field docs in api package * Fix test * Format imports * Rename snapshot test variable names * Add plumbing for Native API GW Subscriptions (#16003) Co-authored-by: Sarah Alsmiller <sarah.alsmiller@hashicorp.com> Co-authored-by: Nathan Coleman <nathan.coleman@hashicorp.com> Co-authored-by: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Co-authored-by: Andrew Stucki <andrew.stucki@hashicorp.com>
2023-01-18 22:14:34 +00:00
{
name: "api-gateway",
entry: &APIGatewayConfigEntry{
Name: "test",
Listeners: []APIGatewayListener{
{
Name: "test",
Port: 100,
Protocol: "http",
},
},
},
Native API Gateway Config Entries (#15897) * Stub Config Entries for Consul Native API Gateway (#15644) * Add empty InlineCertificate struct and protobuf * apigateway stubs * Stub HTTPRoute in api pkg * Stub HTTPRoute in structs pkg * Simplify api.APIGatewayConfigEntry to be consistent w/ other entries * Update makeConfigEntry switch, add docstring for HTTPRouteConfigEntry * Add TCPRoute to MakeConfigEntry, return unique Kind * Stub BoundAPIGatewayConfigEntry in agent * Add RaftIndex to APIGatewayConfigEntry stub * Add new config entry kinds to validation allow-list * Add RaftIndex to other added config entry stubs * Update usage metrics assertions to include new cfg entries * Add Meta and acl.EnterpriseMeta to all new ConfigEntry types * Remove unnecessary Services field from added config entry types * Implement GetMeta(), GetEnterpriseMeta() for added config entry types * Add meta field to proto, name consistently w/ existing config entries * Format config_entry.proto * Add initial implementation of CanRead + CanWrite for new config entry types * Add unit tests for decoding of new config entry types * Add unit tests for parsing of new config entry types * Add unit tests for API Gateway config entry ACLs * Return typed PermissionDeniedError on BoundAPIGateway CanWrite * Add unit tests for added config entry ACLs * Add BoundAPIGateway type to AllConfigEntryKinds * Return proper kind from BoundAPIGateway * Add docstrings for new config entry types * Add missing config entry kinds to proto def * Update usagemetrics_oss_test.go * Use utility func for returning PermissionDeniedError * EventPublisher subscriptions for Consul Native API Gateway (#15757) * Create new event topics in subscribe proto * Add tests for PBSubscribe func * Make configs singular, add all configs to PBToStreamSubscribeRequest * Add snapshot methods * Add config_entry_events tests * Add config entry kind to topic for new configs * Add unit tests for snapshot methods * Start adding integration test * Test using the new controller code * Update agent/consul/state/config_entry_events.go * Check value of error * Add controller stubs for API Gateway (#15837) * update initial stub implementation * move files, clean up mutex references * Remove embed, use idiomatic names for constructors * Remove stray file introduced in merge * Add APIGateway validation (#15847) * Add APIGateway validation * Add additional validations * Add cert ref validation * Add protobuf definitions * Fix up field types * Add API structs * Move struct fields around a bit * APIGateway InlineCertificate validation (#15856) * Add APIGateway validation * Add additional validations * Add protobuf definitions * Tabs to spaces * Add API structs * Move struct fields around a bit * Add validation for InlineCertificate * Fix ACL test * APIGateway BoundAPIGateway validation (#15858) * Add APIGateway validation * Add additional validations * Add cert ref validation * Add protobuf definitions * Fix up field types * Add API structs * Move struct fields around a bit * Add validation for BoundAPIGateway * APIGateway TCPRoute validation (#15855) * Add APIGateway validation * Add additional validations * Add cert ref validation * Add protobuf definitions * Fix up field types * Add API structs * Add TCPRoute normalization and validation * Add forgotten Status * Add some more field docs in api package * Fix test * Format imports * Rename snapshot test variable names * Add plumbing for Native API GW Subscriptions (#16003) Co-authored-by: Sarah Alsmiller <sarah.alsmiller@hashicorp.com> Co-authored-by: Nathan Coleman <nathan.coleman@hashicorp.com> Co-authored-by: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Co-authored-by: Andrew Stucki <andrew.stucki@hashicorp.com>
2023-01-18 22:14:34 +00:00
expectACLs: []testACL{
{
name: "no-authz",
authorizer: newAuthz(t, ``),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator deny",
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator deny",
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator deny",
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh deny",
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh deny",
authorizer: newServiceAndMeshACL(t, "read", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh deny",
authorizer: newServiceAndMeshACL(t, "write", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator read",
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator read",
authorizer: newServiceAndOperatorACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator read",
authorizer: newServiceAndOperatorACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator write",
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
canRead: false,
canWrite: true,
},
{
name: "service read and operator write",
authorizer: newServiceAndOperatorACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and operator write",
authorizer: newServiceAndOperatorACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
{
name: "service deny and mesh read",
authorizer: newServiceAndMeshACL(t, "deny", "read"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh read",
authorizer: newServiceAndMeshACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh read",
authorizer: newServiceAndMeshACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh write",
authorizer: newServiceAndMeshACL(t, "deny", "write"),
canRead: false,
canWrite: true,
},
{
name: "service read and mesh write",
authorizer: newServiceAndMeshACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and mesh write",
authorizer: newServiceAndMeshACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
},
},
{
name: "inline-certificate",
entry: &InlineCertificateConfigEntry{Name: "test", Certificate: validCertificate, PrivateKey: validPrivateKey},
expectACLs: []testACL{
{
name: "no-authz",
authorizer: newAuthz(t, ``),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator deny",
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator deny",
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service write and operator deny",
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service deny and mesh deny",
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh deny",
authorizer: newServiceAndMeshACL(t, "read", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service write and mesh deny",
authorizer: newServiceAndMeshACL(t, "write", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator read",
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
canRead: true,
canWrite: false,
},
{
name: "service read and operator read",
authorizer: newServiceAndOperatorACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator read",
authorizer: newServiceAndOperatorACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator write",
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
canRead: true,
canWrite: true,
},
{
name: "service read and operator write",
authorizer: newServiceAndOperatorACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and operator write",
authorizer: newServiceAndOperatorACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
{
name: "service deny and mesh read",
authorizer: newServiceAndMeshACL(t, "deny", "read"),
canRead: true,
canWrite: false,
},
{
name: "service read and mesh read",
authorizer: newServiceAndMeshACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh read",
authorizer: newServiceAndMeshACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh write",
authorizer: newServiceAndMeshACL(t, "deny", "write"),
canRead: true,
canWrite: true,
},
{
name: "service read and mesh write",
authorizer: newServiceAndMeshACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and mesh write",
authorizer: newServiceAndMeshACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
},
},
{
name: "http-route",
entry: &HTTPRouteConfigEntry{Name: "test"},
expectACLs: []testACL{
{
name: "no-authz",
authorizer: newAuthz(t, ``),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator deny",
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator deny",
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service write and operator deny",
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service deny and mesh deny",
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh deny",
authorizer: newServiceAndMeshACL(t, "read", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service write and mesh deny",
authorizer: newServiceAndMeshACL(t, "write", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator read",
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
canRead: true,
canWrite: false,
},
{
name: "service read and operator read",
authorizer: newServiceAndOperatorACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator read",
authorizer: newServiceAndOperatorACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator write",
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
canRead: true,
canWrite: true,
},
{
name: "service read and operator write",
authorizer: newServiceAndOperatorACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and operator write",
authorizer: newServiceAndOperatorACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
{
name: "service deny and mesh read",
authorizer: newServiceAndMeshACL(t, "deny", "read"),
canRead: true,
canWrite: false,
},
{
name: "service read and mesh read",
authorizer: newServiceAndMeshACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh read",
authorizer: newServiceAndMeshACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh write",
authorizer: newServiceAndMeshACL(t, "deny", "write"),
canRead: true,
canWrite: true,
},
{
name: "service read and mesh write",
authorizer: newServiceAndMeshACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and mesh write",
authorizer: newServiceAndMeshACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
},
},
{
name: "tcp-route",
entry: &TCPRouteConfigEntry{Name: "test"},
expectACLs: []testACL{
{
name: "no-authz",
authorizer: newAuthz(t, ``),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator deny",
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator deny",
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service write and operator deny",
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service deny and mesh deny",
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh deny",
authorizer: newServiceAndMeshACL(t, "read", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service write and mesh deny",
authorizer: newServiceAndMeshACL(t, "write", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator read",
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
canRead: true,
canWrite: false,
},
{
name: "service read and operator read",
authorizer: newServiceAndOperatorACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator read",
authorizer: newServiceAndOperatorACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator write",
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
canRead: true,
canWrite: true,
},
{
name: "service read and operator write",
authorizer: newServiceAndOperatorACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and operator write",
authorizer: newServiceAndOperatorACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
{
name: "service deny and mesh read",
authorizer: newServiceAndMeshACL(t, "deny", "read"),
canRead: true,
canWrite: false,
},
{
name: "service read and mesh read",
authorizer: newServiceAndMeshACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh read",
authorizer: newServiceAndMeshACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh write",
authorizer: newServiceAndMeshACL(t, "deny", "write"),
canRead: true,
canWrite: true,
},
{
name: "service read and mesh write",
authorizer: newServiceAndMeshACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and mesh write",
authorizer: newServiceAndMeshACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
},
},
{
name: "bound-api-gateway",
entry: &BoundAPIGatewayConfigEntry{Name: "test"},
expectACLs: []testACL{
{
name: "no-authz",
authorizer: newAuthz(t, ``),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator deny",
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator deny",
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator deny",
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh deny",
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh deny",
authorizer: newServiceAndMeshACL(t, "read", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh deny",
authorizer: newServiceAndMeshACL(t, "write", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator read",
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator read",
authorizer: newServiceAndOperatorACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator read",
authorizer: newServiceAndOperatorACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator write",
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator write",
authorizer: newServiceAndOperatorACL(t, "read", "write"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator write",
authorizer: newServiceAndOperatorACL(t, "write", "write"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh read",
authorizer: newServiceAndMeshACL(t, "deny", "read"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh read",
authorizer: newServiceAndMeshACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh read",
authorizer: newServiceAndMeshACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh write",
authorizer: newServiceAndMeshACL(t, "deny", "write"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh write",
authorizer: newServiceAndMeshACL(t, "read", "write"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh write",
authorizer: newServiceAndMeshACL(t, "write", "write"),
canRead: true,
canWrite: false,
},
},
},
2021-08-20 22:11:01 +00:00
{
name: "terminating-gateway",
entry: &TerminatingGatewayConfigEntry{Name: "test"},
expectACLs: []testACL{
{
name: "no-authz",
authorizer: newAuthz(t, ``),
canRead: false,
canWrite: false,
},
{
name: "service deny and operator deny",
authorizer: newServiceAndOperatorACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator deny",
authorizer: newServiceAndOperatorACL(t, "read", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator deny",
authorizer: newServiceAndOperatorACL(t, "write", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh deny",
authorizer: newServiceAndMeshACL(t, "deny", "deny"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh deny",
authorizer: newServiceAndMeshACL(t, "read", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh deny",
authorizer: newServiceAndMeshACL(t, "write", "deny"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator read",
authorizer: newServiceAndOperatorACL(t, "deny", "read"),
canRead: false,
canWrite: false,
},
{
name: "service read and operator read",
authorizer: newServiceAndOperatorACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and operator read",
authorizer: newServiceAndOperatorACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and operator write",
authorizer: newServiceAndOperatorACL(t, "deny", "write"),
canRead: false,
canWrite: true,
},
{
name: "service read and operator write",
authorizer: newServiceAndOperatorACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and operator write",
authorizer: newServiceAndOperatorACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
{
name: "service deny and mesh read",
authorizer: newServiceAndMeshACL(t, "deny", "read"),
canRead: false,
canWrite: false,
},
{
name: "service read and mesh read",
authorizer: newServiceAndMeshACL(t, "read", "read"),
canRead: true,
canWrite: false,
},
{
name: "service write and mesh read",
authorizer: newServiceAndMeshACL(t, "write", "read"),
canRead: true,
canWrite: false,
},
{
name: "service deny and mesh write",
authorizer: newServiceAndMeshACL(t, "deny", "write"),
canRead: false,
canWrite: true,
},
{
name: "service read and mesh write",
authorizer: newServiceAndMeshACL(t, "read", "write"),
canRead: true,
canWrite: true,
},
{
name: "service write and mesh write",
authorizer: newServiceAndMeshACL(t, "write", "write"),
canRead: true,
canWrite: true,
},
},
},
}
2021-08-20 22:11:01 +00:00
testConfigEntries_ListRelatedServices_AndACLs(t, cases)
}
func TestServiceResolverConfigEntry(t *testing.T) {
type testcase struct {
name string
entry *ServiceResolverConfigEntry
normalizeErr string
validateErr string
// check is called between normalize and validate
check func(t *testing.T, entry *ServiceResolverConfigEntry)
}
cases := []testcase{
{
name: "nil",
entry: nil,
normalizeErr: "config entry is nil",
},
{
name: "no name",
entry: &ServiceResolverConfigEntry{},
validateErr: "Name is required",
},
{
name: "empty",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
},
},
{
name: "empty subset name",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Subsets: map[string]ServiceResolverSubset{
"": {OnlyPassing: true},
},
},
validateErr: "Subset defined with empty name",
},
{
name: "invalid boolean expression subset filter",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Subsets: map[string]ServiceResolverSubset{
"v1": {Filter: "random string"},
},
},
validateErr: `Filter for subset "v1" is not a valid expression`,
},
{
name: "default subset does not exist",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
DefaultSubset: "gone",
Subsets: map[string]ServiceResolverSubset{
"v1": {Filter: "Service.Meta.version == v1"},
},
},
validateErr: `DefaultSubset "gone" is not a valid subset`,
},
{
name: "default subset does exist",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
DefaultSubset: "v1",
Subsets: map[string]ServiceResolverSubset{
"v1": {Filter: "Service.Meta.version == v1"},
},
},
},
{
name: "empty redirect",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{},
},
validateErr: "Redirect is empty",
},
{
name: "empty redirect",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{},
},
validateErr: "Redirect is empty",
},
{
name: "redirect subset with no service",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{
ServiceSubset: "next",
},
},
validateErr: "Redirect.ServiceSubset defined without Redirect.Service",
},
{
name: "self redirect with invalid subset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{
Service: "test",
ServiceSubset: "gone",
},
},
validateErr: `Redirect.ServiceSubset "gone" is not a valid subset of "test"`,
},
{
name: "redirect with peer and subset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{
Peer: "cluster-01",
ServiceSubset: "gone",
},
},
validateErr: `Redirect.Peer cannot be set with Redirect.ServiceSubset`,
},
{
name: "redirect with peer and datacenter",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{
Peer: "cluster-01",
Datacenter: "dc2",
},
},
validateErr: `Redirect.Peer cannot be set with Redirect.Datacenter`,
},
{
name: "redirect with peer and datacenter",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{
Peer: "cluster-01",
},
},
validateErr: `Redirect.Peer defined without Redirect.Service`,
},
{
name: "self redirect with valid subset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{
Service: "test",
ServiceSubset: "v1",
},
Subsets: map[string]ServiceResolverSubset{
"v1": {Filter: "Service.Meta.version == v1"},
},
},
},
{
name: "redirect to peer",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{
Service: "other",
Peer: "cluster-01",
},
},
},
{
name: "simple wildcard failover",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
Datacenters: []string{"dc2"},
},
},
},
},
{
name: "failover for missing subset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"gone": {
Datacenters: []string{"dc2"},
},
},
},
validateErr: `Bad Failover["gone"]: not a valid subset`,
},
{
name: "failover for present subset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Subsets: map[string]ServiceResolverSubset{
"v1": {Filter: "Service.Meta.version == v1"},
},
Failover: map[string]ServiceResolverFailover{
"v1": {
Datacenters: []string{"dc2"},
},
},
},
},
{
name: "failover empty",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Subsets: map[string]ServiceResolverSubset{
"v1": {Filter: "Service.Meta.version == v1"},
},
Failover: map[string]ServiceResolverFailover{
"v1": {},
},
},
validateErr: `Bad Failover["v1"]: one of Service, ServiceSubset, Namespace, Targets, SamenessGroup, or Datacenters is required`,
},
{
name: "failover to self using invalid subset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Subsets: map[string]ServiceResolverSubset{
"v1": {Filter: "Service.Meta.version == v1"},
},
Failover: map[string]ServiceResolverFailover{
"v1": {
Service: "test",
ServiceSubset: "gone",
},
},
},
validateErr: `Bad Failover["v1"]: ServiceSubset "gone" is not a valid subset of "test"`,
},
{
name: "failover to self using valid subset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Subsets: map[string]ServiceResolverSubset{
"v1": {Filter: "Service.Meta.version == v1"},
"v2": {Filter: "Service.Meta.version == v2"},
},
Failover: map[string]ServiceResolverFailover{
"v1": {
Service: "test",
ServiceSubset: "v2",
},
},
},
},
{
name: "failover with empty datacenters in list",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
Service: "backup",
Datacenters: []string{"", "dc2", "dc3"},
},
},
},
validateErr: `Bad Failover["*"].Datacenters: found empty datacenter`,
},
{
name: "failover target with an invalid subset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
Targets: []ServiceResolverFailoverTarget{{ServiceSubset: "subset"}},
},
},
},
validateErr: `Bad Failover["*"].Targets[0]: ServiceSubset "subset" is not a valid subset of "test"`,
},
{
name: "failover targets can't have Peer and ServiceSubset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
Targets: []ServiceResolverFailoverTarget{{Peer: "cluster-01", ServiceSubset: "subset"}},
},
},
},
validateErr: `Bad Failover["*"].Targets[0]: Peer cannot be set with ServiceSubset`,
},
{
name: "failover targets can't have Peer and Datacenter",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
Targets: []ServiceResolverFailoverTarget{{Peer: "cluster-01", Datacenter: "dc1"}},
},
},
},
validateErr: `Bad Failover["*"].Targets[0]: Peer cannot be set with Datacenter`,
},
{
name: "failover Targets cannot be set with Datacenters",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
Datacenters: []string{"a"},
Targets: []ServiceResolverFailoverTarget{{Peer: "cluster-01"}},
},
},
},
validateErr: `Bad Failover["*"]: Targets cannot be set with Datacenters`,
},
{
name: "failover Targets cannot be set with ServiceSubset",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
ServiceSubset: "v2",
Targets: []ServiceResolverFailoverTarget{{Peer: "cluster-01"}},
},
},
Subsets: map[string]ServiceResolverSubset{
"v2": {Filter: "Service.Meta.version == v2"},
},
},
validateErr: `Bad Failover["*"]: Targets cannot be set with ServiceSubset`,
},
{
name: "failover Targets cannot be set with Service",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
Service: "another-service",
Targets: []ServiceResolverFailoverTarget{{Peer: "cluster-01"}},
},
},
Subsets: map[string]ServiceResolverSubset{
"v2": {Filter: "Service.Meta.version == v2"},
},
},
validateErr: `Bad Failover["*"]: Targets cannot be set with Service`,
},
{
name: "complicated failover targets",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
Targets: []ServiceResolverFailoverTarget{
{Peer: "cluster-01", Service: "test-v2"},
{Service: "test-v2", ServiceSubset: "test"},
{Datacenter: "dc2"},
},
},
},
},
},
{
name: "bad connect timeout",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
ConnectTimeout: -1 * time.Second,
},
validateErr: "Bad ConnectTimeout",
},
Backport of Include RequestTimeout in marshal/unmarshal of ServiceResolverConfigE… into release/1.16.x (#19034) * Net-2712/resource hcl parsing (#18250) * Initial protohcl implementation Co-authored-by: Matt Keeler <mkeeler@users.noreply.github.com> Co-authored-by: Daniel Upton <daniel@floppy.co> * resourcehcl: implement resource decoding on top of protohcl Co-authored-by: Daniel Upton <daniel@floppy.co> * fix: resolve ci failures * test: add additional unmarshalling tests * refactor: update function test to clean protohcl package imports --------- Co-authored-by: Matt Keeler <mkeeler@users.noreply.github.com> Co-authored-by: Daniel Upton <daniel@floppy.co> * read endpoint (#18268) implement http read endpoint to expose resource grpc service read method * Net-2707/list resource endpoint (#18444) feat: list resources endpoint * Fix incorrect yaml in examples (#18463) * NET-5187: Upgrade test timeout due to log producer errors (#18461) Stop log producer before restarting container * [NET-4799] [OSS] xdsv2: listeners L4 support for connect proxies (#18436) * refactor to avoid future import cycles * resource: Make resource list tenancy aware (#18475) * [COMPLIANCE] License update (#18479) Update BUSL LICENSE to include licensed product and version. Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> * docs: specify manual server metadata intervention (#18477) * docs: Update OpenShift compatibility (#18478) Update compatibility.mdx * resource: Make resource delete tenancy aware (#18476) resource: Make resource delete tenancy awarae * Add license-checker action that fails when any backported file contains BUSL header (#18485) * Add license-checker action that fails when any backported file contains BUSL header * Quote echoed variable to retain line breaks * Add ticket to reference for more details * docs: Update K8s TGW tutorial to reliably obtain role ID (#18474) The `grep` command used to obtain the ID for the terminating gateway role is not reliable in all scenarios. For example, if there is a similarly named role, the command may return the wrong role ID for the active terminating gateway instance. This commit updates the command to use jq to obtain the role ID. If multiple roles are found, jq will raise an error informing the user that it cannot reliably determine the role ID. * NET-5371 License checker pt2 (#18491) * Update grep command to work on ubuntu * Run license checker when new commits are pushed to PR * README - Update KV use case to Dynamic App Configuration (#18301) * Update README.md * Update kv.mdx * Add BUSL badge * README - re-order badges and update hub link (#18498) Update README.md * [NET-5163] Support locality testing in consul-container (#18484) * Support locality testing in consul-container Support including locality in client sidecar config. Also align test config structs with Ent to avoid future conflicts. * Refactor consul-container fortio helpers Refactor fortio test helpers to separate HTTP retries from waiting on fortio result changes due to e.g. service startup and failovers. * NET-4853 - xds v2 - implement base connect proxy functionality for clusters (#18499) * Fix HCL (#18513) * Fix HCL * Update create-sameness-groups.mdx * NET-4932 - xds v2 - implement base connect proxy functionality for endpoints (#18500) * NET-4853 - xds v2 - implement base connect proxy functionality for clusters * NET-4853 - xds v2 - implement base connect proxy functionality for clusters * NET-4932 - xds v2 - implement base connect proxy functionality for endpoints * Update endpoints_test.go * gofmt * Update naming.go * Support custom watches on controller (#18439) * Support custom watches on controller * refactor mapper methods * NET-4858 - xds v2 - implement base connect proxy functionality for routes (#18501) * NET-4853 - xds v2 - implement base connect proxy functionality for clusters * NET-4853 - xds v2 - implement base connect proxy functionality for clusters * NET-4932 - xds v2 - implement base connect proxy functionality for endpoints * Update endpoints_test.go * gofmt * NET-4858 - Make connect proxy route tests pass using xds v2 * Update endpoints_test.go * Update naming.go * use alsoRunTestForV2 * remove unused makeAddress * gofmt * fixing clusters * bimapper: allow to untrack links and support reference or id (#18451) * bimapper: fix data race (#18519) * CI Split integration tests to run nightly and every PR (#18518) * CI Split integration tests to run nightly and every PR * Checkout release branch for nightly test * Fix broken links caught in weekly report (#18522) * fix broken link caught in weekly report * 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> * Create nightly test-integration for consul release branch (#18530) * Create nightly test-integration for consul release branch * test * fix * docs: Fix spelling errors across various pages on the site (#18533) This commit fixes numerous spelling errors across the site and also removes unnecessary whitespace that was present in the edited files. * Make proto-public license MPL (#18531) * Make proto-public license MPL * Add proto-public dir to exclusion list in .copywrite.hcl * NET-4943 - Implement ProxyTracker (#18535) * resource: Make resource watchlist tenancy aware (#18539) * Reduce required type arguments for DecodedResource (#18540) * OSS -> CE (community edition) changes (#18517) * CI: fix envoy versions in CI of release branch (#18538) * CI: fix envoy versions in CI of release branch * - remove steps sds for 1.15 nightly run - checkout the release branch * add default name * fix * Update go-tests.yml * fix checkout code * add comments and revert schedule * catalog: add failover mode enum validation (#18545) * mesh: add validation for the new pbmesh resources (#18410) Adds validation for HTTPRoute, GRPCRoute, TCPRoute, DestinationPolicy, and ComputedRoutes. * xds controller: resolve ServiceEndpoints references in ProxyStateTemp… (#18544) xds controller: resolve ServiceEndpoints references in ProxyStateTemplate * mesh: adding type aliases for mesh resource usage (#18448) Introduces some simple type aliases for DecodedResource[*X] wrappers for each type which cut down on the verbosity * Update trust bundle into proxy-state-template (#18550) * catalog: validating Protocol and Health enums on Service, Workload, and ServiceEndpoints (#18554) * chore: fix missing/incorrect license headers (#18555) * fix for , non presence of consul-version meta (#18464) * fix for #18406 , non presence of consul-version meta * removed redundant checks * updated mock-api to mimic api response for synthetic nodes * added test to test getDistinctConsulVersions method with synthetic-node case * updated typo in comments * added change log * feat: add experiments flag to testserver sdk (#18541) * bug: prevent go routine leakage due to existing DeferCheck (#18558) * bug: prevent go routine leakage due to existing DeferCheck * add changelog * NET-5382 & PLAT-1159: Do not trigger workflow if only doc files are in commit history (#18528) NET-5382: Do not run workflow runs if at least one path matches * UI: community verbiage (#18560) * chore: update community verbiage * chore: add changelog entry * Fix Windows FIPS Build (#18357) * Fix Windows FIPS Build * create new file for windows arm * removed build tag * fix buidl tags * fix multiple go build * CE port of enterprise extension (#18572) CE commit * resource: Make resource listbyowner tenancy aware (#18566) * NET-5147: Added placeholder structs for JWT functionality (#18575) * Added placeholder structs for JWT functionality * Added watches for CE vs ENT * Add license header * Undo plumbing work * Add context arg * resource: Make resource writestatus tenancy aware (#18577) * NET-5338 - NET-5338 - Run a v2 mode xds server (#18579) * NET-5338 - NET-5338 - Run a v2 mode xds server * fix linting * CI: send slack notification on failed nightly job (#18578) * CE commit (#18583) * Fix broken link on sameness group page (#18511) fix broken link * Fix typo in permissive mTLS docs (#18551) Update onboarding-tproxy-mode.mdx * Set concurrency for workflows (#18567) * Set concurrency for workflows * test: run automated tests against Vault 1.11 - 1.14 (#18590) Begin to test against Vault 1.14. Drop tests against Vault 1.10. * Reduce the frequency of metric exports to minutely (#18584) * NET-4944 - wire up controllers with proxy tracker (#18603) Co-authored-by: github-team-consul-core <github-team-consul-core@hashicorp.com> * Watch the ProxyTracker from xDS controller (#18611) * Distinguish v2 catalog Protocol unset from default (#18612) Even though we intend to default to TCP when this field is not explicitly provided, uncluding an `UNSPECIFIED` default enum value allows us to create inheritance chains, e.g. service to workload. * Added OpenTelemetry Access Logging Envoy extension (#18336) * [HCP Telemetry] Move first TelemetryConfig Fetch into the TelemetryConfigProvider (#18318) * Add Enabler interface to turn sink on/off * Use h for hcpProviderImpl vars, fix PR feeback and fix errors * Keep nil check in exporter and fix tests * Clarify comment and fix function name * Use disable instead of enable * Fix errors nit in otlp_transform * Add test for refreshInterval of updateConfig * Add disabled field in MetricsConfig struct * Fix PR feedback: improve comment and remove double colons * Fix deps test which requires a maybe * Update hcp-sdk-go to v0.61.0 * use disabled flag in telemetry_config.go * Handle 4XX errors in telemetry_provider * Fix deps test * Check 4XX instead * Run make go-mod-tidy * update comments and docs about running envoy integration tests with the ENVOY_VERSION set. (#18614) update ENVOY_VERSION and documentation of it used in the bats envoy tests. Co-authored-by: github-team-consul-core <github-team-consul-core@hashicorp.com> * delete all v2 resources type when deleting a namespace (CE) (#18621) * add namespace scope to ServiceV1Alpha1Type * add CE portion of namespace deletion * resource: Allow nil tenancy (#18618) * PLAT-1192 - Run CI on smaller instances (#18624) Use smaller runners * NET-3181 - Allow log file naming like Nomad (#18617) * fixes file name for consul * added log file * added tests for rename method * Add the plumbing for APIGW JWT work (#18609) * Add the plumbing for APIGW JWT work * Remove unneeded import * Add deep equal function for HTTPMatch * Added plumbing for status conditions * Remove unneeded comment * Fix comments * Add calls in xds listener for apigateway to setup listener jwt auth * docs: admin partition and DNS clarification (#18613) * fix: make UNSPECIFIED protocol pass validation (#18634) We explicitly enumerate the allowed protocols in validation, so this change is necessary to use the new enum value. Also add tests for enum validators to ensure they stay aligned to protos unless we explicitly want them to diverge. * NET 1594 - Snapshot Agent Filename Should Include Consul Version / Datacenter (#18625) * init * tests added and few fixes * revert arg message * changelog added * removed var declaration * fix CI * fix test * added node name and status * updated save.mdx * added example * fix tense * fix description * Net 3181 consul gh issue 15709 allow log file naming like nomad - fix bug (#18631) * fixes file name for consul * added log file * added tests for rename method * append instead of trunc * fix file truncate issue * added changelog * fix for build destros ci * removed changelog * solaris * resource: Require scope for resource registration (#18635) * [NET-4958] Fix issue where envoy endpoints would fail to populate after snapshot restore (#18636) Fix issue where agentless endpoints would fail to populate after snapshot restore. Fixes an issue that was introduced in #17775. This issue happens because a long-lived pointer to the state store is held, which is unsafe to do. Snapshot restorations will swap out this state store, meaning that the proxycfg watches would break for agentless. * Add extra logging for mesh health endpoints. (#18647) * feat: Change global-read-only policy to non editable (#18602) * remove stray FIPS file (#18651) * Add known issue notice for #18636. (#18650) * fix: remove Intention test with latency to make consul-enterprise rep… (#18654) fix: remove Intention test with latency to make consul-enterprise repo tests work * fix: add telemetry bind socket to proxyConfiguration proto (#18652) * feat: copy edits for built-in policy alert (#18655) * test: fix snapshot save w/ append test for ENT (#18656) * Net 2714/xw cli read command (#18462) enable `consul resource read` command in cli * change log for audit log (#18668) * change log for audit log * fix file name * breaking change * Add TCP+TLS Healthchecks (#18381) * Begin adding TCPUseTLS * More TCP with TLS plumbing * Making forward progress * Keep on adding TCP+TLS support for healthchecks * Removed too many lines * Unit tests for TCP+TLS * Update tlsutil/config.go Co-authored-by: Samantha <hello@entropy.cat> * Working on the tcp+tls unit test * Updated the runtime integration tests * Progress * Revert this file back to HEAD * Remove debugging lines * Implement TLS enabled TCP socket server and make a successful TCP+TLS healthcheck on it * Update docs * Update agent/agent_test.go Co-authored-by: Samantha <hello@entropy.cat> * Update website/content/docs/ecs/configuration-reference.mdx Co-authored-by: Samantha <hello@entropy.cat> * Update website/content/docs/ecs/configuration-reference.mdx Co-authored-by: Samantha <hello@entropy.cat> * Update agent/checks/check.go Co-authored-by: Samantha <hello@entropy.cat> * Address comments * Remove extraneous bracket * Update agent/agent_test.go Co-authored-by: Samantha <hello@entropy.cat> * Update agent/agent_test.go Co-authored-by: Samantha <hello@entropy.cat> * Update website/content/docs/ecs/configuration-reference.mdx Co-authored-by: Samantha <hello@entropy.cat> * Update the mockTLSServer * Remove trailing newline * Address comments * Fix merge problem * Add changelog entry --------- Co-authored-by: Samantha <hello@entropy.cat> * Fix windows Ent runner (#18683) * fix windows image for enterprise * added quotesT * Removed nodename and status from consul snapshot save -append-filename command and using leader version in version (#18680) * init * fix tests * fix tests lint * fix api call inside dc * updated doc * address comments * update guide to reflect tenancy and scope (#18687) * update guide to reflect tenancy and scope * Apply suggestions from code review Co-authored-by: Semir Patel <semir.patel@hashicorp.com> * update ACLHooks signature * Update docs/resources/guide.md Co-authored-by: Semir Patel <semir.patel@hashicorp.com> --------- Co-authored-by: Semir Patel <semir.patel@hashicorp.com> * Add support for querying tokens by service name. (#18667) Add support for querying tokens by service name The consul-k8s endpoints controller has a workflow where it fetches all tokens. This is not performant for large clusters, where there may be a sizable number of tokens. This commit attempts to alleviate that problem and introduces a new way to query by the token's service name. * restore common-topo peering integration tests, but nightly [NET-5476] (#18673) * dataplane: Allow getting bootstrap parameters when using V2 APIs (#18504) This PR enables the GetEnvoyBootstrapParams endpoint to construct envoy bootstrap parameters from v2 catalog and mesh resources. * Make bootstrap request and response parameters less specific to services so that we can re-use them for workloads or service instances. * Remove ServiceKind from bootstrap params response. This value was unused previously and is not needed for V2. * Make access logs generation generic so that we can generate them using v1 or v2 resources. * switch all client nodes in dc2 to dataplane [NET-4299] (#18608) * Reformat proxy docs refresh (#18623) * first commit; reformat PD conf entry * updated proxies overview page * added Deploy SM proxy usage and removed reg index * moved sidecar proxy usage to main proxy folder * recast sidecar reg page as Deploy sidecar services * fix typos * recast SM reg as conf reference- set the sidebar * add redirects * fix links * add PD conf entry usage to appropro pages * edits to proxy conf ref * fix links on index page * example command to write PD conf entry * updated links to old SM proxy reg page * updated links to sidecar service reg page * tryna fix front matter issues * Apply suggestions from code review Co-authored-by: Ronald <roncodingenthusiast@users.noreply.github.com> * added paragraph about SM proxies to overivew * Apply suggestions from code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> --------- Co-authored-by: Ronald <roncodingenthusiast@users.noreply.github.com> Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> * sidecar-proxy controller: L4 controller with explicit upstreams (NET-3988) (#18352) * This controller generates and saves ProxyStateTemplate for sidecar proxies. * It currently supports single-port L4 ports only. * It keeps a cache of all destinations to make it easier to compute and retrieve destinations. * It will update the status of the pbmesh.Upstreams resource if anything is invalid. * This commit also changes service endpoints to include workload identity. This made the implementation a bit easier as we don't need to look up as many workloads and instead rely on endpoints data. * test: update sidecarproxy/builder golden tests to use determinstic golden data (#18703) * [APIGW] NET-5017 JWT Cleanup/Status Conditions (#18700) * Fixes issues in setting status * Update golden files for changes to xds generation to not use deprecated methods * Fixed default for validation of JWT for route * Added changelog for jwt features (#18709) * fix: NET-1521 show latest config in /v1/agent/self (#18681) * fix: NET-1521 show latest config in /v1/agent/self * Using larger machine for Enterprise (#18713) using 4x large for ent * [NET-5325] ACL templated policies support in tokens and roles (#18708) * [NET-5325] ACL templated policies support in tokens and roles - Add API support for creating tokens/roles with templated-policies - Add CLI support for creating tokens/roles with templated-policies * adding changelog * Adds PassiveHealthCheck Fields to ServiceDefaults and IngressGateway (#18532) Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com> * Instantiate secure resource service client after the grpc server (#18712) * ENT port: test-integ/peering: peer through mesh gateway [NET-4609] (#18605) Co-authored-by: Matt Keeler <mjkeeler7@gmail.com> * [CC-6039] Update builtin policy descriptions (#18705) * Net-2713/resource apply command (#18610) * feat: implement apply command * fix: resolve lint issues * refactor: use fixed types * fix: ci lint failures after main rebase * NET-5530 Support response header modifiers on http-route config entry (#18646) * Add response header filters to http-route config entry definitions * Map response header filters from config entry when constructing route destination * Support response header modifiers at the service level as well * Update protobuf definitions * Update existing unit tests * Add response filters to route consolidation logic * Make existing unit tests more robust * Add missing docstring * Add changelog entry * Add response filter modifiers to existing integration test * Add more robust testing for response header modifiers in the discovery chain * Add more robust testing for request header modifiers in the discovery chain * Modify test to verify that service filter modifiers take precedence over rule filter modifiers * fix: emit consul version metric on a regular interval (#18724) * [NET-5330] Support templated policies in Binding rules (#18719) * [NET-5330] Support templated policies in Binding rules * changelog for templated policy support in binding rules * NET-5530 Generate deep-copy code (#18730) * Generate deep-copy code * Undo license header removal * sidecar-proxy controller: Add support for transparent proxy (NET-5069) (#18458) This commit adds support for transparent proxy to the sidecar proxy controller. As we do not yet support inferring destinations from intentions, this assumes that all services in the cluster are destinations. * Prefix sidecar proxy test files with source and destination. (#18620) * mesh-controller: handle L4 protocols for a proxy without upstreams * sidecar-controller: Support explicit destinations for L4 protocols and single ports. * This controller generates and saves ProxyStateTemplate for sidecar proxies. * It currently supports single-port L4 ports only. * It keeps a cache of all destinations to make it easier to compute and retrieve destinations. * It will update the status of the pbmesh.Upstreams resource if anything is invalid. * endpoints-controller: add workload identity to the service endpoints resource * small fixes * review comments * Address PR comments * sidecar-proxy controller: Add support for transparent proxy This currently does not support inferring destinations from intentions. * PR review comments * mesh-controller: handle L4 protocols for a proxy without upstreams * sidecar-controller: Support explicit destinations for L4 protocols and single ports. * This controller generates and saves ProxyStateTemplate for sidecar proxies. * It currently supports single-port L4 ports only. * It keeps a cache of all destinations to make it easier to compute and retrieve destinations. * It will update the status of the pbmesh.Upstreams resource if anything is invalid. * endpoints-controller: add workload identity to the service endpoints resource * small fixes * review comments * Make sure endpoint refs route to mesh port instead of an app port * Address PR comments * fixing copyright * tidy imports * sidecar-proxy controller: Add support for transparent proxy This currently does not support inferring destinations from intentions. * tidy imports * add copyright headers * Prefix sidecar proxy test files with source and destination. * Update controller_test.go --------- Co-authored-by: Iryna Shustava <iryna@hashicorp.com> Co-authored-by: R.B. Boyer <rb@hashicorp.com> Co-authored-by: github-team-consul-core <github-team-consul-core@hashicorp.com> * k8s: Update audit-logging docs to include K8s examples (#18721) Update audit-logging.mdx * Fix windows integrations tests machine size (#18736) fix windows integrations tests machine size * Increase timeout for integration test windows. (#18737) increase timeout * Net-4292/list command (#18649) * feat: implement list command * refactor: apply command file parsing * Added logs for debugging (#18740) * added logs * added echo * removed pull request * xds: update golden tests to be deterministic (#18707) * Run copyright after running deep-copy as part of the Makefile/CI (#18741) * execute copyright headers after performing deep-copy generation. * fix copyright install * Apply suggestions from code review Co-authored-by: Semir Patel <semir.patel@hashicorp.com> * Apply suggestions from code review Co-authored-by: Semir Patel <semir.patel@hashicorp.com> * rename steps to match codegen naming * remove copywrite install category --------- Co-authored-by: Semir Patel <semir.patel@hashicorp.com> * [NET-5333] Add api to read/list and preview templated policies (#18748) * NET-5132 - Configure multiport routing for connect proxies in TProxy mode (#18606) * mesh-controller: handle L4 protocols for a proxy without upstreams * sidecar-controller: Support explicit destinations for L4 protocols and single ports. * This controller generates and saves ProxyStateTemplate for sidecar proxies. * It currently supports single-port L4 ports only. * It keeps a cache of all destinations to make it easier to compute and retrieve destinations. * It will update the status of the pbmesh.Upstreams resource if anything is invalid. * endpoints-controller: add workload identity to the service endpoints resource * small fixes * review comments * Address PR comments * sidecar-proxy controller: Add support for transparent proxy This currently does not support inferring destinations from intentions. * PR review comments * mesh-controller: handle L4 protocols for a proxy without upstreams * sidecar-controller: Support explicit destinations for L4 protocols and single ports. * This controller generates and saves ProxyStateTemplate for sidecar proxies. * It currently supports single-port L4 ports only. * It keeps a cache of all destinations to make it easier to compute and retrieve destinations. * It will update the status of the pbmesh.Upstreams resource if anything is invalid. * endpoints-controller: add workload identity to the service endpoints resource * small fixes * review comments * Make sure endpoint refs route to mesh port instead of an app port * Address PR comments * fixing copyright * tidy imports * sidecar-proxy controller: Add support for transparent proxy This currently does not support inferring destinations from intentions. * tidy imports * add copyright headers * Prefix sidecar proxy test files with source and destination. * Update controller_test.go * NET-5132 - Configure multiport routing for connect proxies in TProxy mode * formatting golden files * reverting golden files and adding changes in manually. build implicit destinations still has some issues. * fixing files that were incorrectly repeating the outbound listener * PR comments * extract AlpnProtocol naming convention to getAlpnProtocolFromPortName(portName) * removing address level filtering. * adding license to resources_test.go --------- Co-authored-by: Iryna Shustava <iryna@hashicorp.com> Co-authored-by: R.B. Boyer <rb@hashicorp.com> Co-authored-by: github-team-consul-core <github-team-consul-core@hashicorp.com> * added time stamps (#18757) * fix: write endpoint errors out gracefully (#18743) * [NET-5574] Update Go version to 1.20.8 (#18742) Update Go version to 1.20.8 This resolves several CVEs (see changelog entry). * Add Envoy golden test for OTEL access logging extension (#18760) * mesh: create new routes-controller to reconcile xRoute types into a ComputedRoutes resource (#18460) This new controller produces an intermediate output (ComputedRoutes) that is meant to summarize all relevant xRoutes and related mesh configuration in an easier-to-use format for downstream use to construct the ProxyStateTemplate. It also applies status updates to the xRoute resource types to indicate that they are themselves semantically valid inputs. * xds controller: setup watches for and compute leaf cert references in ProxyStateTemplate, and wire up leaf cert manager dependency (#18756) * Refactors the leafcert package to not have a dependency on agent/consul and agent/cache to avoid import cycles. This way the xds controller can just import the leafcert package to use the leafcert manager. The leaf cert logic in the controller: * Sets up watches for leaf certs that are referenced in the ProxyStateTemplate (which generates the leaf certs too). * Gets the leaf cert from the leaf cert cache * Stores the leaf cert in the ProxyState that's pushed to xds * For the cert watches, this PR also uses a bimapper + a thin wrapper to map leaf cert events to related ProxyStateTemplates Since bimapper uses a resource.Reference or resource.ID to map between two resource types, I've created an internal type for a leaf certificate to use for the resource.Reference, since it's not a v2 resource. The wrapper allows mapping events to resources (as opposed to mapping resources to resources) The controller tests: Unit: Ensure that we resolve leaf cert references Lifecycle: Ensure that when the CA is updated, the leaf cert is as well Also adds a new spiffe id type, and adds workload identity and workload identity URI to leaf certs. This is so certs are generated with the new workload identity based SPIFFE id. * Pulls out some leaf cert test helpers into a helpers file so it can be used in the xds controller tests. * Wires up leaf cert manager dependency * Support getting token from proxytracker * Add workload identity spiffe id type to the authorize and sign functions --------- Co-authored-by: John Murret <john.murret@hashicorp.com> * Test: NET-5592 - update Nomad integration testing (#18768) NET-5592 - update Nomad integration testing * Add workload identity ACL rules (#18769) * Windows Integration Test Changes (#18758) * some changes to debug * revert machines * increased timeout * added sleep 10 seconds before test start * chagne envoy version * removed sleep * revert timeout * replace position * removed date * Revert "[NET-5217] [OSS] Derive sidecar proxy locality from parent service (#18437)" This reverts commit 05604eeec1186a4c8a6156e7519a579171659c1a. * fix build * Revert "replace position" This reverts commit 48e6af46a8daae186c283f30d316b1104906993e. * Revert "Revert "[NET-5217] [OSS] Derive sidecar proxy locality from parent service (#18437)"" This reverts commit d7c568e2be727b72e6827225782e0e17ac06b74a. * comment out api gateway http hostnames test * fix import * revert integ test run on PR * Add V2 TCP traffic permissions (#18771) Add support for TCP traffic permissions * resource: add helper to normalize inner Reference tenancy during mutate (#18765) When one resource contains an inner field that is of type *pbresource.Reference we want the Tenancy to be reasonably defaulted by the following rules: 1. The final values will be limited by the scope of the referenced type. 2. Values will be inferred from the parent's tenancy, and if that is insufficient then using the default tenancy for the type's scope. 3. Namespace will only be used from a parent if the reference and the parent share a partition, otherwise the default namespace will be used. Until we tackle peering, this hard codes an assumption of peer name being local. The logic for defaulting may need adjustment when that is addressed. * Disable docs change CI skip detection (#18775) There's currently a bug that causes CI to be skipped on all non-PR changes. Until that's fixed and we can be certain the check will fail CI or default to running tests in the case of errors, disabling this check. * delete command ready (#18679) * delete command for resource management * Vault CA provider clean up previous default issuers (#18773) * Fix snapshot creation issue. (#18783) The renaming of files from oss -> ce caused incorrect snapshots to be created due to ce writes now happening prior to ent writes. When this happens various entities will attempt to be restored from the snapshot prior to a partition existing and will cause a panic to occur. * catalog: service endpoints inherits protocol from service when workload doesn't have one (#18792) * add fuzz tests to resourcehcl package and fix some panics (#18798) * add a simple fuzz test for the resourcehcl package and fix some panics uncovered by the test * fix default for null values * Clean up resources in test (#18799) * Updates gateway-class-config docs to include fields for privileged container port-mapping and Openshift Security Context Constraints Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com> * Removes errant word in docs that was breaking the build (#18804) * Fix flaky test (#18805) * register traffic permission and workload identity types (#18704) * add workload identity and traffic permission protos * register new types * add generated pb code * fix exports.go path * add proto newlines * fix type name Co-authored-by: Eric Haberkorn <erichaberkorn@gmail.com> * address review * fix protos and add tests * fix validation constraints * add tests --------- Co-authored-by: Eric Haberkorn <erichaberkorn@gmail.com> * [NET-5329] use acl templated policy under the hood for node/service identities (#18813) * [NET-5334] Added CLI commands for templated policies (#18816) * mesh: Wire ComputedRoutes into the ProxyStateTemplate via the sidecar controller (#18752) Reworks the sidecar controller to accept ComputedRoutes as an input and use it to generate appropriate ProxyStateTemplate resources containing L4/L7 mesh configuration. * mesh: call the right mapper (#18818) * fix templated policy cli output (#18821) * resource: default peername to "local" for now (#18822) * Wire up traffic permissions (#18812) Wire up traffic permissions * 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 <michael.zalimeni@hashicorp.com> --------- Co-authored-by: Michael Zalimeni <michael.zalimeni@hashicorp.com> * Default to tcp protocol when workload protocol is unspecified (#18824) * 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 * Use embedded strings for templated policies (#18829) * consul operator raft transfer-leader should send the id (#17107) Fixes #16955 Co-authored-by: Dhia Ayachi <dhia@hashicorp.com> * Allow empty data writes for resources (#18819) * allow nil data writes for resources * update demo to test valid type with no data * Fix nondeterministic test (#18828) * mesh: prevent writing a ComputedRoutes with no ported configs (#18833) * catalog: Default protocol to tcp in catalog.Service if unspecified (#18832) * Adjust metrics test (#18837) * Audit log consul 1.17x changes (#18669) * audit log consul 1.17x changes * added some details * verbose * 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 * 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 * 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 * docs: Fix typo in description for server_addresses (#18838) Change 'If not port' to 'If no port'. Resolves #18553 * grafana: display connected consul-dataplanes (#18842) * 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 <blake.covarrubias@gmail.com> * 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 <blake.covarrubias@gmail.com> * 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. * 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 * Move ACL templated policies to hcl files (#18853) * mesh: update xds controller to synthesize empty endpoints when no endpoints ref is found (#18835) * 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. * resource: mutate and validate before acls on write (#18868) * Remove flaky test assertions (#18870) * 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 * NET-4519 Collecting journald logs in "consul debug" bundle (#18797) * debug since * fix docs * chagelog added * fix go mod * debug test fix * fix test * tabs test fix * Update .changelog/18797.txt Co-authored-by: Ganesh S <ganesh.seetharaman@hashicorp.com> --------- Co-authored-by: Ganesh S <ganesh.seetharaman@hashicorp.com> * CI: lint test-integ (#18875) * CI: lint test-integ * fix lint error * Honor Default Traffic Permissions in V2 (#18886) wire up v2 default traffic permissions * docs: Fix HCL, JSON, and YAML syntax errors (#18879) This commit fixes syntax errors in HCL, JSON, and YAML example configurations. In some cases, it replaces the code example with the proper format for the code block. Also fixes HCL formatting and misc opportunistic updates to codeblock. Co-authored-by: Tu Nguyen <im2nguyen@gmail.com> * Fix code block examples on dns-static-lookups.mdx (#18880) HCL and JSON configuration examples were being displayed in the same code block. This commit separates the configurations to properly display them as independent configuration examples. * docs: Fix Kubernetes CRD example configs (#18878) Fixes configuration examples for several Consul Kubernetes CRDs. The CRDs were missing required fields such as `apiVersion`, `metadata`, and `spec`. Co-authored-by: Tu Nguyen <im2nguyen@gmail.com> * docs: Apigee extension backport (#18847) * commit * link text edits * Add operator audit endpoint changes (#18899) * docs: Remove YAML service registration examples (#18877) Remove YAML service registration examples and replace them with JSON. This is because YAML is not a supported configuration format for the Consul's agent configuration, nor is it supported by the HTTP API. This commit replaces the YAML examples with JSON and adds additional JSON examples where they were missing. * Add otel-access-logging Envoy extension integration test (#18898) * Add note about service upstream env var dot broken (#18895) * add note about service upstream env var dot broken * Apply suggestions from code review Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com> --------- Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com> * xds: Use downstream protocol when connecting to local app (#18573) Configure Envoy to use the same HTTP protocol version used by the downstream caller when forwarding requests to a local application that is configured with the protocol set to either `http2` or `grpc`. This allows upstream applications that support both HTTP/1.1 and HTTP/2 on a single port to receive requests using either protocol. This is beneficial when the application primarily communicates using HTTP/2, but also needs to support HTTP/1.1, such as to respond to Kubernetes HTTP readiness/liveness probes. Co-authored-by: Derek Menteer <derek.menteer@hashicorp.com> * resource: ensure resource.AuthorizerContext properly strips the local… (#18908) resource: ensure resource.AuthorizerContext properly strips the local peer name * docs: Add complete auth method payloads (#18849) This commit modifies the example payloads for various auth methods to remove 'other fields' and instead use complete example payloads. * agent/config: prevent startup if resource-apis experiment and cloud are enabled (#18876) * v2: various fixes to make K8s tproxy multiport acceptance tests and manual explicit upstreams (single port) tests pass (#18874) Adding coauthors who mobbed/paired at various points throughout last week. Co-authored-by: Dan Stough <dan.stough@hashicorp.com> Co-authored-by: Iryna Shustava <iryna@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com> Co-authored-by: Michael Zalimeni <michael.zalimeni@hashicorp.com> Co-authored-by: Ashwin Venkatesh <ashwin@hashicorp.com> Co-authored-by: Michael Wilkerson <mwilkerson@hashicorp.com> * Added changelog entries for 1.14.10, 1.15.6, 1.16.2 (#18917) * [NET-5332] Add nomad server templated policy (#18888) * [NET-5332] Add nomad server templated policy * slksfd * mesh: make FailoverPolicy work in xdsv2 and ProxyStateTemplate (#18900) Ensure that configuring a FailoverPolicy for a service that is reachable via a xRoute or a direct upstream causes an envoy aggregate cluster to be created for the original cluster name, but with separate clusters for each one of the possible destinations. * Fix changelog order (#18918) * Fix changelog order * fix ordering or entries * Add namespace proto and registration (#18848) * add namespace proto and registration * fix proto generation * add missing copywrite headers * fix proto linter errors * fix exports and Type export * add mutate hook and more validation * add more validation rules and tests * Apply suggestions from code review Co-authored-by: Semir Patel <semir.patel@hashicorp.com> * fix owner error and add test * remove ACL for now * add tests around space suffix prefix. * only fait when ns and ap are default, add test for it --------- Co-authored-by: Semir Patel <semir.patel@hashicorp.com> * Update base ref property name (#18851) * Update base ref property name * Test skip ci (#18924) test_push_merge * cleanup test push code * dns token (#17936) * dns token fix whitespace for docs and comments fix test cases fix test cases remove tabs in help text Add changelog Peering dns test Peering dns test Partial implementation of Peered DNS test Swap to new topology lib expose dns port for integration tests on client remove partial test implementation remove extra port exposure remove changelog from the ent pr Add dns token to set-agent-token switch Add enterprise golden file Use builtin/dns template in tests Update ent dns policy Update ent dns template test remove local gen certs fix templated policy specs * add changelog * go mod tidy * NET-5131 - support multiple ported upstreams tests (#18923) * add multiple upstream ports to golden file test for destination builder * NET-5131 - add unit tests for multiple ported upstreams * fix merge conflicts * Fix for loop in filter_changed_files_go_test script (#18931) * iterate through array * remove comment * Fix create dns token docs (#18927) * [NET-5589] Optimize leaf watch diff on xds controller. (#18921) Optimize leaf watch diff on xds controller. * NET-4884 - Terminating gateway tests for namespaces & partitions (#18820) * Add gateway test to CE * Fix V2 Wildcard RBAC Regular Expressions (#18941) fix wildcard rbac regular expressions * skip flaky test (#18949) * docs: Fix invalid JSON in code examples (#18932) This commit fixes invalid JSON in various code examples. * do not trigger integration tests (#18948) * Dump response body on fail (#18962) * Add protoc generator to emit resource type variables (#18957) The annotations include a little more data than is strictly necessary because we will also have a protoc generator for openapi output. * docs: Change heading to filename in CodeBlockConfig (#18951) Change various CodeBlockConfig objects to use the `filename` attribute instead of `heading` when the code block references a named file. * docs: Replace unicode quotes with ASCII quotes (#18950) Replaces unicode quotation marks with ASCII quotation marks. For code examples, this fixes HCL decoding errors that would otherwise be raised when attempting to read the file. * net-5689 fix disabling panic threshold logic (#18958) * tenancy: split up tenancy `types.go` into CE version (#18966) * [NET-5589] Add jitter to xds v2 leaf cert watches (#18940) Add jitter to xds v2 leaf cert watches. * Fix docs for log file name changes (#18913) * fix docs * Update website/content/docs/agent/config/cli-flags.mdx Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> * Update website/content/docs/agent/config/cli-flags.mdx Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> * Update website/content/docs/agent/config/cli-flags.mdx Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> --------- Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> * resource: allow for the ACLs.Read hook to request the entire data payload to perform the authz check (#18925) The ACLs.Read hook for a resource only allows for the identity of a resource to be passed in for use in authz consideration. For some resources we wish to allow for the current stored value to dictate how to enforce the ACLs (such as reading a list of applicable services from the payload and allowing service:read on any of them to control reading the enclosing resource). This change update the interface to usually accept a *pbresource.ID, but if the hook decides it needs more data it returns a sentinel error and the resource service knows to defer the authz check until after fetching the data from storage. * catalog: add ACL checks for FailoverPolicy resources (#18919) FailoverPolicy resources are name-aligned with the Service they control. They also contain a list of possible failover destinations that are References to other Services. The ACLs should be: - list: (default) - read: service:<resource_name>:read - write: service:<resource_name>:write + service:<destination_name>:read (for any destination) * catalog: fix for new method argument (#18978) * catalog,mesh,auth: Bump versions to v2beta1 (#18930) * [Docs] Add note to jwt docs to specify the need for ACLs (#18942) * remove now orphaned generated v1alpha1 pb.go files (#18982) * peerstream: fix flaky test related to autopilot integration (#18979) * mesh: add ACL checks for DestinationPolicy resources (#18920) DestinationPolicy resources are name-aligned with the Service they control. The ACLs should be: - list: (default) - read: service:<resource_name>:read - write: service:<resource_name>:write * mesh: add ACL checks for xRoute resources (#18926) xRoute resources are not name-aligned with the Services they control. They have a list of "parent ref" services that they alter traffic flow for, and they contain a list of "backend ref" services that they direct that traffic to. The ACLs should be: - list: (default) - read: - ALL service:<parent_ref_service>:read - write: - ALL service:<parent_ref_service>:write - ALL service:<backend_ref_service>:read * Traffic Permissions Validations (#18907) add TP validations and mutation and add CTP validations * feat: remove resource api client from api module (#18984) * feat: remove resource api client from api module * fix: go mod clean up * mesh: store bound reference pointers on a ComputedRoutes resource and use during reconcile (#18965) xRoute resource types contain a slice of parentRefs to services that they manipulate traffic for. All xRoutes that have a parentRef to given Service will be merged together to generate a ComputedRoutes resource name-aligned with that Service. This means that a write of an xRoute with 2 parent ref pointers will cause at most 2 reconciles for ComputedRoutes. If that xRoute's list of parentRefs were ever to be reduced, or otherwise lose an item, that subsequent map event will only emit events for the current set of refs. The removed ref will not cause the generated ComputedRoutes related to that service to be re-reconciled to omit the influence of that xRoute. To combat this, we will store on the ComputedRoutes resource a BoundResources []*pbresource.Reference field with references to all resources that were used to influence the generated output. When the routes controller reconciles, it will use a bimapper to index this influence, and the dependency mappers for the xRoutes will look themselves up in that index to discover additional (former) ComputedRoutes that need to be notified as well. * Add CLI support for json (#18991) * add cli support for json format * add tests for json parsing * make owner and id pointers. * add copyright header * remove print --------- Co-authored-by: Poonam Jadhav <poonam.jadhav@hashicorp.com> * mesh: compute more of the xRoute features into ComputedRoutes (#18980) Convert more of the xRoutes features that were skipped in an earlier PR into ComputedRoutes and make them work: - DestinationPolicy defaults - more timeouts - load balancer policy - request/response header mutations - urlrewrite - GRPCRoute matches * catalog,mesh,auth: Move resource types to the proto-public module (#18935) * mesh: update various protobuf comments for mesh types (#18993) * docs: add changelog (#18994) * api: add Token field to ServiceRegisterOpts (#18983) Ongoing work to support Nomad Workload Identity for authenticating with Consul will mean that Nomad's service registration sync with Consul will want to use Consul tokens scoped to individual workloads for registering services and checks. The `ServiceRegisterOpts` type in the API doesn't have an option to pass the token in, which prevent us from sharing the same Consul connection for all workloads. Add a `Token` field to match the behavior of `ServiceDeregisterOpts`. * api: add `CheckRegisterOpts` method to Agent API (#18943) Ongoing work to support Nomad Workload Identity for authenticating with Consul will mean that Nomad's service registration sync with Consul will want to use Consul tokens scoped to individual workloads for registering services and checks. The `CheckRegister` method in the API doesn't have an option to pass the token in, which prevent us from sharing the same Consul connection for all workloads. Add a `CheckRegisterOpts` to match the behavior of `ServiceRegisterOpts`. * grafana: fix a query metrics from ent and add consul version (#18998) * v2 explicit destination traffic permissions (#18823) * workload identity boilerplate * notes from discussion with Iryna * WIP traffic permissions controller poc * workload identity, traffic permissions validation, errors, types * traffic permissions mapper framing, traffic permissions controller updates. * more roughing out of the controller * cleanup * controller and mapper logic * tests * refactor mapper logic, add tests * clean up tenancy and integration test stubs * consolidate mapping * cleanup cache leak, revert bimapper changes * address review comments * test fix and rebase * use resource helper --------- Co-authored-by: John Landa <john.landa@hashicorp.com> * mesh: rename Upstreams and UpstreamsConfiguration to Destinations* (#18995) * auth: register auth controllers with the server (#19000) * acl: default tenancy with the no-auth ACL resolver (#19006) When using the no-auth acl resolver (the case for most controllers and the get-envoy-boostrap-params endpoint), ResolveTokenAndDefaultMeta method only returns an acl resolver. However, the resource service relies on the ent meta to be filled in to do the tenancy defaulting and inheriting it from the token when one is present. So this change makes sure that the ent meta defaulting always happens in the ACL resolver. * NET-5657 - consul-containers test for explicit upstreams (#18952) * Explicit container test * remove static resources * fix passing serviceBindPorts * WIP * fix explicit upstream test * use my image in CI until dataplane is fixed. * gofmt * fixing reference to v2beta1 in test-containers * WIP * remove bad references * add missing license headers * allow access internal/resource/resourcetest * fix check-allowed-imports to append array items * use preview image for dataplane * revert some inadverntent comment updates in peering_topology * add building local consul-dataplane image to compatibility-tests CI * fix substitution in CI * change upstreams to destinations based on incoming change * fixing use of upstreams in resource update * remove commented out lines and enable envoy concurrency on dataplane. * changes to addess PR feedback * small fixes --------- Co-authored-by: Eric <eric@haberkorn.co> * Skip the catalog v2 upgrade test (#19005) We intentionally broke api compatibility here as we are not yet maintaining backwards compat for the v2 apis * Fix type of datacenter in Service Resolvers Config Entry (#19004) * docs: Rename Consul OSS to Consul CE (#19009) Rename references of Consul OSS to Consul Community Edition (CE). Co-authored-by: Tu Nguyen <im2nguyen@gmail.com> * Update release notes to indicate folks should upgrade to to 1.16.2 or later (#19002) * Update v1_16_x.mdx * Update upgrade-specific.mdx * Update website/content/docs/upgrading/upgrade-specific.mdx Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> * Handle Traffic Permissions With Empty Sources Properly (#19024) Fix issues with empty sources * Validate that each permission on traffic permissions resources has at least one source. * Don't construct RBAC policies when there aren't any principals. This resulted in Envoy rejecting xDS updates with a validation error. ``` error= | rpc error: code = Internal desc = Error adding/updating listener(s) public_listener: Proto constraint validation failed (RBACValidationError.Rules: embedded message failed validation | caused by RBACValidationError.Policies[consul-intentions-layer4-1]: embedded message failed validation | caused by PolicyValidationError.Principals: value must contain at least 1 item(s)): rules { ``` * Include RequestTimeout in marshal/unmarshal of ServiceResolverConfigEntry * backport of commit 4ad1a8760d1276ec2f2ce7b9be741badb7abb1f6 * backport of commit 9109bdd79163dbdda4ef1e7dc7e990ba2cab8858 --------- Co-authored-by: Poonam Jadhav <poonam.jadhav@hashicorp.com> Co-authored-by: Matt Keeler <mkeeler@users.noreply.github.com> Co-authored-by: Daniel Upton <daniel@floppy.co> Co-authored-by: wangxinyi7 <121973291+wangxinyi7@users.noreply.github.com> Co-authored-by: Luke Kysow <1034429+lkysow@users.noreply.github.com> Co-authored-by: Anita Akaeze <anita.akaeze@hashicorp.com> Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com> Co-authored-by: Semir Patel <semir.patel@hashicorp.com> Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Co-authored-by: Dan Bond <danbond@protonmail.com> Co-authored-by: David Yu <dyu@hashicorp.com> Co-authored-by: Nathan Coleman <nathan.coleman@hashicorp.com> Co-authored-by: Blake Covarrubias <blake@covarrubi.as> Co-authored-by: Michael Zalimeni <michael.zalimeni@hashicorp.com> Co-authored-by: John Murret <john.murret@hashicorp.com> Co-authored-by: Ashwin Venkatesh <ashwin@hashicorp.com> Co-authored-by: Iryna Shustava <ishustava@users.noreply.github.com> Co-authored-by: cskh <hui.kang@hashicorp.com> Co-authored-by: Tu Nguyen <im2nguyen@users.noreply.github.com> Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> Co-authored-by: R.B. Boyer <4903+rboyer@users.noreply.github.com> Co-authored-by: Vijay <vijayraghav22@gmail.com> Co-authored-by: Dan Stough <dan.stough@hashicorp.com> Co-authored-by: Natalie Smith <nataliemegans@gmail.com> Co-authored-by: Ashesh Vidyut <134911583+absolutelightning@users.noreply.github.com> Co-authored-by: Chris S. Kim <ckim@hashicorp.com> Co-authored-by: John Maguire <john.maguire@hashicorp.com> Co-authored-by: Curt Bushko <cbushko@gmail.com> Co-authored-by: Jared Kirschner <85913323+jkirschner-hashicorp@users.noreply.github.com> Co-authored-by: Joshua Timmons <josh.timmons@hashicorp.com> Co-authored-by: Hardik Shingala <34568645+hdkshingala@users.noreply.github.com> Co-authored-by: Ashvitha <ashvitha.sridharan@hashicorp.com> Co-authored-by: Dhia Ayachi <dhia@hashicorp.com> Co-authored-by: Derek Menteer <105233703+hashi-derek@users.noreply.github.com> Co-authored-by: Valeriia Ruban <valeriia.ruban@hashicorp.com> Co-authored-by: skpratt <sarah.pratt@hashicorp.com> Co-authored-by: Phil Porada <pgporada@users.noreply.github.com> Co-authored-by: Samantha <hello@entropy.cat> Co-authored-by: Nick Irvine <115657443+nfi-hashicorp@users.noreply.github.com> Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com> Co-authored-by: Ronald <roncodingenthusiast@users.noreply.github.com> Co-authored-by: Gerard Nguyen <gerard@hashicorp.com> Co-authored-by: Melisa Griffin <missylbytes@users.noreply.github.com> Co-authored-by: Matt Keeler <mjkeeler7@gmail.com> Co-authored-by: Jeremy Jacobson <jjacobson93@users.noreply.github.com> Co-authored-by: Iryna Shustava <iryna@hashicorp.com> Co-authored-by: R.B. Boyer <rb@hashicorp.com> Co-authored-by: Chris Thain <32781396+cthain@users.noreply.github.com> Co-authored-by: Sophie Gairo <97480023+sophie-gairo@users.noreply.github.com> Co-authored-by: Eric Haberkorn <erichaberkorn@gmail.com> Co-authored-by: sarahalsmiller <100602640+sarahalsmiller@users.noreply.github.com> Co-authored-by: James Hartig <me@jameshartig.com> Co-authored-by: Gautam <gautambaghel93@gmail.com> Co-authored-by: Blake Covarrubias <blake.covarrubias@gmail.com> Co-authored-by: Andrew Stucki <andrew.stucki@hashicorp.com> Co-authored-by: Ganesh S <ganesh.seetharaman@hashicorp.com> Co-authored-by: Tu Nguyen <im2nguyen@gmail.com> Co-authored-by: Derek Menteer <derek.menteer@hashicorp.com> Co-authored-by: Nick Ethier <nethier@hashicorp.com> Co-authored-by: John Landa <jonathanlanda@gmail.com> Co-authored-by: Tim Gross <tgross@hashicorp.com> Co-authored-by: John Landa <john.landa@hashicorp.com> Co-authored-by: Eric <eric@haberkorn.co> Co-authored-by: Chris Thain <chris.m.thain@gmail.com>
2023-09-29 17:57:36 +00:00
{
name: "bad request timeout",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
RequestTimeout: -1 * time.Second,
},
validateErr: "Bad RequestTimeout",
},
}
// Bulk add a bunch of similar validation cases.
for _, invalidSubset := range invalidSubsetNames {
tc := testcase{
name: "invalid subset name: " + invalidSubset,
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Subsets: map[string]ServiceResolverSubset{
invalidSubset: {OnlyPassing: true},
},
},
validateErr: fmt.Sprintf("Subset %q is invalid", invalidSubset),
}
cases = append(cases, tc)
}
for _, goodSubset := range validSubsetNames {
tc := testcase{
name: "valid subset name: " + goodSubset,
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Subsets: map[string]ServiceResolverSubset{
goodSubset: {OnlyPassing: true},
},
},
}
cases = append(cases, tc)
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
err := tc.entry.Normalize()
if tc.normalizeErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.normalizeErr)
return
}
require.NoError(t, err)
if tc.check != nil {
tc.check(t, tc.entry)
}
err = tc.entry.Validate()
if tc.validateErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.validateErr)
return
}
require.NoError(t, err)
})
}
}
2020-08-23 00:05:09 +00:00
func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
type testcase struct {
name string
entry *ServiceResolverConfigEntry
normalizeErr string
validateErr string
// check is called between normalize and validate
check func(t *testing.T, entry *ServiceResolverConfigEntry)
}
cases := []testcase{
{
name: "empty policy is valid",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: "",
},
2020-08-23 00:05:09 +00:00
},
},
{
name: "supported policy",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyRandom,
},
2020-08-23 00:05:09 +00:00
},
},
{
name: "unsupported policy",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: "fake-policy",
},
2020-08-23 00:05:09 +00:00
},
validateErr: `"fake-policy" is not supported`,
},
{
name: "bad policy for least request config",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyRingHash,
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 10},
2020-08-23 00:05:09 +00:00
},
},
validateErr: `LeastRequestConfig specified for incompatible load balancing policy`,
},
{
name: "bad policy for ring hash config",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyLeastRequest,
RingHashConfig: &RingHashConfig{MinimumRingSize: 1024},
2020-08-23 00:05:09 +00:00
},
},
validateErr: `RingHashConfig specified for incompatible load balancing policy`,
},
{
name: "good policy for ring hash config",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyRingHash,
RingHashConfig: &RingHashConfig{MinimumRingSize: 1024},
2020-08-23 00:05:09 +00:00
},
},
},
{
name: "good policy for least request config",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyLeastRequest,
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 2},
2020-08-23 00:05:09 +00:00
},
},
},
{
name: "empty policy is not defaulted",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: "",
2020-08-23 00:05:09 +00:00
},
},
check: func(t *testing.T, entry *ServiceResolverConfigEntry) {
2020-09-11 15:21:43 +00:00
require.Equal(t, "", entry.LoadBalancer.Policy)
2020-08-23 00:05:09 +00:00
},
},
{
name: "empty policy with hash policy",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: "",
HashPolicies: []HashPolicy{
{
SourceIP: true,
2020-08-23 00:05:09 +00:00
},
},
},
},
validateErr: `HashPolicies specified for non-hash-based Policy`,
},
{
2020-09-12 00:34:03 +00:00
name: "cookie config with header policy",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: HashPolicyHeader,
FieldValue: "x-user-id",
CookieConfig: &CookieConfig{
TTL: 10 * time.Second,
Path: "/root",
},
},
},
},
},
validateErr: `cookie_config provided for "header"`,
},
{
2020-09-12 00:34:03 +00:00
name: "cannot generate session cookie with ttl",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: HashPolicyCookie,
FieldValue: "good-cookie",
CookieConfig: &CookieConfig{
Session: true,
TTL: 10 * time.Second,
},
},
},
},
},
validateErr: `a session cookie cannot have an associated TTL`,
},
{
name: "valid cookie policy",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: HashPolicyCookie,
FieldValue: "good-cookie",
CookieConfig: &CookieConfig{
TTL: 10 * time.Second,
Path: "/oven",
},
},
},
},
},
},
2020-08-23 00:05:09 +00:00
{
name: "supported match field",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: "header",
FieldValue: "X-Consul-Token",
2020-08-23 00:05:09 +00:00
},
},
},
},
},
{
name: "unsupported match field",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: "fake-field",
2020-08-23 00:05:09 +00:00
},
},
},
},
validateErr: `"fake-field" is not a supported field`,
2020-08-23 00:05:09 +00:00
},
{
name: "cannot match on source address and custom field",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: "header",
SourceIP: true,
2020-08-23 00:05:09 +00:00
},
},
},
},
validateErr: `A single hash policy cannot hash both a source address and a "header"`,
},
{
name: "matchvalue not compatible with source address",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
FieldValue: "X-Consul-Token",
SourceIP: true,
2020-08-23 00:05:09 +00:00
},
},
},
},
validateErr: `A FieldValue cannot be specified when hashing SourceIP`,
2020-08-23 00:05:09 +00:00
},
{
name: "field without match value",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: "header",
2020-08-23 00:05:09 +00:00
},
},
},
},
validateErr: `Field "header" was specified without a FieldValue`,
2020-08-23 00:05:09 +00:00
},
{
name: "field without match value",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
FieldValue: "my-cookie",
2020-08-23 00:05:09 +00:00
},
},
},
},
validateErr: `FieldValue requires a Field to apply to`,
2020-08-23 00:05:09 +00:00
},
{
name: "ring hash kitchen sink",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyRingHash,
RingHashConfig: &RingHashConfig{MaximumRingSize: 10, MinimumRingSize: 2},
HashPolicies: []HashPolicy{
{
Field: "cookie",
FieldValue: "my-cookie",
},
{
Field: "header",
FieldValue: "alt-header",
Terminal: true,
2020-08-23 00:05:09 +00:00
},
},
},
},
},
{
name: "least request kitchen sink",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
2020-09-11 15:21:43 +00:00
Policy: LBPolicyLeastRequest,
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 20},
2020-08-23 00:05:09 +00:00
},
},
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
err := tc.entry.Normalize()
if tc.normalizeErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.normalizeErr)
return
}
require.NoError(t, err)
if tc.check != nil {
tc.check(t, tc.entry)
}
err = tc.entry.Validate()
if tc.validateErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.validateErr)
return
}
require.NoError(t, err)
})
}
}
func TestServiceSplitterConfigEntry(t *testing.T) {
makesplitter := func(splits ...ServiceSplit) *ServiceSplitterConfigEntry {
return &ServiceSplitterConfigEntry{
Kind: ServiceSplitter,
Name: "test",
Splits: splits,
}
}
makesplit := func(weight float32, service, serviceSubset, namespace string) ServiceSplit {
return ServiceSplit{
Weight: weight,
Service: service,
ServiceSubset: serviceSubset,
Namespace: namespace,
}
}
for _, tc := range []struct {
name string
entry *ServiceSplitterConfigEntry
normalizeErr string
validateErr string
// check is called between normalize and validate
check func(t *testing.T, entry *ServiceSplitterConfigEntry)
}{
{
name: "nil",
entry: nil,
normalizeErr: "config entry is nil",
},
{
name: "no name",
entry: &ServiceSplitterConfigEntry{},
validateErr: "Name is required",
},
{
name: "empty",
entry: makesplitter(),
validateErr: "no splits configured",
},
{
name: "1 split",
entry: makesplitter(
makesplit(100, "test", "", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(100), entry.Splits[0].Weight)
},
},
{
name: "1 split not enough weight",
entry: makesplitter(
makesplit(99.99, "test", "", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(99.99), entry.Splits[0].Weight)
},
validateErr: "the sum of all split weights must be 100",
},
{
name: "1 split too much weight",
entry: makesplitter(
makesplit(100.01, "test", "", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(100.01), entry.Splits[0].Weight)
},
validateErr: "the sum of all split weights must be 100",
},
{
name: "2 splits",
entry: makesplitter(
makesplit(99, "test", "v1", ""),
makesplit(1, "test", "v2", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(99), entry.Splits[0].Weight)
require.Equal(t, float32(1), entry.Splits[1].Weight)
},
},
{
name: "2 splits - rounded up to smallest units",
entry: makesplitter(
makesplit(99.999, "test", "v1", ""),
makesplit(0.001, "test", "v2", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(100), entry.Splits[0].Weight)
require.Equal(t, float32(0), entry.Splits[1].Weight)
},
},
{
name: "2 splits not enough weight",
entry: makesplitter(
makesplit(99.98, "test", "v1", ""),
makesplit(0.01, "test", "v2", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(99.98), entry.Splits[0].Weight)
require.Equal(t, float32(0.01), entry.Splits[1].Weight)
},
validateErr: "the sum of all split weights must be 100",
},
{
name: "2 splits too much weight",
entry: makesplitter(
makesplit(100, "test", "v1", ""),
makesplit(0.01, "test", "v2", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(100), entry.Splits[0].Weight)
require.Equal(t, float32(0.01), entry.Splits[1].Weight)
},
validateErr: "the sum of all split weights must be 100",
},
{
name: "3 splits",
entry: makesplitter(
makesplit(34, "test", "v1", ""),
makesplit(33, "test", "v2", ""),
makesplit(33, "test", "v3", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(34), entry.Splits[0].Weight)
require.Equal(t, float32(33), entry.Splits[1].Weight)
require.Equal(t, float32(33), entry.Splits[2].Weight)
},
},
{
name: "3 splits one duplicated same weights",
entry: makesplitter(
makesplit(34, "test", "v1", ""),
makesplit(33, "test", "v2", ""),
makesplit(33, "test", "v2", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(34), entry.Splits[0].Weight)
require.Equal(t, float32(33), entry.Splits[1].Weight)
require.Equal(t, float32(33), entry.Splits[2].Weight)
},
validateErr: "split destination occurs more than once",
},
{
name: "3 splits one duplicated diff weights",
entry: makesplitter(
makesplit(34, "test", "v1", ""),
makesplit(33, "test", "v2", ""),
makesplit(33, "test", "v1", ""),
),
check: func(t *testing.T, entry *ServiceSplitterConfigEntry) {
require.Equal(t, float32(34), entry.Splits[0].Weight)
require.Equal(t, float32(33), entry.Splits[1].Weight)
require.Equal(t, float32(33), entry.Splits[2].Weight)
},
validateErr: "split destination occurs more than once",
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
err := tc.entry.Normalize()
if tc.normalizeErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.normalizeErr)
return
}
require.NoError(t, err)
if tc.check != nil {
tc.check(t, tc.entry)
}
err = tc.entry.Validate()
if tc.validateErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.validateErr)
return
}
require.NoError(t, err)
})
}
}
func TestServiceSplitMergeParent(t *testing.T) {
type testCase struct {
name string
split, parent, want *ServiceSplit
wantErr string
}
run := func(t *testing.T, tc testCase) {
got, err := tc.split.MergeParent(tc.parent)
if tc.wantErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.wantErr)
} else {
require.NoError(t, err)
require.Equal(t, tc.want, got)
}
}
testCases := []testCase{
{
name: "all header manip fields set",
split: &ServiceSplit{
Weight: 50.0,
Service: "foo",
RequestHeaders: &HTTPHeaderModifiers{
Add: map[string]string{
"child-only": "1",
"both-want-child": "2",
},
Set: map[string]string{
"child-only": "3",
"both-want-child": "4",
},
Remove: []string{"child-only-req", "both-req"},
},
ResponseHeaders: &HTTPHeaderModifiers{
Add: map[string]string{
"child-only": "5",
"both-want-parent": "6",
},
Set: map[string]string{
"child-only": "7",
"both-want-parent": "8",
},
Remove: []string{"child-only-resp", "both-resp"},
},
},
parent: &ServiceSplit{
Weight: 25.0,
Service: "bar",
RequestHeaders: &HTTPHeaderModifiers{
Add: map[string]string{
"parent-only": "9",
"both-want-child": "10",
},
Set: map[string]string{
"parent-only": "11",
"both-want-child": "12",
},
Remove: []string{"parent-only-req", "both-req"},
},
ResponseHeaders: &HTTPHeaderModifiers{
Add: map[string]string{
"parent-only": "13",
"both-want-parent": "14",
},
Set: map[string]string{
"parent-only": "15",
"both-want-parent": "16",
},
Remove: []string{"parent-only-resp", "both-resp"},
},
},
want: &ServiceSplit{
Weight: 50.0,
Service: "foo",
RequestHeaders: &HTTPHeaderModifiers{
Add: map[string]string{
"child-only": "1",
"both-want-child": "2",
"parent-only": "9",
},
Set: map[string]string{
"child-only": "3",
"both-want-child": "4",
"parent-only": "11",
},
Remove: []string{"parent-only-req", "both-req", "child-only-req"},
},
ResponseHeaders: &HTTPHeaderModifiers{
Add: map[string]string{
"child-only": "5",
"parent-only": "13",
"both-want-parent": "14",
},
Set: map[string]string{
"child-only": "7",
"parent-only": "15",
"both-want-parent": "16",
},
Remove: []string{"child-only-resp", "both-resp", "parent-only-resp"},
},
},
},
{
name: "no header manip",
split: &ServiceSplit{
Weight: 50,
Service: "foo",
},
parent: &ServiceSplit{
Weight: 50,
Service: "bar",
},
want: &ServiceSplit{
Weight: 50,
Service: "foo",
},
},
{
name: "nil parent",
split: &ServiceSplit{
Weight: 50,
Service: "foo",
},
parent: nil,
want: &ServiceSplit{
Weight: 50,
Service: "foo",
},
},
{
name: "nil child",
split: nil,
parent: &ServiceSplit{
Weight: 50,
Service: "foo",
},
want: &ServiceSplit{
Weight: 50,
Service: "foo",
},
},
{
name: "both nil",
split: nil,
parent: nil,
want: nil,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
run(t, tc)
})
}
}
func TestServiceRouterConfigEntry(t *testing.T) {
httpMatch := func(http *ServiceRouteHTTPMatch) *ServiceRouteMatch {
return &ServiceRouteMatch{HTTP: http}
}
httpMatchHeader := func(headers ...ServiceRouteHTTPMatchHeader) *ServiceRouteMatch {
return httpMatch(&ServiceRouteHTTPMatch{
Header: headers,
})
}
httpMatchParam := func(params ...ServiceRouteHTTPMatchQueryParam) *ServiceRouteMatch {
return httpMatch(&ServiceRouteHTTPMatch{
QueryParam: params,
})
}
toService := func(svc string) *ServiceRouteDestination {
return &ServiceRouteDestination{Service: svc}
}
routeMatch := func(match *ServiceRouteMatch) ServiceRoute {
return ServiceRoute{
Match: match,
Destination: toService("other"),
}
}
makerouter := func(routes ...ServiceRoute) *ServiceRouterConfigEntry {
return &ServiceRouterConfigEntry{
Kind: ServiceRouter,
Name: "test",
Routes: routes,
}
}
type testcase struct {
name string
entry *ServiceRouterConfigEntry
normalizeErr string
validateErr string
// check is called between normalize and validate
check func(t *testing.T, entry *ServiceRouterConfigEntry)
}
cases := []testcase{
{
name: "nil",
entry: nil,
normalizeErr: "config entry is nil",
},
{
name: "no name",
entry: &ServiceRouterConfigEntry{},
validateErr: "Name is required",
},
{
name: "empty",
entry: makerouter(),
},
{
name: "1 empty route",
entry: makerouter(
ServiceRoute{},
),
},
{
name: "route with path exact",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
PathExact: "/exact",
}))),
},
{
name: "route with bad path exact",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
PathExact: "no-leading-slash",
}))),
validateErr: "PathExact doesn't start with '/'",
},
{
name: "route with path prefix",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
PathPrefix: "/prefix",
}))),
},
{
name: "route with bad path prefix",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
PathPrefix: "no-leading-slash",
}))),
validateErr: "PathPrefix doesn't start with '/'",
},
{
name: "route with path regex",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
PathRegex: "/regex",
}))),
},
{
name: "route with path exact and prefix",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
PathExact: "/exact",
PathPrefix: "/prefix",
}))),
validateErr: "should only contain at most one of PathExact, PathPrefix, or PathRegex",
},
{
name: "route with path exact and regex",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
PathExact: "/exact",
PathRegex: "/regex",
}))),
validateErr: "should only contain at most one of PathExact, PathPrefix, or PathRegex",
},
{
name: "route with path prefix and regex",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
PathPrefix: "/prefix",
PathRegex: "/regex",
}))),
validateErr: "should only contain at most one of PathExact, PathPrefix, or PathRegex",
},
{
name: "route with path exact, prefix, and regex",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
PathExact: "/exact",
PathPrefix: "/prefix",
PathRegex: "/regex",
}))),
validateErr: "should only contain at most one of PathExact, PathPrefix, or PathRegex",
},
{
name: "route with no name header",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Present: true,
}))),
validateErr: "missing required Name field",
},
{
name: "route with header present",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Present: true,
}))),
},
{
name: "route with header not present",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Present: true,
Invert: true,
}))),
},
{
name: "route with header exact",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Exact: "bar",
}))),
},
{
name: "route with header regex",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Regex: "bar",
}))),
},
{
name: "route with header prefix",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Prefix: "bar",
}))),
},
{
name: "route with header suffix",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Suffix: "bar",
}))),
},
{
name: "route with header present and exact",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Present: true,
Exact: "bar",
}))),
validateErr: "should only contain one of Present, Exact, Prefix, Suffix, or Regex",
},
{
name: "route with header present and regex",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Present: true,
Regex: "bar",
}))),
validateErr: "should only contain one of Present, Exact, Prefix, Suffix, or Regex",
},
{
name: "route with header present and prefix",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Present: true,
Prefix: "bar",
}))),
validateErr: "should only contain one of Present, Exact, Prefix, Suffix, or Regex",
},
{
name: "route with header present and suffix",
entry: makerouter(routeMatch(httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Present: true,
Suffix: "bar",
}))),
validateErr: "should only contain one of Present, Exact, Prefix, Suffix, or Regex",
},
// NOTE: Some combinatoric cases for header operators (some 5 choose 2,
// all 5 choose 3, all 5 choose 4, all 5 choose 5) are omitted from
// testing.
////////////////
{
name: "route with no name query param",
entry: makerouter(routeMatch(httpMatchParam(ServiceRouteHTTPMatchQueryParam{
Exact: "foo",
}))),
validateErr: "missing required Name field",
},
{
name: "route with query param exact match",
entry: makerouter(routeMatch(httpMatchParam(ServiceRouteHTTPMatchQueryParam{
Name: "foo",
Exact: "bar",
}))),
},
{
name: "route with query param regex match",
entry: makerouter(routeMatch(httpMatchParam(ServiceRouteHTTPMatchQueryParam{
Name: "foo",
Regex: "bar",
}))),
},
{
name: "route with query param present match",
entry: makerouter(routeMatch(httpMatchParam(ServiceRouteHTTPMatchQueryParam{
Name: "foo",
Present: true,
}))),
},
{
name: "route with query param exact and regex match",
entry: makerouter(routeMatch(httpMatchParam(ServiceRouteHTTPMatchQueryParam{
Name: "foo",
Exact: "bar",
Regex: "bar",
}))),
validateErr: "should only contain one of Present, Exact, or Regex",
},
{
name: "route with query param exact and present match",
entry: makerouter(routeMatch(httpMatchParam(ServiceRouteHTTPMatchQueryParam{
Name: "foo",
Exact: "bar",
Present: true,
}))),
validateErr: "should only contain one of Present, Exact, or Regex",
},
{
name: "route with query param regex and present match",
entry: makerouter(routeMatch(httpMatchParam(ServiceRouteHTTPMatchQueryParam{
Name: "foo",
Regex: "bar",
Present: true,
}))),
validateErr: "should only contain one of Present, Exact, or Regex",
},
{
name: "route with query param exact, regex, and present match",
entry: makerouter(routeMatch(httpMatchParam(ServiceRouteHTTPMatchQueryParam{
Name: "foo",
Exact: "bar",
Regex: "bar",
Present: true,
}))),
validateErr: "should only contain one of Present, Exact, or Regex",
},
////////////////
{
name: "route with no match and prefix rewrite",
entry: makerouter(ServiceRoute{
Match: nil,
Destination: &ServiceRouteDestination{
Service: "other",
PrefixRewrite: "/new",
},
}),
validateErr: "cannot make use of PrefixRewrite without configuring either PathExact or PathPrefix",
},
{
name: "route with path prefix match and prefix rewrite",
entry: makerouter(ServiceRoute{
Match: httpMatch(&ServiceRouteHTTPMatch{
PathPrefix: "/api",
}),
Destination: &ServiceRouteDestination{
Service: "other",
PrefixRewrite: "/new",
},
}),
},
{
name: "route with path exact match and prefix rewrite",
entry: makerouter(ServiceRoute{
Match: httpMatch(&ServiceRouteHTTPMatch{
PathExact: "/api",
}),
Destination: &ServiceRouteDestination{
Service: "other",
PrefixRewrite: "/new",
},
}),
},
{
name: "route with path regex match and prefix rewrite",
entry: makerouter(ServiceRoute{
Match: httpMatch(&ServiceRouteHTTPMatch{
PathRegex: "/api",
}),
Destination: &ServiceRouteDestination{
Service: "other",
PrefixRewrite: "/new",
},
}),
validateErr: "cannot make use of PrefixRewrite without configuring either PathExact or PathPrefix",
},
{
name: "route with header match and prefix rewrite",
entry: makerouter(ServiceRoute{
Match: httpMatchHeader(ServiceRouteHTTPMatchHeader{
Name: "foo",
Exact: "bar",
}),
Destination: &ServiceRouteDestination{
Service: "other",
PrefixRewrite: "/new",
},
}),
validateErr: "cannot make use of PrefixRewrite without configuring either PathExact or PathPrefix",
},
{
name: "route with header match and prefix rewrite",
entry: makerouter(ServiceRoute{
Match: httpMatchParam(ServiceRouteHTTPMatchQueryParam{
Name: "foo",
Exact: "bar",
}),
Destination: &ServiceRouteDestination{
Service: "other",
PrefixRewrite: "/new",
},
}),
validateErr: "cannot make use of PrefixRewrite without configuring either PathExact or PathPrefix",
},
////////////////
{
name: "route with method matches",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
Methods: []string{
"get", "POST", "dElEtE",
},
}))),
check: func(t *testing.T, entry *ServiceRouterConfigEntry) {
m := entry.Routes[0].Match.HTTP.Methods
require.Equal(t, []string{"GET", "POST", "DELETE"}, m)
},
},
{
name: "route with method matches repeated",
entry: makerouter(routeMatch(httpMatch(&ServiceRouteHTTPMatch{
Methods: []string{
"GET", "DELETE", "get",
},
}))),
validateErr: "Methods contains \"GET\" more than once",
},
////////////////
{
name: "route with no match with retry condition",
entry: makerouter(ServiceRoute{
Match: nil,
Destination: &ServiceRouteDestination{
Service: "other",
RetryOn: []string{
"5xx",
"gateway-error",
"reset",
"connect-failure",
"envoy-ratelimited",
"retriable-4xx",
"refused-stream",
"cancelled",
"deadline-exceeded",
"internal",
"resource-exhausted",
"unavailable",
},
},
}),
},
{
name: "route with no match with invalid retry condition",
entry: makerouter(ServiceRoute{
Match: nil,
Destination: &ServiceRouteDestination{
Service: "other",
RetryOn: []string{
"invalid-retry-condition",
},
},
}),
validateErr: "contains an invalid retry condition: \"invalid-retry-condition\"",
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
err := tc.entry.Normalize()
if tc.normalizeErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.normalizeErr)
return
}
require.NoError(t, err)
if tc.check != nil {
tc.check(t, tc.entry)
}
err = tc.entry.Validate()
if tc.validateErr != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.validateErr)
return
}
require.NoError(t, err)
})
}
}
var validSubsetNames = []string{
"a", "aa", "2a", "a2", "a2a", "a22a",
"1", "11", "10", "01",
"a-a", "a--a", "a--a--a",
"0-0", "0--0", "0--0--0",
strings.Repeat("a", 63),
}
var invalidSubsetNames = []string{
"A", "AA", "2A", "A2", "A2A", "A22A",
"A-A", "A--A", "A--A--A",
" ", " a", "a ", "a a",
"_", "_a", "a_", "a_a",
".", ".a", "a.", "a.a",
"-", "-a", "a-",
strings.Repeat("a", 64),
}
func TestValidateServiceSubset(t *testing.T) {
for _, name := range validSubsetNames {
t.Run(name, func(t *testing.T) {
require.NoError(t, validateServiceSubset(name))
})
}
for _, name := range invalidSubsetNames {
t.Run(name, func(t *testing.T) {
require.Error(t, validateServiceSubset(name))
})
}
}
func TestIsProtocolHTTPLike(t *testing.T) {
assert.False(t, IsProtocolHTTPLike(""))
assert.False(t, IsProtocolHTTPLike("tcp"))
assert.True(t, IsProtocolHTTPLike("http"))
assert.True(t, IsProtocolHTTPLike("http2"))
assert.True(t, IsProtocolHTTPLike("grpc"))
}
func TestIsValidRetryCondition(t *testing.T) {
assert.False(t, isValidRetryCondition(""))
assert.False(t, isValidRetryCondition("retriable-headers"))
assert.False(t, isValidRetryCondition("retriable-status-codes"))
assert.True(t, isValidRetryCondition("5xx"))
assert.True(t, isValidRetryCondition("gateway-error"))
assert.True(t, isValidRetryCondition("reset"))
assert.True(t, isValidRetryCondition("connect-failure"))
assert.True(t, isValidRetryCondition("envoy-ratelimited"))
assert.True(t, isValidRetryCondition("retriable-4xx"))
assert.True(t, isValidRetryCondition("refused-stream"))
assert.True(t, isValidRetryCondition("cancelled"))
assert.True(t, isValidRetryCondition("deadline-exceeded"))
assert.True(t, isValidRetryCondition("internal"))
assert.True(t, isValidRetryCondition("resource-exhausted"))
assert.True(t, isValidRetryCondition("unavailable"))
}