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
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.7.0
v1.7.1
4 changes: 3 additions & 1 deletion api/v1alpha1/connection_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,14 @@ type PreconnectPolicy struct {
PredictivePercent *uint32 `json:"predictivePercent,omitempty"`
}

// +kubebuilder:validation:XValidation:rule="!has(self.closeDelay) || has(self.value)",message="closeDelay can only be configured when value is set"
type ConnectionLimit struct {
// Value of the maximum concurrent connections limit.
// When the limit is reached, incoming connections will be closed after the CloseDelay duration.
//
// +kubebuilder:validation:Minimum=1
Value int64 `json:"value"`
// +optional
Value *int64 `json:"value,omitempty"`

// CloseDelay defines the delay to use before closing connections that are rejected
// once the limit value is reached.
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,10 @@ spec:
format: int64
minimum: 1
type: integer
required:
- value
type: object
x-kubernetes-validations:
- message: closeDelay can only be configured when value is set
rule: '!has(self.closeDelay) || has(self.value)'
maxAcceptPerSocketEvent:
default: 1
description: |-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,10 @@ spec:
format: int64
minimum: 1
type: integer
required:
- value
type: object
x-kubernetes-validations:
- message: closeDelay can only be configured when value is set
rule: '!has(self.closeDelay) || has(self.value)'
maxAcceptPerSocketEvent:
default: 1
description: |-
Expand Down
70 changes: 58 additions & 12 deletions internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute(
// Find the Gateway that the route belongs to and add it to the
// gatewayRouteMap and ancestor list, which will be used to check
// policy overrides and populate its ancestor status.
parentRefs := GetParentReferences(targetedRoute)
parentRefs := GetManagedParentReferences(targetedRoute)
ancestorRefs := make([]*gwapiv1.ParentReference, 0, len(parentRefs))
// parentRefCtxs holds parent gateway/listener contexts for using in policy merge logic.
parentRefCtxs := make([]*RouteParentContext, 0, len(parentRefs))
Expand Down Expand Up @@ -295,12 +295,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute(
// Do need a section name since the policy is targeting to a route.
ancestorRef := getAncestorRefForPolicy(mapKey.NamespacedName, p.SectionName)
ancestorRefs = append(ancestorRefs, &ancestorRef)

// Only process parentRefs that were handled by this translator
// (skip those referencing Gateways with different GatewayClasses)
if parentRefCtx := targetedRoute.GetRouteParentContext(p); parentRefCtx != nil {
parentRefCtxs = append(parentRefCtxs, parentRefCtx)
}
parentRefCtxs = append(parentRefCtxs, targetedRoute.GetRouteParentContext(p))
}
}

Expand Down Expand Up @@ -671,7 +666,7 @@ func (t *Translator) translateBackendTrafficPolicyForRouteWithMerge(
policyTargetGatewayNN types.NamespacedName, policyTargetListener *gwapiv1.SectionName, route RouteContext,
xdsIR resource.XdsIRMap,
) error {
mergedPolicy, err := mergeBackendTrafficPolicy(policy, parentPolicy)
mergedPolicy, err := t.mergeBackendTrafficPolicy(policy, parentPolicy)
if err != nil {
return fmt.Errorf("error merging policies: %w", err)
}
Expand Down Expand Up @@ -842,14 +837,24 @@ func (t *Translator) applyTrafficFeatureToRoute(route RouteContext,
}
}

func mergeBackendTrafficPolicy(routePolicy, gwPolicy *egv1a1.BackendTrafficPolicy) (*egv1a1.BackendTrafficPolicy, error) {
// mergeBackendTrafficPolicy merges route policy into gateway policy.
func (t *Translator) mergeBackendTrafficPolicy(routePolicy, gwPolicy *egv1a1.BackendTrafficPolicy) (*egv1a1.BackendTrafficPolicy, error) {
if routePolicy.Spec.MergeType == nil || gwPolicy == nil {
return routePolicy, nil
}

return utils.Merge[*egv1a1.BackendTrafficPolicy](gwPolicy, routePolicy, *routePolicy.Spec.MergeType)
// Resolve LocalObjectReferences to inline content in the policies before merge so the merge operates on concrete values.
if err := t.resolveLocalObjectRefsInPolicy(gwPolicy); err != nil {
return nil, err
}
if err := t.resolveLocalObjectRefsInPolicy(routePolicy); err != nil {
return nil, err
}

return utils.Merge(gwPolicy, routePolicy, *routePolicy.Spec.MergeType)
}

// buildTrafficFeatures builds IR traffic features from a BackendTrafficPolicy.
func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy) (*ir.TrafficFeatures, error) {
var (
rl *ir.RateLimit
Expand Down Expand Up @@ -1684,10 +1689,10 @@ func (t *Translator) getCustomResponseBody(
return binData, nil
}
default:
return nil, fmt.Errorf("can't find the key %s in the referenced configmap %s", ResponseBodyConfigMapKey, body.ValueRef.Name)
return nil, fmt.Errorf("can't find the key %s in the referenced configmap %s/%s", ResponseBodyConfigMapKey, policyNs, body.ValueRef.Name)
}
} else {
return nil, fmt.Errorf("can't find the referenced configmap %s", body.ValueRef.Name)
return nil, fmt.Errorf("can't find the referenced configmap %s/%s", policyNs, body.ValueRef.Name)
}
} else if body.Inline != nil {
inlineValue := []byte(*body.Inline)
Expand All @@ -1697,6 +1702,47 @@ func (t *Translator) getCustomResponseBody(
return nil, nil
}

// resolveCustomResponseBodyRefToInline resolves a ValueRef in body to inline content using the given namespace.
// It mutates body in place: replaces Type and ValueRef with Inline content. No-op if body is nil or already Inline.
func (t *Translator) resolveCustomResponseBodyRefToInline(body *egv1a1.CustomResponseBody, policyNs string) error {
if body == nil {
return nil
}
if body.Type == nil || *body.Type != egv1a1.ResponseValueTypeValueRef || body.ValueRef == nil {
return nil
}
data, err := t.getCustomResponseBody(body, policyNs)
if err != nil {
return err
}
inlineStr := string(data)
t.Logger.Info("resolved custom response body ref to inline before merge",
"namespace", policyNs,
"ref", body.ValueRef.Name,
)
body.Type = ptr.To(egv1a1.ResponseValueTypeInline)
body.Inline = &inlineStr
body.ValueRef = nil
return nil
}

// resolveLocalObjectRefsInPolicy resolves LocalObjectReferences to inline content in the given policy (mutates in place).
// Currently handles ResponseOverride body ValueRefs; may be extended for other refs BackendTrafficPolicy supports.
func (t *Translator) resolveLocalObjectRefsInPolicy(policy *egv1a1.BackendTrafficPolicy) error {
if policy == nil || len(policy.Spec.ResponseOverride) == 0 {
return nil
}
policyNs := policy.Namespace
for _, ro := range policy.Spec.ResponseOverride {
if ro != nil && ro.Response != nil && ro.Response.Body != nil {
if err := t.resolveCustomResponseBodyRefToInline(ro.Response.Body, policyNs); err != nil {
return err
}
}
}
return nil
}

func defaultResponseOverrideRuleName(policy *egv1a1.BackendTrafficPolicy, index int) string {
return fmt.Sprintf(
"%s/responseoverride/rule/%s",
Expand Down
4 changes: 3 additions & 1 deletion internal/gatewayapi/clienttrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,9 @@ func buildConnection(connection *egv1a1.ClientConnection) (*ir.ClientConnection,
if connection.ConnectionLimit != nil {
irConnectionLimit := &ir.ConnectionLimit{}

irConnectionLimit.Value = ptr.To(uint64(connection.ConnectionLimit.Value))
if connection.ConnectionLimit.Value != nil {
irConnectionLimit.Value = ptr.To(uint64(*connection.ConnectionLimit.Value))
}

if connection.ConnectionLimit.CloseDelay != nil {
d, err := time.ParseDuration(string(*connection.ConnectionLimit.CloseDelay))
Expand Down
20 changes: 18 additions & 2 deletions internal/gatewayapi/contexts.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func (l *ListenerContext) AllowsNamespace(namespace *corev1.Namespace) bool {
}

if l.AllowedRoutes == nil || l.AllowedRoutes.Namespaces == nil || l.AllowedRoutes.Namespaces.From == nil {
return l.gateway.Namespace == namespace.Name
return l.GetNamespace() == namespace.Name
}

switch *l.AllowedRoutes.Namespaces.From {
Expand All @@ -183,7 +183,7 @@ func (l *ListenerContext) AllowsNamespace(namespace *corev1.Namespace) bool {
return l.namespaceSelector.Matches(labels.Set(namespace.Labels))
default:
// NamespacesFromSame is the default
return l.gateway.Namespace == namespace.Name
return l.GetNamespace() == namespace.Name
}
}

Expand Down Expand Up @@ -534,6 +534,22 @@ func GetParentReferences(route RouteContext) []gwapiv1.ParentReference {
return route.GetParentReferences()
}

// GetManagedParentReferences returns route parentRefs that are managed by this controller.
func GetManagedParentReferences(route RouteContext) []gwapiv1.ParentReference {
parentRefs := GetParentReferences(route)
managed := make([]gwapiv1.ParentReference, 0, len(parentRefs))
for _, parentRef := range parentRefs {
// RouteParentContext is only created for parentRefs handled by this
// translator run. If absent, the parentRef points to a Gateway that is
// not managed by this controller.
if route.GetRouteParentContext(parentRef) == nil {
continue
}
managed = append(managed, parentRef)
}
return managed
}

// GetRouteStatus returns the RouteStatus object associated with the Route.
func GetRouteStatus(route RouteContext) *gwapiv1.RouteStatus {
return route.GetRouteStatus()
Expand Down
2 changes: 1 addition & 1 deletion internal/gatewayapi/envoyextensionpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func (t *Translator) processEnvoyExtensionPolicyForRoute(
// Find the Gateway that the route belongs to and add it to the
// gatewayRouteMap and ancestor list, which will be used to check
// policy overrides and populate its ancestor status.
parentRefs := GetParentReferences(targetedRoute)
parentRefs := GetManagedParentReferences(targetedRoute)
for _, p := range parentRefs {
if p.Kind == nil || *p.Kind == resource.KindGateway {
namespace := targetedRoute.GetNamespace()
Expand Down
33 changes: 24 additions & 9 deletions internal/gatewayapi/extensionserverpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (t *Translator) ProcessExtensionServerPolicies(policies []unstructured.Unst
}
if accepted {
res = append(res, *policy)
policy.Object["status"] = policyStatusToUnstructured(policyStatus)
policy.Object["status"] = PolicyStatusToUnstructured(policyStatus)
}
}

Expand All @@ -108,14 +108,6 @@ func extractTargetRefs(policy *unstructured.Unstructured, gateways []*GatewayCon
return ret, nil
}

func policyStatusToUnstructured(policyStatus gwapiv1.PolicyStatus) map[string]any {
ret := map[string]any{}
// No need to check the marshal/unmarshal error here
d, _ := json.Marshal(policyStatus)
_ = json.Unmarshal(d, &ret)
return ret
}

func resolveExtServerPolicyGatewayTargetRef(policy *unstructured.Unstructured, target gwapiv1.LocalPolicyTargetReferenceWithSectionName, gateways map[types.NamespacedName]*policyGatewayTargetContext) *GatewayContext {
// Check if the gateway exists
key := types.NamespacedName{
Expand All @@ -132,6 +124,29 @@ func resolveExtServerPolicyGatewayTargetRef(policy *unstructured.Unstructured, t
return gateway.GatewayContext
}

func PolicyStatusToUnstructured(policyStatus gwapiv1.PolicyStatus) map[string]any {
ret := map[string]any{}
// No need to check the marshal/unmarshal error here
d, _ := json.Marshal(policyStatus)
_ = json.Unmarshal(d, &ret)
return ret
}

func ExtServerPolicyStatusAsPolicyStatus(policy *unstructured.Unstructured) gwapiv1.PolicyStatus {
statusObj := policy.Object["status"]
status := gwapiv1.PolicyStatus{}
if _, ok := statusObj.(map[string]any); ok {
// No need to check the json marshal/unmarshal error, the policyStatus was
// created via a typed object so the marshalling/unmarshalling will always
// work
d, _ := json.Marshal(statusObj)
_ = json.Unmarshal(d, &status)
} else if _, ok := statusObj.(gwapiv1.PolicyStatus); ok {
status = statusObj.(gwapiv1.PolicyStatus)
}
return status
}

func (t *Translator) translateExtServerPolicyForGateway(
policy *unstructured.Unstructured,
gateway *GatewayContext,
Expand Down
Loading
Loading