Skip to content

Commit

Permalink
kuma-cp: generate HTTP-specific outbound listeners for services tagge…
Browse files Browse the repository at this point in the history
…d with `protocol: http` (#585)

* kuma-cp: generate HTTP-specific outbound listeners for services tagged with `protocol: http`

* code review: avoid exporting `getCommonProtocol` function

* code review: refactor out `GetInboundRouteName()` function

* code review: revert breaking changes to e2e test
  • Loading branch information
yskopets authored Feb 18, 2020
1 parent 619475d commit 805116c
Show file tree
Hide file tree
Showing 28 changed files with 1,654 additions and 116 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

Changes:

* feature: generate HTTP-specific outbound listeners for services tagged with `protocol: http`
[#585](https://github.com/Kong/kuma/pull/585)
* feature: TracingTrace in Kuma REST API
[#583](https://github.com/Kong/kuma/pull/583)
* feature: TracingTrace entity
Expand Down
45 changes: 12 additions & 33 deletions pkg/xds/envoy/listeners/http_inbound_route_configurer.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package listeners

import (
"fmt"

"github.com/golang/protobuf/proto"
wrappers "github.com/golang/protobuf/ptypes/wrappers"

v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
envoy_listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"
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_wellknown "github.com/envoyproxy/go-control-plane/pkg/wellknown"

envoy_common "github.com/Kong/kuma/pkg/xds/envoy"
envoy_names "github.com/Kong/kuma/pkg/xds/envoy/names"
envoy_routes "github.com/Kong/kuma/pkg/xds/envoy/routes"
)

func HttpInboundRoute(service string, cluster envoy_common.ClusterInfo) FilterChainBuilderOpt {
Expand All @@ -31,7 +28,16 @@ type HttpInboundRouteConfigurer struct {
}

func (c *HttpInboundRouteConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
routeConfig := c.routeConfiguration()
routeName := envoy_names.GetInboundRouteName(c.service)
routeConfig, err := envoy_routes.NewRouteConfigurationBuilder().
Configure(envoy_routes.CommonRouteConfiguration(routeName)).
Configure(envoy_routes.VirtualHost(envoy_routes.NewVirtualHostBuilder().
Configure(envoy_routes.CommonVirtualHost(c.service)).
Configure(envoy_routes.DefaultRoute(c.cluster)))).
Build()
if err != nil {
return err
}

return UpdateFilterConfig(filterChain, envoy_wellknown.HTTPConnectionManager, func(filterConfig proto.Message) error {
hcm, ok := filterConfig.(*envoy_hcm.HttpConnectionManager)
Expand All @@ -44,30 +50,3 @@ func (c *HttpInboundRouteConfigurer) Configure(filterChain *envoy_listener.Filte
return nil
})
}

func (c *HttpInboundRouteConfigurer) routeConfiguration() *v2.RouteConfiguration {
return &v2.RouteConfiguration{
Name: fmt.Sprintf("inbound:%s", c.service),
VirtualHosts: []*envoy_route.VirtualHost{{
Name: c.service,
Domains: []string{"*"},
Routes: []*envoy_route.Route{{
Match: &envoy_route.RouteMatch{
PathSpecifier: &envoy_route.RouteMatch_Prefix{
Prefix: "/",
},
},
Action: &envoy_route.Route_Route{
Route: &envoy_route.RouteAction{
ClusterSpecifier: &envoy_route.RouteAction_Cluster{
Cluster: c.cluster.Name,
},
},
},
}},
}},
ValidateClusters: &wrappers.BoolValue{
Value: true,
},
}
}
43 changes: 43 additions & 0 deletions pkg/xds/envoy/listeners/http_outbound_route_configurer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package listeners

import (
"github.com/golang/protobuf/proto"

envoy_core "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
envoy_listener "github.com/envoyproxy/go-control-plane/envoy/api/v2/listener"
envoy_hcm "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2"
envoy_wellknown "github.com/envoyproxy/go-control-plane/pkg/wellknown"
)

func HttpOutboundRoute(routeName string) FilterChainBuilderOpt {
return FilterChainBuilderOptFunc(func(config *FilterChainBuilderConfig) {
config.Add(&HttpOutboundRouteConfigurer{
routeName: routeName,
})
})
}

type HttpOutboundRouteConfigurer struct {
routeName string
}

func (c *HttpOutboundRouteConfigurer) Configure(filterChain *envoy_listener.FilterChain) error {
return UpdateFilterConfig(filterChain, envoy_wellknown.HTTPConnectionManager, func(filterConfig proto.Message) error {
hcm, ok := filterConfig.(*envoy_hcm.HttpConnectionManager)
if !ok {
return NewUnexpectedFilterConfigTypeError(filterConfig, &envoy_hcm.HttpConnectionManager{})
}

hcm.RouteSpecifier = &envoy_hcm.HttpConnectionManager_Rds{
Rds: &envoy_hcm.Rds{
ConfigSource: &envoy_core.ConfigSource{
ConfigSourceSpecifier: &envoy_core.ConfigSource_Ads{
Ads: &envoy_core.AggregatedConfigSource{},
},
},
RouteConfigName: c.routeName,
},
}
return nil
})
}
69 changes: 69 additions & 0 deletions pkg/xds/envoy/listeners/http_outbound_route_configurer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package listeners_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"

. "github.com/Kong/kuma/pkg/xds/envoy/listeners"

util_proto "github.com/Kong/kuma/pkg/util/proto"
)

var _ = Describe("HttpOutboundRouteConfigurer", func() {

type testCase struct {
listenerName string
listenerAddress string
listenerPort uint32
statsName string
routeName string
expected string
}

DescribeTable("should generate proper Envoy config",
func(given testCase) {
// when
listener, err := NewListenerBuilder().
Configure(OutboundListener(given.listenerName, given.listenerAddress, given.listenerPort)).
Configure(FilterChain(NewFilterChainBuilder().
Configure(HttpConnectionManager(given.statsName)).
Configure(HttpOutboundRoute(given.routeName)))).
Build()
// then
Expect(err).ToNot(HaveOccurred())

// when
actual, err := util_proto.ToYAML(listener)
Expect(err).ToNot(HaveOccurred())
// and
Expect(actual).To(MatchYAML(given.expected))
},
Entry("basic http_connection_manager with an outbound route", testCase{
listenerName: "outbound:127.0.0.1:18080",
listenerAddress: "127.0.0.1",
listenerPort: 18080,
statsName: "127.0.0.1:18080",
routeName: "outbound:backend",
expected: `
name: outbound:127.0.0.1:18080
address:
socketAddress:
address: 127.0.0.1
portValue: 18080
filterChains:
- filters:
- name: envoy.http_connection_manager
typedConfig:
'@type': type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
httpFilters:
- name: envoy.router
rds:
configSource:
ads: {}
routeConfigName: outbound:backend
statPrefix: "127_0_0_1_18080"
`,
}),
)
})
1 change: 0 additions & 1 deletion pkg/xds/envoy/listeners/network_rbac_configurer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
mesh_core "github.com/Kong/kuma/pkg/core/resources/apis/mesh"

test_model "github.com/Kong/kuma/pkg/test/resources/model"

util_proto "github.com/Kong/kuma/pkg/util/proto"
envoy_common "github.com/Kong/kuma/pkg/xds/envoy"
)
Expand Down
52 changes: 52 additions & 0 deletions pkg/xds/envoy/names/resource_names.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package names

import (
"fmt"
"sort"
"strings"

mesh_proto "github.com/Kong/kuma/api/mesh/v1alpha1"
)

func GetLocalClusterName(port uint32) string {
return fmt.Sprintf("localhost:%d", port)
}

func GetInboundListenerName(address string, port uint32) string {
return fmt.Sprintf("inbound:%s:%d", address, port)
}

func GetOutboundListenerName(address string, port uint32) string {
return fmt.Sprintf("outbound:%s:%d", address, port)
}

func GetInboundRouteName(service string) string {
return fmt.Sprintf("inbound:%s", service)
}

func GetOutboundRouteName(service string) string {
return fmt.Sprintf("outbound:%s", service)
}

func GetEnvoyAdminClusterName() string {
return "kuma:envoy:admin"
}

func GetPrometheusListenerName() string {
return "kuma:metrics:prometheus"
}

func GetDestinationClusterName(service string, selector map[string]string) string {
var pairs []string
for key, value := range selector {
if key == mesh_proto.ServiceTag {
continue
}
pairs = append(pairs, fmt.Sprintf("%s=%s", key, value))
}
if len(pairs) == 0 {
return service
}
sort.Strings(pairs)
return fmt.Sprintf("%s{%s}", service, strings.Join(pairs, ","))
}
27 changes: 27 additions & 0 deletions pkg/xds/envoy/routes/common_route_configuration_configurer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package routes

import (
"github.com/golang/protobuf/ptypes/wrappers"

v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
)

func CommonRouteConfiguration(name string) RouteConfigurationBuilderOpt {
return RouteConfigurationBuilderOptFunc(func(config *RouteConfigurationBuilderConfig) {
config.Add(&CommonRouteConfigurationConfigurer{
name: name,
})
})
}

type CommonRouteConfigurationConfigurer struct {
name string
}

func (c CommonRouteConfigurationConfigurer) Configure(routeConfiguration *v2.RouteConfiguration) error {
routeConfiguration.Name = c.name
routeConfiguration.ValidateClusters = &wrappers.BoolValue{
Value: true,
}
return nil
}
44 changes: 44 additions & 0 deletions pkg/xds/envoy/routes/common_route_configuration_configurer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package routes_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"

. "github.com/Kong/kuma/pkg/xds/envoy/routes"

util_proto "github.com/Kong/kuma/pkg/util/proto"
)

var _ = Describe("CommonRouteConfigurationConfigurer", func() {

type testCase struct {
routeName string
expected string
}

DescribeTable("should generate proper Envoy config",
func(given testCase) {
// when
routeConfiguration, err := NewRouteConfigurationBuilder().
Configure(CommonRouteConfiguration(given.routeName)).
Build()
// then
Expect(err).ToNot(HaveOccurred())

// when
actual, err := util_proto.ToYAML(routeConfiguration)
// then
Expect(err).ToNot(HaveOccurred())
// and
Expect(actual).To(MatchYAML(given.expected))
},
Entry("basic RouteConfiguration", testCase{
routeName: "outbound:backend",
expected: `
name: outbound:backend
validateClusters: true
`,
}),
)
})
23 changes: 23 additions & 0 deletions pkg/xds/envoy/routes/common_virtual_host_configurer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package routes

import (
envoy_route "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
)

func CommonVirtualHost(name string) VirtualHostBuilderOpt {
return VirtualHostBuilderOptFunc(func(config *VirtualHostBuilderConfig) {
config.Add(&CommonVirtualHostConfigurer{
name: name,
})
})
}

type CommonVirtualHostConfigurer struct {
name string
}

func (c CommonVirtualHostConfigurer) Configure(virtualHost *envoy_route.VirtualHost) error {
virtualHost.Name = c.name
virtualHost.Domains = []string{"*"}
return nil
}
45 changes: 45 additions & 0 deletions pkg/xds/envoy/routes/common_virtual_host_configurer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package routes_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"

. "github.com/Kong/kuma/pkg/xds/envoy/routes"

util_proto "github.com/Kong/kuma/pkg/util/proto"
)

var _ = Describe("CommonVirtualHostConfigurer", func() {

type testCase struct {
virtualHostName string
expected string
}

DescribeTable("should generate proper Envoy config",
func(given testCase) {
// when
routeConfiguration, err := NewVirtualHostBuilder().
Configure(CommonVirtualHost(given.virtualHostName)).
Build()
// then
Expect(err).ToNot(HaveOccurred())

// when
actual, err := util_proto.ToYAML(routeConfiguration)
// then
Expect(err).ToNot(HaveOccurred())
// and
Expect(actual).To(MatchYAML(given.expected))
},
Entry("basic VirtualHost", testCase{
virtualHostName: "backend",
expected: `
name: backend
domains:
- '*'
`,
}),
)
})
Loading

0 comments on commit 805116c

Please sign in to comment.