Skip to content
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
12 changes: 12 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/envoyproxy/gateway
go 1.18

require (
github.com/envoyproxy/go-control-plane v0.10.3
github.com/spf13/cobra v1.4.0
github.com/stretchr/testify v1.7.1
k8s.io/apimachinery v0.24.2
Expand All @@ -14,6 +15,17 @@ require (
github.com/go-logr/logr v1.2.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/tetratelabs/multierror v1.1.0
google.golang.org/protobuf v1.28.0
sigs.k8s.io/yaml v1.3.0
)

require (
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc // indirect
github.com/envoyproxy/protoc-gen-validate v0.6.7 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand Down
332 changes: 331 additions & 1 deletion go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type Xds struct {
// Name of the Xds IR.
Name string
// HTTP listeners exposed by the gateway.
HTTP []HTTPListener
HTTP []*HTTPListener
}

// HTTPListener holds the listener configuration.
Expand Down
68 changes: 68 additions & 0 deletions pkg/xds/translator/cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package translator

import (
"time"

cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/wrapperspb"

"github.com/envoyproxy/gateway/pkg/ir"
)

func buildXdsCluster(httpRoute *ir.HTTPRoute) (*cluster.Cluster, error) {
localities := make([]*endpoint.LocalityLbEndpoints, 0, 1)
locality := &endpoint.LocalityLbEndpoints{
Locality: &core.Locality{},
LbEndpoints: buildXdsEndpoints(httpRoute.Destinations),
Priority: 0,
// Each locality gets the same weight 1. There is a single locality
// per priority, so the weight value does not really matter, but some
// load balancers need the value to be set.
LoadBalancingWeight: &wrapperspb.UInt32Value{Value: 1}}
localities = append(localities, locality)
clusterName := getXdsClusterName(httpRoute.Name)
return &cluster.Cluster{
Name: clusterName,
ConnectTimeout: durationpb.New(5 * time.Second),
ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_STATIC},
LbPolicy: cluster.Cluster_ROUND_ROBIN,
LoadAssignment: &endpoint.ClusterLoadAssignment{ClusterName: clusterName, Endpoints: localities},
DnsLookupFamily: cluster.Cluster_V4_ONLY,
CommonLbConfig: &cluster.Cluster_CommonLbConfig{
LocalityConfigSpecifier: &cluster.Cluster_CommonLbConfig_LocalityWeightedLbConfig_{
LocalityWeightedLbConfig: &cluster.Cluster_CommonLbConfig_LocalityWeightedLbConfig{}}},
OutlierDetection: &cluster.OutlierDetection{},
}, nil

}

func buildXdsEndpoints(destinations []*ir.RouteDestination) []*endpoint.LbEndpoint {
endpoints := make([]*endpoint.LbEndpoint, 0, len(destinations))
for _, destination := range destinations {
lbEndpoint := &endpoint.LbEndpoint{
HostIdentifier: &endpoint.LbEndpoint_Endpoint{
Endpoint: &endpoint.Endpoint{
Address: &core.Address{
Address: &core.Address_SocketAddress{
SocketAddress: &core.SocketAddress{
Protocol: core.SocketAddress_TCP,
Address: destination.Host,
PortSpecifier: &core.SocketAddress_PortValue{
PortValue: destination.Port,
},
},
},
},
},
},
}
if destination.Weight != 0 {
lbEndpoint.LoadBalancingWeight = &wrapperspb.UInt32Value{Value: destination.Weight}
}
endpoints = append(endpoints, lbEndpoint)
}
return endpoints
}
30 changes: 30 additions & 0 deletions pkg/xds/translator/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package translator

import (
"github.com/envoyproxy/go-control-plane/pkg/cache/types"
resource "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
)

// XdsResources represents all the xds resources
type XdsResources map[resource.Type][]types.Resource

// Context holds all the translated xds resources
type Context struct {
xdsResources XdsResources
}

// GetXdsResources retrieves the translated xds resources saved in the translator context.
func (t *Context) GetXdsResources() XdsResources {
return t.xdsResources
}

func (t *Context) addXdsResource(rType resource.Type, xdsResource types.Resource) {
if t.xdsResources == nil {
t.xdsResources = make(XdsResources)
}
if t.xdsResources[rType] == nil {
t.xdsResources[rType] = make([]types.Resource, 0, 1)
}

t.xdsResources[rType] = append(t.xdsResources[rType], xdsResource)
}
10 changes: 10 additions & 0 deletions pkg/xds/translator/in/xds-ir/http-route.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: "http-route"
http:
- name: "listener-1"
port: 10080
hostnames:
- "*"
routes:
- destinations:
- host: "1.2.3.4"
port: 50000
109 changes: 109 additions & 0 deletions pkg/xds/translator/listener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package translator

import (
"errors"

core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
"github.com/envoyproxy/go-control-plane/pkg/wellknown"
"google.golang.org/protobuf/types/known/anypb"

"github.com/envoyproxy/gateway/pkg/ir"
)

func buildXdsListener(httpListener *ir.HTTPListener) (*listener.Listener, error) {
if httpListener == nil {
return nil, errors.New("http listener is nil")
}
// HTTP filter configuration
mgr := &hcm.HttpConnectionManager{
CodecType: hcm.HttpConnectionManager_AUTO,
StatPrefix: "http",
RouteSpecifier: &hcm.HttpConnectionManager_Rds{
Rds: &hcm.Rds{
ConfigSource: makeConfigSource(),
// Configure route name to be found via RDS.
RouteConfigName: getXdsRouteName(httpListener.Name),
},
},
// Use only router.
HttpFilters: []*hcm.HttpFilter{{
Name: wellknown.Router,
}},
}

mgrAny, err := anypb.New(mgr)
if err != nil {
return nil, err
}

return &listener.Listener{
Name: getXdsListenerName(httpListener.Name, httpListener.Port),
Address: &core.Address{
Address: &core.Address_SocketAddress{
SocketAddress: &core.SocketAddress{
Protocol: core.SocketAddress_TCP,
Address: httpListener.Address,
PortSpecifier: &core.SocketAddress_PortValue{
PortValue: httpListener.Port,
},
},
},
},
FilterChains: []*listener.FilterChain{{
Filters: []*listener.Filter{{
Name: wellknown.HTTPConnectionManager,
ConfigType: &listener.Filter_TypedConfig{
TypedConfig: mgrAny,
},
}},
}},
}, nil
}

func buildXdsDownstreamTLSSocket(listenerName string,
tlsConfig *ir.TLSListenerConfig) (*core.TransportSocket, error) {
tlsCtx := &tls.DownstreamTlsContext{
CommonTlsContext: &tls.CommonTlsContext{
TlsCertificateSdsSecretConfigs: []*tls.SdsSecretConfig{{
// Generate key name for this listener. The actual key will be
// delivered to Envoy via SDS.
Name: getXdsSecretName(listenerName),
SdsConfig: makeConfigSource(),
}},
},
}

tlsCtxAny, err := anypb.New(tlsCtx)
if err != nil {
return nil, err
}

return &core.TransportSocket{
Name: wellknown.TransportSocketTls,
ConfigType: &core.TransportSocket_TypedConfig{
TypedConfig: tlsCtxAny,
},
}, nil
}

func buildXdsDownstreamTLSSecret(listenerName string,
tlsConfig *ir.TLSListenerConfig) (*tls.Secret, error) {
// Build the tls secret
return &tls.Secret{
Name: getXdsSecretName(listenerName),
Type: &tls.Secret_TlsCertificate{
TlsCertificate: &tls.TlsCertificate{
CertificateChain: &core.DataSource{
Specifier: &core.DataSource_InlineBytes{InlineBytes: tlsConfig.ServerCertificate},
},
PrivateKey: &core.DataSource{
Specifier: &core.DataSource_InlineBytes{InlineBytes: tlsConfig.PrivateKey},
},
},
},
}, nil

}
117 changes: 117 additions & 0 deletions pkg/xds/translator/route.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package translator

import (
route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"

"github.com/envoyproxy/gateway/pkg/ir"
)

func buildXdsRoute(httpRoute *ir.HTTPRoute) (*route.Route, error) {
return &route.Route{
Match: buildXdsRouteMatch(httpRoute.PathMatch, httpRoute.HeaderMatches, httpRoute.QueryParamMatches),
Action: &route.Route_Route{Route: buildXdsRouteAction(httpRoute.Name)},
}, nil
}

func buildXdsRouteMatch(pathMatch *ir.StringMatch, headerMatches []*ir.StringMatch, queryParamMatches []*ir.StringMatch) *route.RouteMatch {
outMatch := &route.RouteMatch{}

// Return early with a prefix match to '/' if no matches are specified
if pathMatch == nil && len(headerMatches) == 0 && len(queryParamMatches) == 0 {
// Setup default path specifier. It may be overwritten by :host:.
outMatch.PathSpecifier = &route.RouteMatch_Prefix{
Prefix: "/",
}
return outMatch
}

// Path match
//nolint:gocritic
if pathMatch != nil {
if pathMatch.Exact != nil {
outMatch.PathSpecifier = &route.RouteMatch_Path{
Path: *pathMatch.Exact,
}
} else if pathMatch.Prefix != nil {
outMatch.PathSpecifier = &route.RouteMatch_Prefix{
Prefix: *pathMatch.Prefix,
}
} else if pathMatch.SafeRegex != nil {
outMatch.PathSpecifier = &route.RouteMatch_SafeRegex{
SafeRegex: &matcher.RegexMatcher{
EngineType: &matcher.RegexMatcher_GoogleRe2{},
Regex: *pathMatch.SafeRegex,
},
}
}
}

// Header matches
for _, headerMatch := range headerMatches {
stringMatcher := buildXdsStringMatcher(headerMatch)

headerMatcher := &route.HeaderMatcher{
Name: headerMatch.Name,
HeaderMatchSpecifier: &route.HeaderMatcher_StringMatch{
StringMatch: stringMatcher,
},
}
outMatch.Headers = append(outMatch.Headers, headerMatcher)
}

// Query param matches
for _, queryParamMatch := range queryParamMatches {
stringMatcher := buildXdsStringMatcher(queryParamMatch)

queryParamMatcher := &route.QueryParameterMatcher{
Name: queryParamMatch.Name,
QueryParameterMatchSpecifier: &route.QueryParameterMatcher_StringMatch{
StringMatch: stringMatcher,
},
}
outMatch.QueryParameters = append(outMatch.QueryParameters, queryParamMatcher)
}

return outMatch
}

func buildXdsStringMatcher(irMatch *ir.StringMatch) *matcher.StringMatcher {
stringMatcher := new(matcher.StringMatcher)

//nolint:gocritic
if irMatch.Exact != nil {
stringMatcher = &matcher.StringMatcher{
MatchPattern: &matcher.StringMatcher_Exact{
Exact: *irMatch.Exact,
},
}
} else if irMatch.Prefix != nil {
stringMatcher = &matcher.StringMatcher{
MatchPattern: &matcher.StringMatcher_Prefix{
Prefix: *irMatch.Prefix,
},
}
} else if irMatch.SafeRegex != nil {
stringMatcher = &matcher.StringMatcher{
MatchPattern: &matcher.StringMatcher_SafeRegex{
SafeRegex: &matcher.RegexMatcher{
Regex: *irMatch.SafeRegex,
EngineType: &matcher.RegexMatcher_GoogleRe2{
GoogleRe2: &matcher.RegexMatcher_GoogleRE2{},
},
},
},
}
}

return stringMatcher
}

func buildXdsRouteAction(routeName string) *route.RouteAction {
return &route.RouteAction{
ClusterSpecifier: &route.RouteAction_Cluster{
Cluster: getXdsClusterName(routeName),
},
}
}
12 changes: 12 additions & 0 deletions pkg/xds/translator/testdata/in/xds-ir/http-route.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: "http-route"
http:
- name: "first-listener"
address: "0.0.0.0"
port: 10080
hostnames:
- "*"
routes:
- name: "first-route"
destinations:
- host: "1.2.3.4"
port: 50000
Loading