...

Source file src/github.com/datawire/ambassador/v2/pkg/gateway/gw_transforms.go

Documentation: github.com/datawire/ambassador/v2/pkg/gateway

     1  package gateway
     2  
     3  import (
     4  	// standard library
     5  	"fmt"
     6  
     7  	// third-party libraries
     8  	"github.com/pkg/errors"
     9  	"google.golang.org/protobuf/types/known/anypb"
    10  	"google.golang.org/protobuf/types/known/wrapperspb"
    11  	gw "sigs.k8s.io/gateway-api/apis/v1alpha1"
    12  
    13  	// envoy api v2
    14  	apiv2 "github.com/datawire/ambassador/v2/pkg/api/envoy/api/v2"
    15  	apiv2_core "github.com/datawire/ambassador/v2/pkg/api/envoy/api/v2/core"
    16  	apiv2_listener "github.com/datawire/ambassador/v2/pkg/api/envoy/api/v2/listener"
    17  	apiv2_route "github.com/datawire/ambassador/v2/pkg/api/envoy/api/v2/route"
    18  	apiv2_httpman "github.com/datawire/ambassador/v2/pkg/api/envoy/config/filter/network/http_connection_manager/v2"
    19  	api_matcher "github.com/datawire/ambassador/v2/pkg/api/envoy/type/matcher"
    20  
    21  	// envoy control plane
    22  	ecp_wellknown "github.com/datawire/ambassador/v2/pkg/envoy-control-plane/wellknown"
    23  
    24  	// first-party libraries
    25  	"github.com/datawire/ambassador/v2/pkg/kates"
    26  )
    27  
    28  func Compile_Gateway(gateway *gw.Gateway) (*CompiledConfig, error) {
    29  	src := SourceFromResource(gateway)
    30  	var listeners []*CompiledListener
    31  	for idx, l := range gateway.Spec.Listeners {
    32  		name := fmt.Sprintf("%s-%d", getName(gateway), idx)
    33  		listener, err := Compile_Listener(src, l, name)
    34  		if err != nil {
    35  			return nil, err
    36  		}
    37  		listeners = append(listeners, listener)
    38  	}
    39  	return &CompiledConfig{
    40  		CompiledItem: NewCompiledItem(src),
    41  		Listeners:    listeners,
    42  	}, nil
    43  }
    44  
    45  func Compile_Listener(parent Source, lst gw.Listener, name string) (*CompiledListener, error) {
    46  	hcm := &apiv2_httpman.HttpConnectionManager{
    47  		StatPrefix: name,
    48  		HttpFilters: []*apiv2_httpman.HttpFilter{
    49  			{Name: ecp_wellknown.CORS},
    50  			{Name: ecp_wellknown.Router},
    51  		},
    52  		RouteSpecifier: &apiv2_httpman.HttpConnectionManager_Rds{
    53  			Rds: &apiv2_httpman.Rds{
    54  				ConfigSource: &apiv2_core.ConfigSource{
    55  					ConfigSourceSpecifier: &apiv2_core.ConfigSource_Ads{
    56  						Ads: &apiv2_core.AggregatedConfigSource{},
    57  					},
    58  				},
    59  				RouteConfigName: name,
    60  			},
    61  		},
    62  	}
    63  	hcmAny, err := anypb.New(hcm)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	return &CompiledListener{
    69  		CompiledItem: NewCompiledItem(Sourcef("listener %s", lst.Hostname)),
    70  		Listener: &apiv2.Listener{
    71  			Name: name,
    72  			Address: &apiv2_core.Address{Address: &apiv2_core.Address_SocketAddress{SocketAddress: &apiv2_core.SocketAddress{
    73  				Address:       "0.0.0.0",
    74  				PortSpecifier: &apiv2_core.SocketAddress_PortValue{PortValue: uint32(lst.Port)},
    75  			}}},
    76  			FilterChains: []*apiv2_listener.FilterChain{
    77  				{
    78  					Filters: []*apiv2_listener.Filter{
    79  						{
    80  							Name:       ecp_wellknown.HTTPConnectionManager,
    81  							ConfigType: &apiv2_listener.Filter_TypedConfig{TypedConfig: hcmAny},
    82  						},
    83  					},
    84  				},
    85  			},
    86  		},
    87  		Predicate: func(route *CompiledRoute) bool {
    88  			return true
    89  		},
    90  		Domains: []string{"*"},
    91  	}, nil
    92  
    93  }
    94  
    95  func Compile_HTTPRoute(httpRoute *gw.HTTPRoute) (*CompiledConfig, error) {
    96  	src := SourceFromResource(httpRoute)
    97  	clusterRefs := []*ClusterRef{}
    98  	var routes []*apiv2_route.Route
    99  	for idx, rule := range httpRoute.Spec.Rules {
   100  		s := Sourcef("rule %d in %s", idx, src)
   101  		_routes, err := Compile_HTTPRouteRule(s, rule, httpRoute.Namespace, &clusterRefs)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  		routes = append(routes, _routes...)
   106  	}
   107  	return &CompiledConfig{
   108  		CompiledItem: NewCompiledItem(src),
   109  		Routes: []*CompiledRoute{
   110  			{
   111  				CompiledItem: CompiledItem{Source: src, Namespace: httpRoute.Namespace},
   112  				HTTPRoute:    httpRoute,
   113  				Routes:       routes,
   114  				ClusterRefs:  clusterRefs,
   115  			},
   116  		},
   117  	}, nil
   118  }
   119  
   120  func Compile_HTTPRouteRule(src Source, rule gw.HTTPRouteRule, namespace string, clusterRefs *[]*ClusterRef) ([]*apiv2_route.Route, error) {
   121  	var clusters []*apiv2_route.WeightedCluster_ClusterWeight
   122  	for idx, fwd := range rule.ForwardTo {
   123  		s := Sourcef("forwardTo %d in %s", idx, src)
   124  		clusters = append(clusters, Compile_HTTPRouteForwardTo(s, fwd, namespace, clusterRefs))
   125  	}
   126  
   127  	wc := &apiv2_route.WeightedCluster{Clusters: clusters}
   128  
   129  	matches, err := Compile_HTTPRouteMatches(rule.Matches)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	var result []*apiv2_route.Route
   134  	for _, match := range matches {
   135  		result = append(result, &apiv2_route.Route{
   136  			Match: match,
   137  			Action: &apiv2_route.Route_Route{Route: &apiv2_route.RouteAction{
   138  				ClusterSpecifier: &apiv2_route.RouteAction_WeightedClusters{WeightedClusters: wc},
   139  			}},
   140  		})
   141  	}
   142  
   143  	return result, err
   144  }
   145  
   146  func Compile_HTTPRouteForwardTo(src Source, forward gw.HTTPRouteForwardTo, namespace string, clusterRefs *[]*ClusterRef) *apiv2_route.WeightedCluster_ClusterWeight {
   147  	suffix := ""
   148  	clusterName := *forward.ServiceName
   149  	if forward.Port != nil {
   150  		suffix = fmt.Sprintf("/%d", *forward.Port)
   151  		clusterName = fmt.Sprintf("%s_%d", *forward.ServiceName, *forward.Port)
   152  	}
   153  
   154  	*clusterRefs = append(*clusterRefs, &ClusterRef{
   155  		CompiledItem: NewCompiledItem(src),
   156  		Name:         clusterName,
   157  		EndpointPath: fmt.Sprintf("k8s/%s/%s%s", namespace, *forward.ServiceName, suffix),
   158  	})
   159  	return &apiv2_route.WeightedCluster_ClusterWeight{
   160  		Name:   clusterName,
   161  		Weight: &wrapperspb.UInt32Value{Value: uint32(forward.Weight)},
   162  	}
   163  }
   164  
   165  func Compile_HTTPRouteMatches(matches []gw.HTTPRouteMatch) ([]*apiv2_route.RouteMatch, error) {
   166  	var result []*apiv2_route.RouteMatch
   167  	for _, match := range matches {
   168  		item, err := Compile_HTTPRouteMatch(match)
   169  		if err != nil {
   170  			return nil, err
   171  		}
   172  		result = append(result, item)
   173  	}
   174  	return result, nil
   175  }
   176  
   177  func Compile_HTTPRouteMatch(match gw.HTTPRouteMatch) (*apiv2_route.RouteMatch, error) {
   178  	headers, err := Compile_HTTPHeaderMatch(match.Headers)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	result := &apiv2_route.RouteMatch{
   183  		Headers: headers,
   184  	}
   185  
   186  	switch match.Path.Type {
   187  	case gw.PathMatchExact:
   188  		result.PathSpecifier = &apiv2_route.RouteMatch_Path{Path: match.Path.Value}
   189  	case gw.PathMatchPrefix:
   190  		result.PathSpecifier = &apiv2_route.RouteMatch_Prefix{Prefix: match.Path.Value}
   191  	case gw.PathMatchRegularExpression:
   192  		result.PathSpecifier = &apiv2_route.RouteMatch_SafeRegex{SafeRegex: regexMatcher(match.Path.Value)}
   193  	case "":
   194  		// no path match, but PathSpecifier is required
   195  		result.PathSpecifier = &apiv2_route.RouteMatch_Prefix{}
   196  	default:
   197  		return nil, errors.Errorf("unknown path match type: %q", match.Path.Type)
   198  	}
   199  
   200  	return result, nil
   201  }
   202  
   203  func Compile_HTTPHeaderMatch(headerMatch *gw.HTTPHeaderMatch) ([]*apiv2_route.HeaderMatcher, error) {
   204  	if headerMatch == nil {
   205  		return nil, nil
   206  	}
   207  
   208  	var result []*apiv2_route.HeaderMatcher
   209  	for hdr, pattern := range headerMatch.Values {
   210  		hm := &apiv2_route.HeaderMatcher{
   211  			Name:        hdr,
   212  			InvertMatch: false,
   213  		}
   214  
   215  		switch headerMatch.Type {
   216  		case gw.HeaderMatchExact:
   217  			hm.HeaderMatchSpecifier = &apiv2_route.HeaderMatcher_ExactMatch{ExactMatch: pattern}
   218  		case gw.HeaderMatchRegularExpression:
   219  			hm.HeaderMatchSpecifier = &apiv2_route.HeaderMatcher_SafeRegexMatch{SafeRegexMatch: regexMatcher(pattern)}
   220  		default:
   221  			return nil, errors.Errorf("unknown header match type: %s", headerMatch.Type)
   222  		}
   223  
   224  		result = append(result, hm)
   225  	}
   226  	return result, nil
   227  }
   228  
   229  func regexMatcher(pattern string) *api_matcher.RegexMatcher {
   230  	return &api_matcher.RegexMatcher{
   231  		EngineType: &api_matcher.RegexMatcher_GoogleRe2{GoogleRe2: &api_matcher.RegexMatcher_GoogleRE2{}},
   232  		Regex:      pattern,
   233  	}
   234  }
   235  
   236  func getName(resource kates.Object) string {
   237  	return fmt.Sprintf("%s-%s", resource.GetNamespace(), resource.GetName())
   238  }
   239  

View as plain text