Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(kuma-cp) virtual host modifications #909

Merged
merged 6 commits into from
Jul 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 189 additions & 43 deletions api/mesh/v1alpha1/proxy_template.pb.go

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions api/mesh/v1alpha1/proxy_template.proto
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ message ProxyTemplate {
NetworkFilter networkFilter = 3;
// HTTP Filter modification
HttpFilter httpFilter = 4;
// Virtual Host modifications
VirtualHost virtualHost = 5;
}

// Cluster defines modifications to generated clusters
Expand Down Expand Up @@ -125,6 +127,27 @@ message ProxyTemplate {
string listenerName = 3;
}
}

// VirtualHost defines modification to generated virtual hosts
message VirtualHost {
// Only virtual hosts that match will be modified
Match match = 1;
// Operation to apply on a virtual hosts (add, remove, patch)
string operation = 2;
// xDS virtual host
string value = 3;

// Match defines match for virtual host
message Match {
// Origin of the resource generation. (inbound, outbound, prometheus,
// transparent, ingress)
string origin = 1;
// Name of the virtual host to match
string name = 2;
// Name of the route configuration
string routeConfigurationName = 3;
}
}
}
}

Expand Down
24 changes: 24 additions & 0 deletions pkg/core/resources/apis/mesh/proxytemplate_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

envoy_route "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
envoy_hcm "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2"

envoy_api "github.com/envoyproxy/go-control-plane/envoy/api/v2"
Expand Down Expand Up @@ -55,6 +56,29 @@ func validateModification(modification *mesh_proto.ProxyTemplate_Modifications)
verr.AddError("networkFilter", validateNetworkFilterModification(modification.GetNetworkFilter()))
case *mesh_proto.ProxyTemplate_Modifications_HttpFilter_:
verr.AddError("httpFilter", validateHTTPFilterModification(modification.GetHttpFilter()))
case *mesh_proto.ProxyTemplate_Modifications_VirtualHost_:
verr.AddError("virtualHost", validateVirtualHostModification(modification.GetVirtualHost()))
}
return verr
}

