Skip to content

Commit

Permalink
feat: direct response (#4508)
Browse files Browse the repository at this point in the history
* feat: direct response

Relates to #2714

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

* provider logic

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

* default status code is 200

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

---------

Signed-off-by: Arko Dasgupta <arko@tetrate.io>
  • Loading branch information
arkodg authored Oct 24, 2024
1 parent 4f0266c commit 9353be2
Show file tree
Hide file tree
Showing 39 changed files with 292 additions and 179 deletions.
87 changes: 46 additions & 41 deletions internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import (
"strings"

perr "github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/utils/ptr"
gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
Expand All @@ -32,14 +32,14 @@ const (
MaxConsistentHashTableSize = 5000011 // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#config-cluster-v3-cluster-maglevlbconfig
)

func (t *Translator) ProcessBackendTrafficPolicies(backendTrafficPolicies []*egv1a1.BackendTrafficPolicy,
func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources,
gateways []*GatewayContext,
routes []RouteContext,
xdsIR resource.XdsIRMap,
configMaps []*corev1.ConfigMap,
) []*egv1a1.BackendTrafficPolicy {
res := []*egv1a1.BackendTrafficPolicy{}

backendTrafficPolicies := resources.BackendTrafficPolicies
// Sort based on timestamp
sort.Slice(backendTrafficPolicies, func(i, j int) bool {
return backendTrafficPolicies[i].CreationTimestamp.Before(&(backendTrafficPolicies[j].CreationTimestamp))
Expand Down Expand Up @@ -130,7 +130,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(backendTrafficPolicies []*egv
}

// Set conditions for translation error if it got any
if err := t.translateBackendTrafficPolicyForRoute(policy, route, xdsIR, configMaps); err != nil {
if err := t.translateBackendTrafficPolicyForRoute(policy, route, xdsIR, resources); err != nil {
status.SetTranslationErrorForPolicyAncestors(&policy.Status,
ancestorRefs,
t.GatewayControllerName,
Expand Down Expand Up @@ -184,7 +184,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(backendTrafficPolicies []*egv
}

// Set conditions for translation error if it got any
if err := t.translateBackendTrafficPolicyForGateway(policy, currTarget, gateway, xdsIR, configMaps); err != nil {
if err := t.translateBackendTrafficPolicyForGateway(policy, currTarget, gateway, xdsIR, resources); err != nil {
status.SetTranslationErrorForPolicyAncestors(&policy.Status,
ancestorRefs,
t.GatewayControllerName,
Expand Down Expand Up @@ -288,7 +288,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
policy *egv1a1.BackendTrafficPolicy,
route RouteContext,
xdsIR resource.XdsIRMap,
configMaps []*corev1.ConfigMap,
resources *resource.Resources,
) error {
var (
rl *ir.RateLimit
Expand Down Expand Up @@ -349,7 +349,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
errs = errors.Join(errs, err)
}

if ro, err = buildResponseOverride(policy, configMaps); err != nil {
if ro, err = buildResponseOverride(policy, resources); err != nil {
err = perr.WithMessage(err, "ResponseOverride")
errs = errors.Join(errs, err)
}
Expand Down Expand Up @@ -392,8 +392,8 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
if strings.HasPrefix(r.Name, prefix) {
if errs != nil {
// Return a 500 direct response
r.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
r.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
continue
}
Expand Down Expand Up @@ -438,7 +438,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
target gwapiv1a2.LocalPolicyTargetReferenceWithSectionName,
gateway *GatewayContext,
xdsIR resource.XdsIRMap,
configMaps []*corev1.ConfigMap,
resources *resource.Resources,
) error {
var (
rl *ir.RateLimit
Expand Down Expand Up @@ -491,7 +491,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
err = perr.WithMessage(err, "HTTP2")
errs = errors.Join(errs, err)
}
if ro, err = buildResponseOverride(policy, configMaps); err != nil {
if ro, err = buildResponseOverride(policy, resources); err != nil {
err = perr.WithMessage(err, "ResponseOverride")
errs = errors.Join(errs, err)
}
Expand Down Expand Up @@ -561,8 +561,8 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(

if errs != nil {
// Return a 500 direct response
r.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
r.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
continue
}
Expand Down Expand Up @@ -864,7 +864,7 @@ func makeIrTriggerSet(in []egv1a1.TriggerEnum) []ir.TriggerEnum {
return irTriggers
}

func buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, configMaps []*corev1.ConfigMap) (*ir.ResponseOverride, error) {
func buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, resources *resource.Resources) (*ir.ResponseOverride, error) {
if len(policy.Spec.ResponseOverride) == 0 {
return nil, nil
}
Expand Down Expand Up @@ -894,33 +894,10 @@ func buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, configMaps []*co
ContentType: ro.Response.ContentType,
}

if ro.Response.Body.Type != nil && *ro.Response.Body.Type == egv1a1.ResponseValueTypeValueRef {
foundCM := false
for _, cm := range configMaps {
if cm.Namespace == policy.Namespace && cm.Name == string(ro.Response.Body.ValueRef.Name) {
body, dataOk := cm.Data["response.body"]
switch {
case dataOk:
response.Body = body
case len(cm.Data) > 0: // Fallback to the first key if response.body is not found
for _, value := range cm.Data {
body = value
break
}
response.Body = body
default:
return nil, fmt.Errorf("can't find the key response.body in the referenced configmap %s", ro.Response.Body.ValueRef.Name)
}

foundCM = true
break
}
}
if !foundCM {
return nil, fmt.Errorf("can't find the referenced configmap %s", ro.Response.Body.ValueRef.Name)
}
} else {
response.Body = *ro.Response.Body.Inline
var err error
response.Body, err = getCustomResponseBody(ro.Response.Body, resources, policy.Namespace)
if err != nil {
return nil, err
}

rules = append(rules, ir.ResponseOverrideRule{
Expand All @@ -935,6 +912,34 @@ func buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, configMaps []*co
}, nil
}

func getCustomResponseBody(body egv1a1.CustomResponseBody, resources *resource.Resources, policyNs string) (*string, error) {
if body.Type != nil && *body.Type == egv1a1.ResponseValueTypeValueRef {
cm := resources.GetConfigMap(policyNs, string(body.ValueRef.Name))
if cm != nil {
b, dataOk := cm.Data["response.body"]
switch {
case dataOk:
return &b, nil
case len(cm.Data) > 0: // Fallback to the first key if response.body is not found
for _, value := range cm.Data {
b = value
break
}
return &b, nil
default:
return nil, fmt.Errorf("can't find the key response.body in the referenced configmap %s", body.ValueRef.Name)
}

} else {
return nil, fmt.Errorf("can't find the referenced configmap %s", body.ValueRef.Name)
}
} else if body.Inline != nil {
return body.Inline, nil
}

return nil, nil
}

func defaultResponseOverrideRuleName(policy *egv1a1.BackendTrafficPolicy, index int) string {
return fmt.Sprintf(
"%s/responseoverride/rule/%s",
Expand Down
8 changes: 4 additions & 4 deletions internal/gatewayapi/envoyextensionpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,8 @@ func (t *Translator) translateEnvoyExtensionPolicyForRoute(
if strings.HasPrefix(r.Name, prefix) {
// return 500 and do not configure EnvoyExtensions in this case
if errs != nil {
r.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
r.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
continue
}
Expand Down Expand Up @@ -386,8 +386,8 @@ func (t *Translator) translateEnvoyExtensionPolicyForGateway(

// return 500 and do not configure EnvoyExtensions in this case
if errs != nil {
r.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
r.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
continue
}
Expand Down
53 changes: 40 additions & 13 deletions internal/gatewayapi/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type HTTPFiltersContext struct {

// HTTPFilterIR contains the ir processing results.
type HTTPFilterIR struct {
DirectResponse *ir.DirectResponse
DirectResponse *ir.CustomResponse
RedirectResponse *ir.Redirect

URLRewrite *ir.URLRewrite
Expand Down Expand Up @@ -785,8 +785,10 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjec
filterNs := filterContext.Route.GetNamespace()

if string(extFilter.Kind) == egv1a1.KindHTTPRouteFilter {
found := false
for _, hrf := range resources.HTTPRouteFilters {
if hrf.Namespace == filterNs && hrf.Name == string(extFilter.Name) {
found = true
if hrf.Spec.URLRewrite != nil {

if filterContext.URLRewrite != nil {
Expand Down Expand Up @@ -846,15 +848,13 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjec
filterContext.HTTPFilterIR.URLRewrite.Path = &ir.ExtendedHTTPPathModifier{
RegexMatchReplace: rmr,
}
return
}
} else { // no url rewrite
filterContext.HTTPFilterIR.URLRewrite = &ir.URLRewrite{
Path: &ir.ExtendedHTTPPathModifier{
RegexMatchReplace: rmr,
},
}
return
}
}
}
Expand Down Expand Up @@ -887,22 +887,49 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjec
if filterContext.HTTPFilterIR.URLRewrite != nil {
if filterContext.HTTPFilterIR.URLRewrite.Host == nil {
filterContext.HTTPFilterIR.URLRewrite.Host = hm
return
}
} else { // no url rewrite
filterContext.HTTPFilterIR.URLRewrite = &ir.URLRewrite{
Host: hm,
}
}
}

}

if hrf.Spec.DirectResponse != nil {
dr := &ir.CustomResponse{}
if hrf.Spec.DirectResponse.Body != nil {
var err error
if dr.Body, err = getCustomResponseBody(*hrf.Spec.DirectResponse.Body, resources, filterNs); err != nil {
t.processInvalidHTTPFilter(string(extFilter.Kind), filterContext, err)
return
}
}

if hrf.Spec.DirectResponse.StatusCode != nil {
dr.StatusCode = ptr.To(uint32(*hrf.Spec.DirectResponse.StatusCode))
} else {
dr.StatusCode = ptr.To(uint32(200))
}

if hrf.Spec.DirectResponse.ContentType != nil {
newHeader := ir.AddHeader{
Name: "Content-Type",
Value: []string{*hrf.Spec.DirectResponse.ContentType},
}
filterContext.AddResponseHeaders = append(filterContext.AddResponseHeaders, newHeader)
}

filterContext.HTTPFilterIR.DirectResponse = dr
}
}
}
errMsg := fmt.Sprintf("Unable to translate HTTPRouteFilter: %s/%s", filterNs,
extFilter.Name)
t.processUnresolvedHTTPFilter(errMsg, filterContext)
if !found {
errMsg := fmt.Sprintf("Unable to translate HTTPRouteFilter: %s/%s", filterNs,
extFilter.Name)
t.processUnresolvedHTTPFilter(errMsg, filterContext)
}
return
}

Expand Down Expand Up @@ -993,8 +1020,8 @@ func (t *Translator) processUnresolvedHTTPFilter(errMsg string, filterContext *H
gwapiv1.RouteReasonUnsupportedValue,
errMsg,
)
filterContext.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
filterContext.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
}

Expand All @@ -1009,8 +1036,8 @@ func (t *Translator) processUnsupportedHTTPFilter(filterType string, filterConte
gwapiv1.RouteReasonUnsupportedValue,
errMsg,
)
filterContext.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
filterContext.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
}

Expand All @@ -1025,7 +1052,7 @@ func (t *Translator) processInvalidHTTPFilter(filterType string, filterContext *
gwapiv1.RouteReasonUnsupportedValue,
errMsg,
)
filterContext.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
filterContext.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
}
8 changes: 4 additions & 4 deletions internal/gatewayapi/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe
for _, ruleRoute := range ruleRoutes {
noValidBackends := ruleRoute.Destination == nil || ruleRoute.Destination.ToBackendWeights().Valid == 0
if noValidBackends && ruleRoute.Redirect == nil {
ruleRoute.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
ruleRoute.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
}
ruleRoute.IsHTTP2 = false
Expand Down Expand Up @@ -570,8 +570,8 @@ func (t *Translator) processGRPCRouteRules(grpcRoute *GRPCRouteContext, parentRe
for _, ruleRoute := range ruleRoutes {
noValidBackends := ruleRoute.Destination == nil || ruleRoute.Destination.ToBackendWeights().Valid == 0
if noValidBackends && ruleRoute.Redirect == nil {
ruleRoute.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
ruleRoute.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
}
ruleRoute.IsHTTP2 = true
Expand Down
8 changes: 4 additions & 4 deletions internal/gatewayapi/securitypolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,8 @@ func (t *Translator) translateSecurityPolicyForRoute(
}
if errs != nil {
// Return a 500 direct response to avoid unauthorized access
r.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
r.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
}
}
Expand Down Expand Up @@ -514,8 +514,8 @@ func (t *Translator) translateSecurityPolicyForGateway(
}
if errs != nil {
// Return a 500 direct response to avoid unauthorized access
r.DirectResponse = &ir.DirectResponse{
StatusCode: 500,
r.DirectResponse = &ir.CustomResponse{
StatusCode: ptr.To(uint32(500)),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/gatewayapi/translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func (t *Translator) Translate(resources *resource.Resources) (*TranslateResult,

// Process BackendTrafficPolicies
backendTrafficPolicies := t.ProcessBackendTrafficPolicies(
resources.BackendTrafficPolicies, gateways, routes, xdsIR, resources.ConfigMaps)
resources, gateways, routes, xdsIR)

// Process SecurityPolicies
securityPolicies := t.ProcessSecurityPolicies(
Expand Down
Loading

0 comments on commit 9353be2

Please sign in to comment.