Skip to content

Commit

Permalink
Support Listener and ClusterLoadAssignment in property-override (
Browse files Browse the repository at this point in the history
…#17497)

* Support Listener in Property Override

Add support for patching `Listener` resources via the builtin
`property-override` extension.

Refactor existing listener patch code in `BasicEnvoyExtender` to
simplify addition of resource support.

* Support ClusterLoadAssignment in Property Override

Add support for patching `ClusterLoadAssignment` resources via the
builtin `property-override` extension.
  • Loading branch information
zalimeni authored May 29, 2023
1 parent 5a46a8c commit e1df0f2
Show file tree
Hide file tree
Showing 22 changed files with 2,094 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"

envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
"github.com/hashicorp/go-multierror"
Expand Down Expand Up @@ -49,13 +50,17 @@ type ResourceFilter struct {
type ResourceType string

const (
ResourceTypeCluster ResourceType = "cluster"
ResourceTypeRoute ResourceType = "route"
ResourceTypeCluster ResourceType = "cluster"
ResourceTypeClusterLoadAssignment ResourceType = "cluster-load-assignment"
ResourceTypeListener ResourceType = "listener"
ResourceTypeRoute ResourceType = "route"
)

var ResourceTypes = stringSet{
string(ResourceTypeCluster): {},
string(ResourceTypeRoute): {},
string(ResourceTypeCluster): {},
string(ResourceTypeClusterLoadAssignment): {},
string(ResourceTypeListener): {},
string(ResourceTypeRoute): {},
}

// TrafficDirection determines whether inbound or outbound Envoy resources will be patched.
Expand Down Expand Up @@ -177,8 +182,12 @@ func (p *Patch) validate(debug bool) error {
switch p.ResourceFilter.ResourceType {
case ResourceTypeCluster:
_, err = PatchStruct(&envoy_cluster_v3.Cluster{}, *p, debug)
case ResourceTypeClusterLoadAssignment:
_, err = PatchStruct(&envoy_endpoint_v3.ClusterLoadAssignment{}, *p, debug)
case ResourceTypeRoute:
_, err = PatchStruct(&envoy_route_v3.RouteConfiguration{}, *p, debug)
case ResourceTypeListener:
_, err = PatchStruct(&envoy_listener_v3.Listener{}, *p, debug)
default:
return fmt.Errorf("path validation unimplemented for %q", p.ResourceFilter.ResourceType)
}
Expand Down Expand Up @@ -251,6 +260,24 @@ func (p *propertyOverride) PatchCluster(_ *extensioncommon.RuntimeConfig, c *env
return patchResourceType[*envoy_cluster_v3.Cluster](c, p, ResourceTypeCluster, d, &defaultStructPatcher[*envoy_cluster_v3.Cluster]{})
}

// PatchClusterLoadAssignment patches the provided Envoy ClusterLoadAssignment with any applicable `cluster-load-assignment` ResourceType patches.
func (p *propertyOverride) PatchClusterLoadAssignment(_ *extensioncommon.RuntimeConfig, c *envoy_endpoint_v3.ClusterLoadAssignment) (*envoy_endpoint_v3.ClusterLoadAssignment, bool, error) {
d := TrafficDirectionOutbound
if extensioncommon.IsLocalAppClusterLoadAssignment(c) {
d = TrafficDirectionInbound
}
return patchResourceType[*envoy_endpoint_v3.ClusterLoadAssignment](c, p, ResourceTypeClusterLoadAssignment, d, &defaultStructPatcher[*envoy_endpoint_v3.ClusterLoadAssignment]{})
}

// PatchListener patches the provided Envoy Listener with any applicable `listener` ResourceType patches.
func (p *propertyOverride) PatchListener(_ *extensioncommon.RuntimeConfig, l *envoy_listener_v3.Listener) (*envoy_listener_v3.Listener, bool, error) {
d := TrafficDirectionOutbound
if extensioncommon.IsInboundPublicListener(l) {
d = TrafficDirectionInbound
}
return patchResourceType[*envoy_listener_v3.Listener](l, p, ResourceTypeListener, d, &defaultStructPatcher[*envoy_listener_v3.Listener]{})
}

// PatchFilter does nothing as this extension does not target Filters directly.
func (p *propertyOverride) PatchFilter(_ *extensioncommon.RuntimeConfig, f *envoy_listener_v3.Filter, _ bool) (*envoy_listener_v3.Filter, bool, error) {
return f, false, nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,11 @@ func TestConstructor(t *testing.T) {
v = nil
}

// Use a valid field (name) for all resource types.
// Use a valid field for all resource types.
path := "/name"
if t == ResourceTypeClusterLoadAssignment {
path = "/cluster_name"
}

return testCase{
arguments: makeArguments(map[string]any{
Expand Down
145 changes: 145 additions & 0 deletions agent/xds/delta_envoy_extender_oss_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,115 @@ end`,
},
})

propertyOverrideServiceDefaultsClusterLoadAssignmentOutboundAdd := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeClusterLoadAssignment,
"TrafficDirection": propertyoverride.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/policy/overprovisioning_factor",
"Value": 123,
},
},
})

propertyOverrideServiceDefaultsClusterLoadAssignmentInboundAdd := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeClusterLoadAssignment,
"TrafficDirection": propertyoverride.TrafficDirectionInbound,
},
"Op": "add",
"Path": "/policy/overprovisioning_factor",
"Value": 123,
},
},
})

propertyOverrideServiceDefaultsListenerInboundAdd := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": propertyoverride.TrafficDirectionInbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats",
},
},
})

propertyOverrideServiceDefaultsListenerOutboundAdd := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": propertyoverride.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats",
},
},
})

propertyOverrideServiceDefaultsListenerOutboundDoesntApplyToInbound := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": propertyoverride.TrafficDirectionInbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats.inbound.only",
},
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": propertyoverride.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats.outbound.only",
},
},
})

// Reverse order of above patches, to prove order is inconsequential
propertyOverrideServiceDefaultsListenerInboundDoesntApplyToOutbound := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": propertyoverride.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats.outbound.only",
},
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": propertyoverride.TrafficDirectionInbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats.inbound.only",
},
},
})

tests := []struct {
name string
create func(t testinf.T) *proxycfg.ConfigSnapshot
Expand Down Expand Up @@ -215,6 +324,42 @@ end`,
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsRemoveOutlierDetection, nil)
},
},
{
name: "propertyoverride-cluster-load-assignment-outbound-add",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsClusterLoadAssignmentOutboundAdd, nil)
},
},
{
name: "propertyoverride-cluster-load-assignment-inbound-add",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsClusterLoadAssignmentInboundAdd, nil)
},
},
{
name: "propertyoverride-listener-outbound-add",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsListenerOutboundAdd, nil)
},
},
{
name: "propertyoverride-listener-inbound-add",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsListenerInboundAdd, nil)
},
},
{
name: "propertyoverride-outbound-doesnt-apply-to-inbound",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsListenerOutboundDoesntApplyToInbound, nil)
},
},
{
name: "propertyoverride-inbound-doesnt-apply-to-outbound",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsListenerInboundDoesntApplyToOutbound, nil)
},
},
{
name: "lambda-connect-proxy",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "5s",
"circuitBreakers": {},
"outlierDetection": {},
"commonLbConfig": {
"healthyPanicThreshold": {}
},
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
"commonTlsContext": {
"tlsParams": {},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
},
"matchSubjectAltNames": [
{
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db"
}
]
}
},
"sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
},
{
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "5s",
"circuitBreakers": {},
"outlierDetection": {},
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
"commonTlsContext": {
"tlsParams": {},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
},
"matchSubjectAltNames": [
{
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target"
},
{
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target"
}
]
}
},
"sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul"
}
}
},
{
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"name": "local_app",
"type": "STATIC",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "local_app",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "127.0.0.1",
"portValue": 8080
}
}
}
}
]
}
],
"policy": {
"overprovisioningFactor": 123
}
}
}
],
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"nonce": "00000001"
}
Loading

0 comments on commit e1df0f2

Please sign in to comment.