func validateVirtualHostModification(vHostMod *mesh_proto.ProxyTemplate_Modifications_VirtualHost) validators.ValidationError {
verr := validators.ValidationError{}
switch vHostMod.Operation {
case mesh_proto.OpAdd:
if vHostMod.GetMatch().GetName() != "" {
verr.AddViolation("match.name", "cannot be defined")
}
if err := ValidateResourceYAML(&envoy_route.VirtualHost{}, vHostMod.Value); err != nil {
verr.AddViolation("value", fmt.Sprintf("native Envoy resource is not valid: %s", err.Error()))
}
case mesh_proto.OpPatch:
if err := ValidateResourceYAMLPatch(&envoy_route.VirtualHost{}, vHostMod.Value); err != nil {
verr.AddViolation("value", fmt.Sprintf("native Envoy resource is not valid: %s", err.Error()))
}
case mesh_proto.OpRemove:
default:
verr.AddViolation("operation", fmt.Sprintf("invalid operation. Available operations: %q, %q, %q", mesh_proto.OpAdd, mesh_proto.OpPatch, mesh_proto.OpRemove))
}
return verr
}
Expand Down
93 changes: 89 additions & 4 deletions pkg/core/resources/apis/mesh/proxytemplate_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ var _ = Describe("ProxyTemplate", func() {
- cluster:
operation: patch
match:
direction: inbound
origin: inbound
value: |
connectTimeout: 5s
- cluster:
Expand All @@ -94,7 +94,7 @@ var _ = Describe("ProxyTemplate", func() {
- cluster:
operation: remove
match:
direction: inbound
origin: inbound
`,
),
Entry("listener modifications", `
Expand Down Expand Up @@ -128,7 +128,7 @@ var _ = Describe("ProxyTemplate", func() {
- listener:
operation: patch
match:
direction: inbound
origin: inbound
value: |
address:
socketAddress:
Expand All @@ -142,7 +142,7 @@ var _ = Describe("ProxyTemplate", func() {
- listener:
operation: remove
match:
direction: inbound
origin: inbound
`,
),
Entry("network filter modifications", `
Expand Down Expand Up @@ -243,6 +243,62 @@ var _ = Describe("ProxyTemplate", func() {
dynamicStats: false
- httpFilter:
operation: remove
`,
),
Entry("virtual host modifications", `
selectors:
- match:
kuma.io/service: backend
conf:
modifications:
- virtualHost:
operation: add
match:
origin: outbound
routeConfigurationName: outbound:backend
value: |
name: backend
domains:
- backend.com
routes:
- match:
prefix: /
route:
cluster: backend
- virtualHost:
operation: patch
value: |
retryPolicy:
retryOn: 5xx
numRetries: 3
- virtualHost:
operation: patch
match:
origin: outbound
value: |
retryPolicy:
retryOn: 5xx
numRetries: 3
- virtualHost:
operation: patch
match:
routeConfigurationName: outbound:backend
name: backend
value: |
retryPolicy:
retryOn: 5xx
numRetries: 3
- cluster:
operation: remove
- cluster:
operation: remove
match:
routeConfigurationName: outbound:backend
name: backend
- cluster:
operation: remove
match:
origin: inbound
`,
),
)
Expand Down Expand Up @@ -529,6 +585,35 @@ var _ = Describe("ProxyTemplate", func() {
- field: conf.modifications[3].httpFilter.match.name
message: cannot be empty
- field: conf.modifications[3].httpFilter.value
message: 'native Envoy resource is not valid: unexpected EOF'`,
}),
Entry("invalid virtual host operation", testCase{
proxyTemplate: `
selectors:
- match:
kuma.io/service: backend
conf:
modifications:
- virtualHost:
operation: add
match:
name: xyz
value: '{'
- virtualHost:
operation: addFirst
- virtualHost:
operation: patch
value: '{'
`,
expected: `
violations:
- field: conf.modifications[0].virtualHost.match.name
message: cannot be defined
- field: conf.modifications[0].virtualHost.value
message: 'native Envoy resource is not valid: unexpected EOF'
- field: conf.modifications[1].virtualHost.operation
message: 'invalid operation. Available operations: "add", "patch", "remove"'
- field: conf.modifications[2].virtualHost.value
message: 'native Envoy resource is not valid: unexpected EOF'`,
}),
)
Expand Down
5 changes: 4 additions & 1 deletion pkg/xds/generator/modifications/modifications.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ func Apply(resources *core_xds.ResourceSet, modifications []*mesh_proto.ProxyTem
case *mesh_proto.ProxyTemplate_Modifications_HttpFilter_:
mod := httpFilterModificator(*modification.GetHttpFilter())
modificator = &mod
case *mesh_proto.ProxyTemplate_Modifications_VirtualHost_:
mod := virtualHostModificator(*modification.GetVirtualHost())
modificator = &mod
default:
return errors.Errorf("invalid modification type %q", modification.Type)
return errors.Errorf("invalid modification type %T", modification.Type)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}
if err := modificator.apply(resources); err != nil {
return errors.Wrapf(err, "could not apply %d modification", i)
Expand Down
78 changes: 78 additions & 0 deletions pkg/xds/generator/modifications/virtual_host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package modifications

import (
envoy_api "github.com/envoyproxy/go-control-plane/envoy/api/v2"
envoy_route "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
envoy_resource "github.com/envoyproxy/go-control-plane/pkg/resource/v2"
"github.com/golang/protobuf/proto"
"github.com/pkg/errors"

mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
core_xds "github.com/kumahq/kuma/pkg/core/xds"
util_proto "github.com/kumahq/kuma/pkg/util/proto"
)

type virtualHostModificator mesh_proto.ProxyTemplate_Modifications_VirtualHost

func (c *virtualHostModificator) apply(resources *core_xds.ResourceSet) error {
virtualHost := &envoy_route.VirtualHost{}
if err := util_proto.FromYAML([]byte(c.Value), virtualHost); err != nil {
return err
}

for _, resource := range resources.Resources(envoy_resource.RouteType) {
if c.routeConfigurationMatches(resource) {
routeCfg := resource.Resource.(*envoy_api.RouteConfiguration)
switch c.Operation {
case mesh_proto.OpAdd:
c.add(routeCfg, virtualHost)
case mesh_proto.OpRemove:
c.remove(routeCfg)
case mesh_proto.OpPatch:
c.patch(routeCfg, virtualHost)
default:
return errors.Errorf("invalid operation: %s", c.Operation)
}
}
}
return nil
}

func (c *virtualHostModificator) patch(routeCfg *envoy_api.RouteConfiguration, vHostPatch *envoy_route.VirtualHost) {
for _, vHost := range routeCfg.VirtualHosts {
if c.virtualHostMatches(vHost) {
proto.Merge(vHost, vHostPatch)
}
}
}

func (c *virtualHostModificator) remove(routeCfg *envoy_api.RouteConfiguration) {
var vHosts []*envoy_route.VirtualHost
for _, vHost := range routeCfg.VirtualHosts {
if !c.virtualHostMatches(vHost) {
vHosts = append(vHosts, vHost)
}
}
routeCfg.VirtualHosts = vHosts
}

func (c *virtualHostModificator) add(routeCfg *envoy_api.RouteConfiguration, vHost *envoy_route.VirtualHost) {
routeCfg.VirtualHosts = append(routeCfg.VirtualHosts, vHost)
}

func (c *virtualHostModificator) virtualHostMatches(vHost *envoy_route.VirtualHost) bool {
if c.Match.GetName() != "" && c.Match.GetName() != vHost.Name {
return false
}
return true
}

func (c *virtualHostModificator) routeConfigurationMatches(routeCfg *core_xds.Resource) bool {
if c.Match.GetOrigin() != "" && c.Match.GetOrigin() != routeCfg.Origin {
return false
}
if c.Match.GetRouteConfigurationName() != "" && c.Match.GetRouteConfigurationName() != routeCfg.Name {
return false
}
return true
}
Loading