From dbd47e3ee66a22a30e2257ad8923b8bc4f08081e Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Thu, 20 Jul 2023 19:57:44 -0700 Subject: [PATCH 01/12] feat: EnvoyPatchPolicy Status * compute the `Accepted` condition in the `gateway-api` layer * compute the `Programmed` condition in the `xds-translator` layer * Enhance Xds IR to hold the EnvoyPatchPolicy status * Publish the EnvoyPatchPolicy status from the `xds-translator` layer * `provider` subscribes to the status and writes it into the API server Signed-off-by: Arko Dasgupta --- api/v1alpha1/envoypatchpolicy_types.go | 28 ++ internal/cmd/server.go | 19 +- internal/gatewayapi/envoypatchpolicy.go | 82 +++- .../envoypatchpolicy-cross-ns-target.in.yaml | 35 ++ .../envoypatchpolicy-cross-ns-target.out.yaml | 74 ++++ ...voypatchpolicy-invalid-target-kind.in.yaml | 35 ++ ...oypatchpolicy-invalid-target-kind.out.yaml | 75 ++++ .../testdata/envoypatchpolicy-valid.out.yaml | 46 +- internal/ir/xds.go | 17 +- internal/ir/zz_generated.deepcopy.go | 40 +- internal/message/types.go | 7 + internal/provider/kubernetes/controller.go | 58 ++- internal/provider/kubernetes/kubernetes.go | 4 +- internal/provider/runner/runner.go | 5 +- internal/status/conditions_test.go | 4 +- internal/status/envoypatchpolicy.go | 42 ++ internal/status/gateway.go | 6 +- internal/status/gateway_test.go | 10 +- internal/utils/ptr/ptr.go | 10 + internal/xds/translator/jsonpatch.go | 404 +++++++++--------- internal/xds/translator/runner/runner.go | 18 +- .../testdata/in/xds-ir/jsonpatch.yaml | 106 ++--- internal/xds/translator/translator.go | 2 +- 23 files changed, 810 insertions(+), 317 deletions(-) create mode 100644 internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.in.yaml create mode 100755 internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml create mode 100644 internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.in.yaml create mode 100755 internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml create mode 100644 internal/status/envoypatchpolicy.go create mode 100644 internal/utils/ptr/ptr.go diff --git a/api/v1alpha1/envoypatchpolicy_types.go b/api/v1alpha1/envoypatchpolicy_types.go index 18bddc72f9..e8f820b397 100644 --- a/api/v1alpha1/envoypatchpolicy_types.go +++ b/api/v1alpha1/envoypatchpolicy_types.go @@ -122,6 +122,34 @@ type EnvoyPatchPolicyStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } +const ( + // PolicyConditionProgrammed indicates whether the policy has been translated + // and ready to be programmed into the data plane. + // + // Possible reasons for this condition to be True are: + // + // * "Programmed" + // + // Possible reasons for this condition to be False are: + // + // * "Invalid" + // * "ResourceNotFound" + // + PolicyConditionProgrammed gwapiv1a2.PolicyConditionType = "Programmed" + + // PolicyReasonProgrammed is used with the "Programmed" condition when the policy + // is ready to be programmed into the data plane. + PolicyReasonProgrammed gwapiv1a2.PolicyConditionReason = "Programmed" + + // PolicyReasonInvalid is used with the "Programmed" condition when the patch + // is syntactically or semantically invalid. + PolicyReasonInvalid gwapiv1a2.PolicyConditionReason = "Invalid" + + // PolicyReasonTargetNotFound is used with the "Programmed" condition when the + // policy cannot find the resource type to patch to. + PolicyReasonResourceNotFound gwapiv1a2.PolicyConditionReason = "ResourceNotFound" +) + //+kubebuilder:object:root=true // EnvoyPatchPolicyList contains a list of EnvoyPatchPolicy resources. diff --git a/internal/cmd/server.go b/internal/cmd/server.go index e3eaeea66c..863b7d1df3 100644 --- a/internal/cmd/server.go +++ b/internal/cmd/server.go @@ -124,12 +124,16 @@ func setupRunners(cfg *config.Server) error { } pResources := new(message.ProviderResources) + ePatchPolicyStatuses := new(message.EnvoyPatchPolicyStatuses) // Start the Provider Service // It fetches the resources from the configured provider type // and publishes it + // It also subscribes to status resources and once it receives + // a status resource back, it writes it out. providerRunner := providerrunner.New(&providerrunner.Config{ - Server: *cfg, - ProviderResources: pResources, + Server: *cfg, + ProviderResources: pResources, + EnvoyPatchPolicyStatuses: ePatchPolicyStatuses, }) if err := providerRunner.Start(ctx); err != nil { return err @@ -154,11 +158,13 @@ func setupRunners(cfg *config.Server) error { xds := new(message.Xds) // Start the Xds Translator Service // It subscribes to the xdsIR, translates it into xds Resources and publishes it. + // It also computes the EnvoyPatchPolicy statuses and publishes it. xdsTranslatorRunner := xdstranslatorrunner.New(&xdstranslatorrunner.Config{ - Server: *cfg, - XdsIR: xdsIR, - Xds: xds, - ExtensionManager: extMgr, + Server: *cfg, + XdsIR: xdsIR, + Xds: xds, + ExtensionManager: extMgr, + EnvoyPatchPolicyStatuses: ePatchPolicyStatuses, }) if err := xdsTranslatorRunner.Start(ctx); err != nil { return err @@ -206,6 +212,7 @@ func setupRunners(cfg *config.Server) error { <-ctx.Done() // Close messages pResources.Close() + ePatchPolicyStatuses.Close() xdsIR.Close() infraIR.Close() xds.Close() diff --git a/internal/gatewayapi/envoypatchpolicy.go b/internal/gatewayapi/envoypatchpolicy.go index ecd7863c98..4a9ff8cc6e 100644 --- a/internal/gatewayapi/envoypatchpolicy.go +++ b/internal/gatewayapi/envoypatchpolicy.go @@ -6,12 +6,16 @@ package gatewayapi import ( + "fmt" "sort" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/status" ) func (t *Translator) ProcessEnvoyPatchPolicies(envoyPatchPolicies []*egv1a1.EnvoyPatchPolicy, xdsIR XdsIRMap) { @@ -21,16 +25,19 @@ func (t *Translator) ProcessEnvoyPatchPolicies(envoyPatchPolicies []*egv1a1.Envo }) for _, policy := range envoyPatchPolicies { - // Ensure policy can only target a Gateway - if policy.Spec.TargetRef.Group != gwv1b1.GroupName || policy.Spec.TargetRef.Kind != KindGateway { - // TODO: Update Status - continue - } - // Ensure Policy and target Gateway are in the same namespace targetNs := policy.Spec.TargetRef.Namespace - if targetNs == nil || policy.Namespace != string(*targetNs) { - // TODO: Update Status + if targetNs == nil { + // This status condition will not get updated in the resource because + // we dont have access to the IR yet, but it has been kept here in case we publish + // the status from this layer instead of the xds layer. + + status.SetEnvoyPatchPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + "TargetRef.Namespace must be set", + ) continue } @@ -39,7 +46,54 @@ func (t *Translator) ProcessEnvoyPatchPolicies(envoyPatchPolicies []*egv1a1.Envo irKey := irStringKey(string(*targetNs), string(policy.Spec.TargetRef.Name)) gwXdsIR, ok := xdsIR[irKey] if !ok { - // TODO: Update Status + // This status condition will not get updated in the resource because + // the IR is missing, but it has been kept here in case we publish + // the status from this layer instead of the xds layer. + message := fmt.Sprintf("Gateway:%s not found.", policy.Spec.TargetRef.Name) + + status.SetEnvoyPatchPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonTargetNotFound, + message, + ) + continue + } + + // Create the IR with the context need to publish the status later + policyIR := ir.EnvoyPatchPolicy{} + policyIR.Name = policy.Name + policyIR.Namespace = policy.Namespace + policyIR.Status = &policy.Status + + // Append the IR + gwXdsIR.EnvoyPatchPolicies = append(gwXdsIR.EnvoyPatchPolicies, &policyIR) + + // Ensure policy can only target a Gateway + if policy.Spec.TargetRef.Group != gwv1b1.GroupName || policy.Spec.TargetRef.Kind != KindGateway { + message := fmt.Sprintf("TargetRef.Group:%s TargetRef.Kind:%s, only TargetRef.Group:%s and TargetRef.Kind:%s is supported.", + policy.Spec.TargetRef.Group, policy.Spec.TargetRef.Kind, gwv1b1.GroupName, KindGateway) + + status.SetEnvoyPatchPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) + continue + } + + // Ensure Policy and target Gateway are in the same namespace + if policy.Namespace != string(*targetNs) { + message := fmt.Sprintf("Namespace:%s TargetRef.Namespace:%s, EnvoyPatchPolicy can only target a Gateway in the same namespace.", + policy.Namespace, *targetNs) + + status.SetEnvoyPatchPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionFalse, + gwv1a2.PolicyReasonInvalid, + message, + ) continue } @@ -52,7 +106,15 @@ func (t *Translator) ProcessEnvoyPatchPolicies(envoyPatchPolicies []*egv1a1.Envo irPatch.Operation.Path = patch.Operation.Path irPatch.Operation.Value = patch.Operation.Value - gwXdsIR.JSONPatches = append(gwXdsIR.JSONPatches, &irPatch) + policyIR.JSONPatches = append(policyIR.JSONPatches, &irPatch) } + + // Set Accepted=True + status.SetEnvoyPatchPolicyCondition(policy, + gwv1a2.PolicyConditionAccepted, + metav1.ConditionTrue, + gwv1a2.PolicyReasonAccepted, + "EnvoyPatchPolicy has been accepted.", + ) } } diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.in.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.in.yaml new file mode 100644 index 0000000000..e44bc28e25 --- /dev/null +++ b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.in.yaml @@ -0,0 +1,35 @@ +envoyPatchPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway-2 + name: edit-conn-buffer-bytes + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/per_connection_buffer_limit_bytes" + value: "1024" +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml new file mode 100755 index 0000000000..73a87075e8 --- /dev/null +++ b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml @@ -0,0 +1,74 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + envoyPatchPolicies: + - Name: edit-conn-buffer-bytes + Namespace: envoy-gateway-2 + status: + conditions: + - lastTransitionTime: "2023-07-22T00:35:42Z" + message: Namespace:envoy-gateway-2 TargetRef.Namespace:envoy-gateway, EnvoyPatchPolicy + can only target a Gateway in the same namespace. + reason: Invalid + status: "False" + type: Accepted + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.in.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.in.yaml new file mode 100644 index 0000000000..7506737e8c --- /dev/null +++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.in.yaml @@ -0,0 +1,35 @@ +envoyPatchPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyPatchPolicy + metadata: + namespace: envoy-gateway + name: edit-conn-buffer-bytes + spec: + type: "JSONPatch" + targetRef: + group: gateway.networking.k8s.io + kind: MyGateway + name: gateway-1 + namespace: envoy-gateway + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "envoy-gateway-gateway-1-http" + operation: + op: replace + path: "/per_connection_buffer_limit_bytes" + value: "1024" +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml new file mode 100755 index 0000000000..2b9f09f3dd --- /dev/null +++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml @@ -0,0 +1,75 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 0 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: "" + ports: + - containerPort: 10080 + name: http + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + envoyPatchPolicies: + - Name: edit-conn-buffer-bytes + Namespace: envoy-gateway + status: + conditions: + - lastTransitionTime: "2023-07-22T00:35:42Z" + message: TargetRef.Group:gateway.networking.k8s.io TargetRef.Kind:MyGateway, + only TargetRef.Group:gateway.networking.k8s.io and TargetRef.Kind:Gateway + is supported. + reason: Invalid + status: "False" + type: Accepted + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: envoy-gateway/gateway-1/http + port: 10080 diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml index da74aa6614..8bcc9e56e2 100644 --- a/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml @@ -54,6 +54,39 @@ xdsIR: accessLog: text: - path: /dev/stdout + envoyPatchPolicies: + - Name: edit-ignore-global-limit + Namespace: envoy-gateway + jsonPatches: + - name: envoy-gateway-gateway-1-http + operation: + op: replace + path: /ignore_global_conn_limit + value: "true" + type: type.googleapis.com/envoy.config.listener.v3.Listener + status: + conditions: + - lastTransitionTime: "2023-07-22T00:35:42Z" + message: EnvoyPatchPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - Name: edit-conn-buffer-bytes + Namespace: envoy-gateway + jsonPatches: + - name: envoy-gateway-gateway-1-http + operation: + op: replace + path: /per_connection_buffer_limit_bytes + value: "1024" + type: type.googleapis.com/envoy.config.listener.v3.Listener + status: + conditions: + - lastTransitionTime: "2023-07-22T00:35:42Z" + message: EnvoyPatchPolicy has been accepted. + reason: Accepted + status: "True" + type: Accepted http: - address: 0.0.0.0 hostnames: @@ -61,16 +94,3 @@ xdsIR: isHTTP2: false name: envoy-gateway/gateway-1/http port: 10080 - jsonPatches: - - name: envoy-gateway-gateway-1-http - operation: - op: replace - path: /ignore_global_conn_limit - value: "true" - type: type.googleapis.com/envoy.config.listener.v3.Listener - - name: envoy-gateway-gateway-1-http - operation: - op: replace - path: /per_connection_buffer_limit_bytes - value: "1024" - type: type.googleapis.com/envoy.config.listener.v3.Listener diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 92c75aee58..2284f9c892 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -13,6 +13,7 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + ktypes "k8s.io/apimachinery/pkg/types" egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" @@ -58,9 +59,8 @@ type Xds struct { TCP []*TCPListener `json:"tcp,omitempty" yaml:"tcp,omitempty"` // UDP Listeners exposed by the gateway. UDP []*UDPListener `json:"udp,omitempty" yaml:"udp,omitempty"` - // JSONPatches are the JSON Patches that - // are to be applied to generaed Xds linked to the gateway. - JSONPatches []*JSONPatchConfig `json:"jsonPatches,omitempty" yaml:"jsonPatches,omitempty"` + // EnvoyPatchPolicies is the intermediate representation of the EnvoyPatchPolicy resource + EnvoyPatchPolicies []*EnvoyPatchPolicy `json:"envoyPatchPolicies,omitempty" yaml:"envoyPatchPolicies,omitempty"` } // Validate the fields within the Xds structure. @@ -800,6 +800,17 @@ type OpenTelemetryAccessLog struct { Resources map[string]string `json:"resources,omitempty" yaml:"resources,omitempty"` } +// EnvoyPatchPolicy defines the intermediate representation of the EnvoyPatchPolicy resource. +// +k8s:deepcopy-gen=true +type EnvoyPatchPolicy struct { + ktypes.NamespacedName + // Status of the EnvoyPatchPolicy + Status *egv1a1.EnvoyPatchPolicyStatus `json:"status,omitempty" yaml:"status,omitempty"` + // JSONPatches are the JSON Patches that + // are to be applied to generaed Xds linked to the gateway. + JSONPatches []*JSONPatchConfig `json:"jsonPatches,omitempty" yaml:"jsonPatches,omitempty"` +} + // JSONPatchConfig defines the configuration for patching a Envoy xDS Resource // using JSONPatch semantics // +k8s:deepcopy-gen=true diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 1520f0e2b0..26946c3b0f 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -98,6 +98,38 @@ func (in *DirectResponse) DeepCopy() *DirectResponse { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPatchPolicy) DeepCopyInto(out *EnvoyPatchPolicy) { + *out = *in + out.NamespacedName = in.NamespacedName + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(apiv1alpha1.EnvoyPatchPolicyStatus) + (*in).DeepCopyInto(*out) + } + if in.JSONPatches != nil { + in, out := &in.JSONPatches, &out.JSONPatches + *out = make([]*JSONPatchConfig, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(JSONPatchConfig) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicy. +func (in *EnvoyPatchPolicy) DeepCopy() *EnvoyPatchPolicy { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GlobalRateLimit) DeepCopyInto(out *GlobalRateLimit) { *out = *in @@ -981,13 +1013,13 @@ func (in *Xds) DeepCopyInto(out *Xds) { } } } - if in.JSONPatches != nil { - in, out := &in.JSONPatches, &out.JSONPatches - *out = make([]*JSONPatchConfig, len(*in)) + if in.EnvoyPatchPolicies != nil { + in, out := &in.EnvoyPatchPolicies, &out.EnvoyPatchPolicies + *out = make([]*EnvoyPatchPolicy, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] - *out = new(JSONPatchConfig) + *out = new(EnvoyPatchPolicy) (*in).DeepCopyInto(*out) } } diff --git a/internal/message/types.go b/internal/message/types.go index fd54aa57e4..ef97667fad 100644 --- a/internal/message/types.go +++ b/internal/message/types.go @@ -11,6 +11,7 @@ import ( gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi" "github.com/envoyproxy/gateway/internal/ir" xdstypes "github.com/envoyproxy/gateway/internal/xds/types" @@ -54,11 +55,17 @@ func (p *ProviderResources) Close() { p.GatewayAPIResources.Close() p.GatewayStatuses.Close() p.HTTPRouteStatuses.Close() + p.GRPCRouteStatuses.Close() p.TLSRouteStatuses.Close() p.TCPRouteStatuses.Close() p.UDPRouteStatuses.Close() } +// EnvoyPatchPolicyStatuses message +type EnvoyPatchPolicyStatuses struct { + watchable.Map[types.NamespacedName, *egv1a1.EnvoyPatchPolicyStatus] +} + // XdsIR message type XdsIR struct { watchable.Map[string, *ir.Xds] diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 7ede2a01ab..107b21c010 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -65,12 +65,14 @@ type gatewayAPIReconciler struct { namespace string envoyGateway *egcfgv1a1.EnvoyGateway - resources *message.ProviderResources - extGVKs []schema.GroupVersionKind + resources *message.ProviderResources + envoyPatchPolicyStatuses *message.EnvoyPatchPolicyStatuses + extGVKs []schema.GroupVersionKind } // newGatewayAPIController -func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status.Updater, resources *message.ProviderResources) error { +func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status.Updater, + resources *message.ProviderResources, eStatuses *message.EnvoyPatchPolicyStatuses) error { ctx := context.Background() // Gather additional resources to watch from registered extensions @@ -83,15 +85,16 @@ func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su status. } r := &gatewayAPIReconciler{ - client: mgr.GetClient(), - log: cfg.Logger, - classController: gwapiv1b1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName), - namespace: cfg.Namespace, - statusUpdater: su, - resources: resources, - extGVKs: extGVKs, - store: newProviderStore(), - envoyGateway: cfg.EnvoyGateway, + client: mgr.GetClient(), + log: cfg.Logger, + classController: gwapiv1b1.GatewayController(cfg.EnvoyGateway.Gateway.ControllerName), + namespace: cfg.Namespace, + statusUpdater: su, + resources: resources, + envoyPatchPolicyStatuses: eStatuses, + extGVKs: extGVKs, + store: newProviderStore(), + envoyGateway: cfg.EnvoyGateway, } c, err := controller.New("gatewayapi", mgr, controller.Options{Reconciler: r}) @@ -242,6 +245,9 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, request reconcile. } for _, policy := range envoyPatchPolicies.Items { policy := policy + // Discard Status to reduce memory consumption in watchable + // It will be recomputed by the gateway-api layer + policy.Status = egv1a1.EnvoyPatchPolicyStatus{} resourceTree.EnvoyPatchPolicies = append(resourceTree.EnvoyPatchPolicies, &policy) } @@ -1067,6 +1073,34 @@ func (r *gatewayAPIReconciler) subscribeAndUpdateStatus(ctx context.Context) { ) r.log.Info("udpRoute status subscriber shutting down") }() + + // EnvoyPatchPolicy object status updater + go func() { + message.HandleSubscription(r.envoyPatchPolicyStatuses.Subscribe(ctx), + func(update message.Update[types.NamespacedName, *egv1a1.EnvoyPatchPolicyStatus]) { + // skip delete updates. + if update.Delete { + return + } + key := update.Key + val := update.Value + r.statusUpdater.Send(status.Update{ + NamespacedName: key, + Resource: new(egv1a1.EnvoyPatchPolicy), + Mutator: status.MutatorFunc(func(obj client.Object) client.Object { + t, ok := obj.(*egv1a1.EnvoyPatchPolicy) + if !ok { + panic(fmt.Sprintf("unsupported object type %T", obj)) + } + tCopy := t.DeepCopy() + tCopy.Status = *val + return tCopy + }), + }) + }, + ) + r.log.Info("envoyPatchPolicy status subscriber shutting down") + }() } // watchResources watches gateway api resources. diff --git a/internal/provider/kubernetes/kubernetes.go b/internal/provider/kubernetes/kubernetes.go index 0140e1d22d..96fea41993 100644 --- a/internal/provider/kubernetes/kubernetes.go +++ b/internal/provider/kubernetes/kubernetes.go @@ -30,7 +30,7 @@ type Provider struct { } // New creates a new Provider from the provided EnvoyGateway. -func New(cfg *rest.Config, svr *config.Server, resources *message.ProviderResources) (*Provider, error) { +func New(cfg *rest.Config, svr *config.Server, resources *message.ProviderResources, eStatuses *message.EnvoyPatchPolicyStatuses) (*Provider, error) { // TODO: Decide which mgr opts should be exposed through envoygateway.provider.kubernetes API. mgrOpts := manager.Options{ Scheme: envoygateway.GetScheme(), @@ -59,7 +59,7 @@ func New(cfg *rest.Config, svr *config.Server, resources *message.ProviderResour } // Create and register the controllers with the manager. - if err := newGatewayAPIController(mgr, svr, updateHandler.Writer(), resources); err != nil { + if err := newGatewayAPIController(mgr, svr, updateHandler.Writer(), resources, eStatuses); err != nil { return nil, fmt.Errorf("failted to create gatewayapi controller: %w", err) } diff --git a/internal/provider/runner/runner.go b/internal/provider/runner/runner.go index 72ab02e581..5a6880337b 100644 --- a/internal/provider/runner/runner.go +++ b/internal/provider/runner/runner.go @@ -19,7 +19,8 @@ import ( type Config struct { config.Server - ProviderResources *message.ProviderResources + ProviderResources *message.ProviderResources + EnvoyPatchPolicyStatuses *message.EnvoyPatchPolicyStatuses } type Runner struct { @@ -43,7 +44,7 @@ func (r *Runner) Start(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to get kubeconfig: %w", err) } - p, err := kubernetes.New(cfg, &r.Config.Server, r.ProviderResources) + p, err := kubernetes.New(cfg, &r.Config.Server, r.ProviderResources, r.EnvoyPatchPolicyStatuses) if err != nil { return fmt.Errorf("failed to create provider %s: %w", v1alpha1.ProviderTypeKubernetes, err) } diff --git a/internal/status/conditions_test.go b/internal/status/conditions_test.go index 55d0aefeda..1dc4b3c5e0 100644 --- a/internal/status/conditions_test.go +++ b/internal/status/conditions_test.go @@ -24,7 +24,7 @@ import ( fakeclock "k8s.io/utils/clock/testing" gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - "github.com/envoyproxy/gateway/internal/gatewayapi" + "github.com/envoyproxy/gateway/internal/utils/ptr" ) var clock utilclock.Clock = utilclock.RealClock{} @@ -325,7 +325,7 @@ func TestGatewayReadyCondition(t *testing.T) { gtw.Status = gwapiv1b1.GatewayStatus{ Addresses: []gwapiv1b1.GatewayAddress{ { - Type: gatewayapi.GatewayAddressTypePtr(gwapiv1b1.IPAddressType), + Type: ptr.To(gwapiv1b1.IPAddressType), Value: "1.1.1.1", }, }, diff --git a/internal/status/envoypatchpolicy.go b/internal/status/envoypatchpolicy.go new file mode 100644 index 0000000000..950ce1f58a --- /dev/null +++ b/internal/status/envoypatchpolicy.go @@ -0,0 +1,42 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package status + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" +) + +func SetEnvoyPatchPolicyCondition(e *egv1a1.EnvoyPatchPolicy, conditionType gwv1a2.PolicyConditionType, status metav1.ConditionStatus, reason gwv1a2.PolicyConditionReason, message string) { + cond := newCondition(string(conditionType), status, string(reason), message, time.Now(), e.Generation) + e.Status.Conditions = MergeConditions(e.Status.Conditions, cond) +} + +func SetEnvoyPatchPolicyProgrammedIfUnset(s *egv1a1.EnvoyPatchPolicyStatus, message string) { + // Return early if Programmed condition is already set + for _, c := range s.Conditions { + if c.Type == string(egv1a1.PolicyConditionProgrammed) { + return + } + } + + cond := newCondition(string(egv1a1.PolicyConditionProgrammed), metav1.ConditionTrue, string(egv1a1.PolicyReasonProgrammed), message, time.Now(), 0) + s.Conditions = MergeConditions(s.Conditions, cond) +} + +func SetEnvoyPatchPolicyInvalid(s *egv1a1.EnvoyPatchPolicyStatus, message string) { + cond := newCondition(string(egv1a1.PolicyConditionProgrammed), metav1.ConditionFalse, string(egv1a1.PolicyReasonInvalid), message, time.Now(), 0) + s.Conditions = MergeConditions(s.Conditions, cond) +} + +func SetEnvoyPatchPolicyResourceNotFound(s *egv1a1.EnvoyPatchPolicyStatus, message string) { + cond := newCondition(string(egv1a1.PolicyConditionProgrammed), metav1.ConditionFalse, string(egv1a1.PolicyReasonResourceNotFound), message, time.Now(), 0) + s.Conditions = MergeConditions(s.Conditions, cond) +} diff --git a/internal/status/gateway.go b/internal/status/gateway.go index a998f62446..d7f5695f72 100644 --- a/internal/status/gateway.go +++ b/internal/status/gateway.go @@ -10,7 +10,7 @@ import ( corev1 "k8s.io/api/core/v1" gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - "github.com/envoyproxy/gateway/internal/gatewayapi" + "github.com/envoyproxy/gateway/internal/utils/ptr" ) // UpdateGatewayStatusAcceptedCondition updates the status condition for the provided Gateway based on the accepted state. @@ -67,7 +67,7 @@ func UpdateGatewayStatusProgrammedCondition(gw *gwapiv1b1.Gateway, svc *corev1.S var gwAddresses []gwapiv1b1.GatewayAddress for i := range addresses { addr := gwapiv1b1.GatewayAddress{ - Type: gatewayapi.GatewayAddressTypePtr(gwapiv1b1.IPAddressType), + Type: ptr.To(gwapiv1b1.IPAddressType), Value: addresses[i], } gwAddresses = append(gwAddresses, addr) @@ -75,7 +75,7 @@ func UpdateGatewayStatusProgrammedCondition(gw *gwapiv1b1.Gateway, svc *corev1.S for i := range hostnames { addr := gwapiv1b1.GatewayAddress{ - Type: gatewayapi.GatewayAddressTypePtr(gwapiv1b1.HostnameAddressType), + Type: ptr.To(gwapiv1b1.HostnameAddressType), Value: hostnames[i], } gwAddresses = append(gwAddresses, addr) diff --git a/internal/status/gateway_test.go b/internal/status/gateway_test.go index fdb04eb03a..6b404cd88d 100644 --- a/internal/status/gateway_test.go +++ b/internal/status/gateway_test.go @@ -15,7 +15,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" - "github.com/envoyproxy/gateway/internal/gatewayapi" + "github.com/envoyproxy/gateway/internal/utils/ptr" ) func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { @@ -60,7 +60,7 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { }, addresses: []gwapiv1b1.GatewayAddress{ { - Type: gatewayapi.GatewayAddressTypePtr(gwapiv1b1.IPAddressType), + Type: ptr.To(gwapiv1b1.IPAddressType), Value: "127.0.0.1", }, }, @@ -89,11 +89,11 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { }, addresses: []gwapiv1b1.GatewayAddress{ { - Type: gatewayapi.GatewayAddressTypePtr(gwapiv1b1.IPAddressType), + Type: ptr.To(gwapiv1b1.IPAddressType), Value: "127.0.0.1", }, { - Type: gatewayapi.GatewayAddressTypePtr(gwapiv1b1.HostnameAddressType), + Type: ptr.To(gwapiv1b1.HostnameAddressType), Value: "localhost", }, }, @@ -113,7 +113,7 @@ func TestUpdateGatewayStatusProgrammedCondition(t *testing.T) { }, addresses: []gwapiv1b1.GatewayAddress{ { - Type: gatewayapi.GatewayAddressTypePtr(gwapiv1b1.IPAddressType), + Type: ptr.To(gwapiv1b1.IPAddressType), Value: "127.0.0.1", }, }, diff --git a/internal/utils/ptr/ptr.go b/internal/utils/ptr/ptr.go new file mode 100644 index 0000000000..368c36c5b8 --- /dev/null +++ b/internal/utils/ptr/ptr.go @@ -0,0 +1,10 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package ptr + +func To[T any](in T) *T { + return &in +} diff --git a/internal/xds/translator/jsonpatch.go b/internal/xds/translator/jsonpatch.go index d5588ac50f..16ca4a5c14 100644 --- a/internal/xds/translator/jsonpatch.go +++ b/internal/xds/translator/jsonpatch.go @@ -19,6 +19,7 @@ import ( "sigs.k8s.io/yaml" "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/status" _ "github.com/envoyproxy/gateway/internal/xds/extensions" // register the generated types to support protojson unmarshalling "github.com/envoyproxy/gateway/internal/xds/types" ) @@ -29,257 +30,262 @@ const ( ) // processJSONPatches applies each JSONPatch to the Xds Resources for a specific type. -func processJSONPatches(tCtx *types.ResourceVersionTable, jsonPatches []*ir.JSONPatchConfig) error { +func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []*ir.EnvoyPatchPolicy) error { var errs error m := protojson.MarshalOptions{ UseProtoNames: true, } - for _, p := range jsonPatches { - var ( - listener *listenerv3.Listener - routeConfig *routev3.RouteConfiguration - cluster *clusterv3.Cluster - endpoint *endpointv3.ClusterLoadAssignment - resourceJSON []byte - err error - ) + for _, e := range envoyPatchPolicies { + for _, p := range e.JSONPatches { + var ( + listener *listenerv3.Listener + routeConfig *routev3.RouteConfiguration + cluster *clusterv3.Cluster + endpoint *endpointv3.ClusterLoadAssignment + resourceJSON []byte + err error + ) + + // If Path is "" and op is "add", unmarshal and add the patch as a complete + // resource + if p.Operation.Op == AddOperation && p.Operation.Path == EmptyPath { + // Convert patch to JSON + // The patch library expects an array so convert it into one + y, err := yaml.Marshal(p.Operation.Value) + if err != nil { + err := fmt.Errorf("unable to marshal patch %+v, err: %v", p.Operation.Value, err) + errs = multierror.Append(errs, err) + continue + } + jsonBytes, err := yaml.YAMLToJSON(y) + if err != nil { + err := fmt.Errorf("unable to convert patch to json %s, err: %v", string(y), err) + errs = multierror.Append(errs, err) + continue + } + switch p.Type { + case string(resourcev3.ListenerType): + temp := &listenerv3.Listener{} + if err = protojson.Unmarshal(jsonBytes, temp); err != nil { + err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + errs = multierror.Append(errs, err) + continue + } + if err = temp.Validate(); err != nil { + err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + errs = multierror.Append(errs, err) + continue + } + + tCtx.AddXdsResource(resourcev3.ListenerType, temp) + case string(resourcev3.RouteType): + temp := &routev3.RouteConfiguration{} + if err = protojson.Unmarshal(jsonBytes, temp); err != nil { + err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + errs = multierror.Append(errs, err) + continue + } + if err = temp.Validate(); err != nil { + err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + errs = multierror.Append(errs, err) + continue + } + tCtx.AddXdsResource(resourcev3.RouteType, temp) + case string(resourcev3.ClusterType): + temp := &clusterv3.Cluster{} + if err = protojson.Unmarshal(jsonBytes, temp); err != nil { + err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + errs = multierror.Append(errs, err) + continue + } + if err = temp.Validate(); err != nil { + err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + errs = multierror.Append(errs, err) + continue + } + tCtx.AddXdsResource(resourcev3.ClusterType, temp) + case string(resourcev3.EndpointType): + temp := &endpointv3.ClusterLoadAssignment{} + if err = protojson.Unmarshal(jsonBytes, temp); err != nil { + err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + errs = multierror.Append(errs, err) + continue + } + if err = temp.Validate(); err != nil { + err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + errs = multierror.Append(errs, err) + continue + } + tCtx.AddXdsResource(resourcev3.EndpointType, temp) + } + + // Skip further processing + continue + } + // Find the resource to patch and convert it to JSON + switch p.Type { + case string(resourcev3.ListenerType): + if listener = findXdsListener(tCtx, p.Name); listener == nil { + msg := fmt.Sprintf("unable to find xds resource %s: %s", p.Type, p.Name) + status.SetEnvoyPatchPolicyResourceNotFound(e.Status, msg) + continue + } + + if resourceJSON, err = m.Marshal(listener); err != nil { + err := fmt.Errorf("unable to marshal xds resource %s: %s, err:%v", p.Type, p.Name, err) + errs = multierror.Append(errs, err) + continue + } + + case string(resourcev3.RouteType): + if routeConfig = findXdsRouteConfig(tCtx, p.Name); routeConfig == nil { + msg := fmt.Sprintf("unable to find xds resource %s: %s", p.Type, p.Name) + status.SetEnvoyPatchPolicyResourceNotFound(e.Status, msg) + continue + } + + if resourceJSON, err = m.Marshal(routeConfig); err != nil { + err = fmt.Errorf("unable to marshal xds resource %s: %s, err:%v", p.Type, p.Name, err) + errs = multierror.Append(errs, err) + continue + } + + case string(resourcev3.ClusterType): + if cluster = findXdsCluster(tCtx, p.Name); cluster == nil { + msg := fmt.Sprintf("unable to find xds resource %s: %s", p.Type, p.Name) + status.SetEnvoyPatchPolicyResourceNotFound(e.Status, msg) + continue + } + + if resourceJSON, err = m.Marshal(cluster); err != nil { + err = fmt.Errorf("unable to marshal xds resource %s: %s, err:%v", p.Type, p.Name, err) + errs = multierror.Append(errs, err) + continue + } + case string(resourcev3.EndpointType): + if endpoint = findXdsEndpoint(tCtx, p.Name); endpoint == nil { + msg := fmt.Sprintf("unable to find xds resource %s: %s", p.Type, p.Name) + status.SetEnvoyPatchPolicyResourceNotFound(e.Status, msg) + continue + } + if resourceJSON, err = m.Marshal(endpoint); err != nil { + err = fmt.Errorf("unable to marshal xds resource %s: %s, err:%v", p.Type, p.Name, err) + errs = multierror.Append(errs, err) + continue + } + } - // If Path is "" and op is "add", unmarshal and add the patch as a complete - // resource - if p.Operation.Op == AddOperation && p.Operation.Path == EmptyPath { // Convert patch to JSON // The patch library expects an array so convert it into one - y, err := yaml.Marshal(p.Operation.Value) + y, err := yaml.Marshal([]ir.JSONPatchOperation{p.Operation}) if err != nil { - err := fmt.Errorf("unable to marshal patch %+v, err: %v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("unable to marshal patch %+v, err: %v", p.Operation, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } jsonBytes, err := yaml.YAMLToJSON(y) if err != nil { - err := fmt.Errorf("unable to convert patch to json %s, err: %v", string(y), err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("unable to convert patch to json %s, err: %v", string(y), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) + continue + } + + patchObj, err := jsonpatchv5.DecodePatch(jsonBytes) + if err != nil { + msg := fmt.Sprintf("unable to decode patch %s, err: %v", string(jsonBytes), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) + continue + } + + // Apply patch + opts := jsonpatchv5.NewApplyOptions() + opts.EnsurePathExistsOnAdd = true + modifiedJSON, err := patchObj.ApplyWithOptions(resourceJSON, opts) + if err != nil { + msg := fmt.Sprintf("unable to apply patch:\n%s on resource:\n%s, err: %v", string(jsonBytes), string(resourceJSON), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } + + // Unmarshal back to typed resource + // Use a temp staging variable that can be marshalled + // into and validated before saving it into the xds output resource switch p.Type { case string(resourcev3.ListenerType): temp := &listenerv3.Listener{} - if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { + msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) + continue + } + if err = deepCopyPtr(temp, listener); err != nil { + err := fmt.Errorf("unable to copy xds resource %s, err:%v", string(modifiedJSON), err) errs = multierror.Append(errs, err) continue } - - tCtx.AddXdsResource(resourcev3.ListenerType, temp) case string(resourcev3.RouteType): temp := &routev3.RouteConfiguration{} - if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { + msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) + continue + } + if err = deepCopyPtr(temp, routeConfig); err != nil { + err := fmt.Errorf("unable to copy xds resource %s, err:%v", string(modifiedJSON), err) errs = multierror.Append(errs, err) continue } - tCtx.AddXdsResource(resourcev3.RouteType, temp) case string(resourcev3.ClusterType): temp := &clusterv3.Cluster{} - if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { + err := fmt.Errorf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) errs = multierror.Append(errs, err) continue } if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) + continue + } + if err = deepCopyPtr(temp, cluster); err != nil { + err := fmt.Errorf("unable to copy xds resource %s, err:%v", string(modifiedJSON), err) errs = multierror.Append(errs, err) continue } - tCtx.AddXdsResource(resourcev3.ClusterType, temp) case string(resourcev3.EndpointType): temp := &endpointv3.ClusterLoadAssignment{} - if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { + msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) + continue + } + if err = deepCopyPtr(temp, endpoint); err != nil { + err := fmt.Errorf("unable to copy xds resource %s, err:%v", string(modifiedJSON), err) errs = multierror.Append(errs, err) continue } - tCtx.AddXdsResource(resourcev3.EndpointType, temp) - } - - // Skip further processing - continue - } - // Find the resource to patch and convert it to JSON - switch p.Type { - case string(resourcev3.ListenerType): - if listener = findXdsListener(tCtx, p.Name); listener == nil { - err = fmt.Errorf("unable to find xds resource %s: %s", p.Type, p.Name) - errs = multierror.Append(errs, err) - continue - } - - if resourceJSON, err = m.Marshal(listener); err != nil { - err = fmt.Errorf("unable to marshal xds resource %s: %s, err:%v", p.Type, p.Name, err) - errs = multierror.Append(errs, err) - continue - } - - case string(resourcev3.RouteType): - if routeConfig = findXdsRouteConfig(tCtx, p.Name); routeConfig == nil { - err := fmt.Errorf("unable to find xds resource %s: %s", p.Type, p.Name) - errs = multierror.Append(errs, err) - continue - } - - if resourceJSON, err = m.Marshal(routeConfig); err != nil { - err = fmt.Errorf("unable to marshal xds resource %s: %s, err:%v", p.Type, p.Name, err) - errs = multierror.Append(errs, err) - continue - } - - case string(resourcev3.ClusterType): - if cluster := findXdsCluster(tCtx, p.Name); cluster == nil { - err := fmt.Errorf("unable to find xds resource %s: %s", p.Type, p.Name) - errs = multierror.Append(errs, err) - continue - } - - if resourceJSON, err = m.Marshal(cluster); err != nil { - err = fmt.Errorf("unable to marshal xds resource %s: %s, err:%v", p.Type, p.Name, err) - errs = multierror.Append(errs, err) - continue } - case string(resourcev3.EndpointType): - endpoint = findXdsEndpoint(tCtx, p.Name) - if endpoint == nil { - err = fmt.Errorf("unable to marshal xds resource %s: %s, err:%v", p.Type, p.Name, err) - errs = multierror.Append(errs, err) - continue - } - if resourceJSON, err = m.Marshal(endpoint); err != nil { - err = fmt.Errorf("unable to marshal xds resource %s: %s, err:%v", p.Type, p.Name, err) - errs = multierror.Append(errs, err) - continue - } - } - - // Convert patch to JSON - // The patch library expects an array so convert it into one - y, err := yaml.Marshal([]ir.JSONPatchOperation{p.Operation}) - if err != nil { - err := fmt.Errorf("unable to marshal patch %+v, err: %v", p.Operation, err) - errs = multierror.Append(errs, err) - continue - } - jsonBytes, err := yaml.YAMLToJSON(y) - if err != nil { - err := fmt.Errorf("unable to convert patch to json %s, err: %v", string(y), err) - errs = multierror.Append(errs, err) - continue } - patchObj, err := jsonpatchv5.DecodePatch(jsonBytes) - if err != nil { - err := fmt.Errorf("unable to decode patch %s, err: %v", string(jsonBytes), err) - errs = multierror.Append(errs, err) - continue - } - - // Apply patch - opts := jsonpatchv5.NewApplyOptions() - opts.EnsurePathExistsOnAdd = true - modifiedJSON, err := patchObj.ApplyWithOptions(resourceJSON, opts) - if err != nil { - err := fmt.Errorf("unable to apply patch:\n%s on resource:\n%s, err: %v", string(jsonBytes), string(resourceJSON), err) - errs = multierror.Append(errs, err) - continue - } - - // Unmarshal back to typed resource - // Use a temp staging variable that can be marshalled - // into and validated before saving it into the xds output resource - switch p.Type { - case string(resourcev3.ListenerType): - temp := &listenerv3.Listener{} - if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - if err = deepCopyPtr(temp, listener); err != nil { - err := fmt.Errorf("unable to copy xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - case string(resourcev3.RouteType): - temp := &routev3.RouteConfiguration{} - if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - if err = deepCopyPtr(temp, routeConfig); err != nil { - err := fmt.Errorf("unable to copy xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - case string(resourcev3.ClusterType): - temp := &clusterv3.Cluster{} - if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - if err = deepCopyPtr(temp, cluster); err != nil { - err := fmt.Errorf("unable to copy xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - case string(resourcev3.EndpointType): - temp := &endpointv3.ClusterLoadAssignment{} - if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - if err = deepCopyPtr(temp, endpoint); err != nil { - err := fmt.Errorf("unable to copy xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) - continue - } - } + // Set Programmed condition if not yet set + status.SetEnvoyPatchPolicyProgrammedIfUnset(e.Status, "successfully applied patches.") } + return errs } diff --git a/internal/xds/translator/runner/runner.go b/internal/xds/translator/runner/runner.go index d5a6677e20..f8a9aee6a2 100644 --- a/internal/xds/translator/runner/runner.go +++ b/internal/xds/translator/runner/runner.go @@ -8,6 +8,8 @@ package runner import ( "context" + ktypes "k8s.io/apimachinery/pkg/types" + "github.com/envoyproxy/gateway/api/config/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway/config" extension "github.com/envoyproxy/gateway/internal/extension/types" @@ -19,9 +21,10 @@ import ( type Config struct { config.Server - XdsIR *message.XdsIR - Xds *message.Xds - ExtensionManager extension.Manager + XdsIR *message.XdsIR + Xds *message.Xds + EnvoyPatchPolicyStatuses *message.EnvoyPatchPolicyStatuses + ExtensionManager extension.Manager } type Runner struct { @@ -77,6 +80,15 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { // Publish r.Xds.Store(key, result) } + + // Publish EnvoyPatchPolicyStatus + for _, e := range val.EnvoyPatchPolicies { + key := ktypes.NamespacedName{ + Name: e.Name, + Namespace: e.Namespace, + } + r.EnvoyPatchPolicyStatuses.Store(key, e.Status) + } } }, ) diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml index baeec6e056..ea662f3916 100644 --- a/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml @@ -1,55 +1,57 @@ -jsonPatches: -- type: "type.googleapis.com/envoy.config.listener.v3.Listener" - name: "first-listener" - operation: - op: "add" - path: "/default_filter_chain/filters/0/typed_config/http_filters/0" - value: - name: "envoy.filters.http.ratelimit" - typed_config: - "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" - domain: "eg-ratelimit" - failure_mode_deny: true - timeout: 1s - rate_limit_service: - grpc_service: - envoy_grpc: - cluster_name: rate-limit-cluster - transport_api_version: V3 -- type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" - name: "first-listener" - operation: - op: "add" - path: "/virtual_hosts/0/rate_limits" - value: - - actions: - - remote_address: {} -- type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" - name: rate-limit-cluster - operation: - op: add - path: "" - value: - name: rate-limit-cluster - type: STRICT_DNS - connect_timeout: 10s - lb_policy: ROUND_ROBIN - http2_protocol_options: {} - load_assignment: - cluster_name: rate-limit-cluster - endpoints: - - lb_endpoints: - - endpoint: - address: - socket_address: - address: ratelimit.svc.cluster.local - port_value: 8081 -- type: "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment" - name: "first-route" - operation: - op: "replace" - path: "/endpoints/0/load_balancing_weight" - value: "50" +envoyPatchPolicies: +- status: {} + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "first-listener" + operation: + op: "add" + path: "/default_filter_chain/filters/0/typed_config/http_filters/0" + value: + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eg-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 + - type: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration" + name: "first-listener" + operation: + op: "add" + path: "/virtual_hosts/0/rate_limits" + value: + - actions: + - remote_address: {} + - type: "type.googleapis.com/envoy.config.cluster.v3.Cluster" + name: rate-limit-cluster + operation: + op: add + path: "" + value: + name: rate-limit-cluster + type: STRICT_DNS + connect_timeout: 10s + lb_policy: ROUND_ROBIN + http2_protocol_options: {} + load_assignment: + cluster_name: rate-limit-cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: ratelimit.svc.cluster.local + port_value: 8081 + - type: "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment" + name: "first-route" + operation: + op: "replace" + path: "/endpoints/0/load_balancing_weight" + value: "50" http: - name: "first-listener" address: "0.0.0.0" diff --git a/internal/xds/translator/translator.go b/internal/xds/translator/translator.go index f67ce3a8e3..625dbfcc5a 100644 --- a/internal/xds/translator/translator.go +++ b/internal/xds/translator/translator.go @@ -59,7 +59,7 @@ func (t *Translator) Translate(ir *ir.Xds) (*types.ResourceVersionTable, error) return nil, err } - if err := processJSONPatches(tCtx, ir.JSONPatches); err != nil { + if err := processJSONPatches(tCtx, ir.EnvoyPatchPolicies); err != nil { return nil, err } From 93b9704bb60bf93253b9e43935c6e4f26f482fe5 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Mon, 24 Jul 2023 14:15:52 -0700 Subject: [PATCH 02/12] fix testdata Signed-off-by: Arko Dasgupta --- .../envoypatchpolicy-cross-ns-target.out.yaml | 6 +++--- ...envoypatchpolicy-invalid-target-kind.out.yaml | 6 +++--- .../testdata/envoypatchpolicy-valid.out.yaml | 16 ++++++++-------- internal/gatewayapi/translator_test.go | 1 - internal/ir/xds.go | 4 ++-- internal/ir/zz_generated.deepcopy.go | 1 - internal/utils/field/field.go | 16 ++++++++++++++-- 7 files changed, 30 insertions(+), 20 deletions(-) diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml index 73a87075e8..52ea692fbd 100755 --- a/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-cross-ns-target.out.yaml @@ -55,11 +55,11 @@ xdsIR: text: - path: /dev/stdout envoyPatchPolicies: - - Name: edit-conn-buffer-bytes - Namespace: envoy-gateway-2 + - name: edit-conn-buffer-bytes + namespace: envoy-gateway-2 status: conditions: - - lastTransitionTime: "2023-07-22T00:35:42Z" + - lastTransitionTime: null message: Namespace:envoy-gateway-2 TargetRef.Namespace:envoy-gateway, EnvoyPatchPolicy can only target a Gateway in the same namespace. reason: Invalid diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml index 2b9f09f3dd..17fb062c20 100755 --- a/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-invalid-target-kind.out.yaml @@ -55,11 +55,11 @@ xdsIR: text: - path: /dev/stdout envoyPatchPolicies: - - Name: edit-conn-buffer-bytes - Namespace: envoy-gateway + - name: edit-conn-buffer-bytes + namespace: envoy-gateway status: conditions: - - lastTransitionTime: "2023-07-22T00:35:42Z" + - lastTransitionTime: null message: TargetRef.Group:gateway.networking.k8s.io TargetRef.Kind:MyGateway, only TargetRef.Group:gateway.networking.k8s.io and TargetRef.Kind:Gateway is supported. diff --git a/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml b/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml index 8bcc9e56e2..06fc756204 100644 --- a/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml +++ b/internal/gatewayapi/testdata/envoypatchpolicy-valid.out.yaml @@ -55,34 +55,34 @@ xdsIR: text: - path: /dev/stdout envoyPatchPolicies: - - Name: edit-ignore-global-limit - Namespace: envoy-gateway - jsonPatches: + - jsonPatches: - name: envoy-gateway-gateway-1-http operation: op: replace path: /ignore_global_conn_limit value: "true" type: type.googleapis.com/envoy.config.listener.v3.Listener + name: edit-ignore-global-limit + namespace: envoy-gateway status: conditions: - - lastTransitionTime: "2023-07-22T00:35:42Z" + - lastTransitionTime: null message: EnvoyPatchPolicy has been accepted. reason: Accepted status: "True" type: Accepted - - Name: edit-conn-buffer-bytes - Namespace: envoy-gateway - jsonPatches: + - jsonPatches: - name: envoy-gateway-gateway-1-http operation: op: replace path: /per_connection_buffer_limit_bytes value: "1024" type: type.googleapis.com/envoy.config.listener.v3.Listener + name: edit-conn-buffer-bytes + namespace: envoy-gateway status: conditions: - - lastTransitionTime: "2023-07-22T00:35:42Z" + - lastTransitionTime: null message: EnvoyPatchPolicy has been accepted. reason: Accepted status: "True" diff --git a/internal/gatewayapi/translator_test.go b/internal/gatewayapi/translator_test.go index 99abe4ed7d..eff48d0629 100644 --- a/internal/gatewayapi/translator_test.go +++ b/internal/gatewayapi/translator_test.go @@ -106,7 +106,6 @@ func TestTranslate(t *testing.T) { got := translator.Translate(resources) require.NoError(t, field.SetValue(got, "LastTransitionTime", metav1.NewTime(time.Time{}))) - outputFilePath := strings.ReplaceAll(inputFile, ".in.yaml", ".out.yaml") out, err := yaml.Marshal(got) require.NoError(t, err) diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 2284f9c892..49ecd035fa 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -13,7 +13,6 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - ktypes "k8s.io/apimachinery/pkg/types" egcfgv1a1 "github.com/envoyproxy/gateway/api/config/v1alpha1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" @@ -803,7 +802,8 @@ type OpenTelemetryAccessLog struct { // EnvoyPatchPolicy defines the intermediate representation of the EnvoyPatchPolicy resource. // +k8s:deepcopy-gen=true type EnvoyPatchPolicy struct { - ktypes.NamespacedName + Name string `json:"name,omitempty" yaml:"name"` + Namespace string `json:"namespace,omitempty" yaml:"namespace"` // Status of the EnvoyPatchPolicy Status *egv1a1.EnvoyPatchPolicyStatus `json:"status,omitempty" yaml:"status,omitempty"` // JSONPatches are the JSON Patches that diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 26946c3b0f..bdb7c84067 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -101,7 +101,6 @@ func (in *DirectResponse) DeepCopy() *DirectResponse { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvoyPatchPolicy) DeepCopyInto(out *EnvoyPatchPolicy) { *out = *in - out.NamespacedName = in.NamespacedName if in.Status != nil { in, out := &in.Status, &out.Status *out = new(apiv1alpha1.EnvoyPatchPolicyStatus) diff --git a/internal/utils/field/field.go b/internal/utils/field/field.go index 09ce8e0029..081e2caa88 100644 --- a/internal/utils/field/field.go +++ b/internal/utils/field/field.go @@ -52,13 +52,13 @@ func SetValue(s any, fieldName string, fieldValue any) error { if field.Kind() == reflect.Slice { for i := 0; i < field.Len(); i++ { elem := field.Index(i) - // If the field is a struct, recursively call the setFieldValue function. + // If the field is a struct, recursively call the setValue function. if elem.Kind() == reflect.Struct { if err := SetValue(elem.Addr().Interface(), fieldName, fieldValue); err != nil { return err } } - // If the field is a pointer, recursively call the setFieldValue function. + // If the field is a pointer, recursively call the setValue function. if elem.Kind() == reflect.Pointer && !elem.IsNil() && elem.Elem().Kind() == reflect.Struct { if err := SetValue(elem.Interface(), fieldName, fieldValue); err != nil { return err @@ -67,6 +67,18 @@ func SetValue(s any, fieldName string, fieldValue any) error { } } + // If the field is a map, recursively call the setValue function + if field.Kind() == reflect.Map { + for _, k := range field.MapKeys() { + v := field.MapIndex(k) + if v.Kind() == reflect.Pointer && !v.IsNil() && v.Elem().Kind() == reflect.Struct { + if err := SetValue(v.Interface(), fieldName, fieldValue); err != nil { + return err + } + } + } + } + // If the field name matches, set the field value. if v.Type().Field(i).Name == fieldName { if !field.CanSet() { From 068489c74dd3719c2b4d1bbc05899f34669f9a72 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Mon, 24 Jul 2023 14:35:44 -0700 Subject: [PATCH 03/12] fix kube-test Signed-off-by: Arko Dasgupta --- internal/provider/kubernetes/kubernetes_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/provider/kubernetes/kubernetes_test.go b/internal/provider/kubernetes/kubernetes_test.go index 1d59310627..a37a138ebe 100644 --- a/internal/provider/kubernetes/kubernetes_test.go +++ b/internal/provider/kubernetes/kubernetes_test.go @@ -53,7 +53,8 @@ func TestProvider(t *testing.T) { svr, err := config.New() require.NoError(t, err) resources := new(message.ProviderResources) - provider, err := New(cliCfg, svr, resources) + ePatchPolicyStatuses := new(message.EnvoyPatchPolicyStatuses) + provider, err := New(cliCfg, svr, resources, ePatchPolicyStatuses) require.NoError(t, err) ctx, cancel := context.WithCancel(ctrl.SetupSignalHandler()) go func() { @@ -1681,7 +1682,8 @@ func TestNamespacedProvider(t *testing.T) { } resources := new(message.ProviderResources) - provider, err := New(cliCfg, svr, resources) + ePatchPolicyStatuses := new(message.EnvoyPatchPolicyStatuses) + provider, err := New(cliCfg, svr, resources, ePatchPolicyStatuses) require.NoError(t, err) ctx, cancel := context.WithCancel(context.Background()) go func() { From 3e0798d29f0c24ac5071791d6416289df0780fef Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Mon, 24 Jul 2023 16:24:29 -0700 Subject: [PATCH 04/12] more fixes * add status for empty path case * refactor ir for envoy patch policy status * test cases for patch policy status in xds-translator layer Signed-off-by: Arko Dasgupta --- internal/ir/xds.go | 12 +++-- internal/ir/zz_generated.deepcopy.go | 26 ++++++++-- internal/xds/translator/jsonpatch.go | 49 ++++++++++--------- internal/xds/translator/runner/runner.go | 2 +- .../testdata/in/xds-ir/jsonpatch.yaml | 2 + .../xds-ir/jsonpatch.envoypatchpolicies.yaml | 9 ++++ internal/xds/translator/translator_test.go | 35 ++++++++++--- internal/xds/types/resourceversiontable.go | 5 ++ 8 files changed, 102 insertions(+), 38 deletions(-) create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch.envoypatchpolicies.yaml diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 49ecd035fa..f9c4ffda04 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -802,13 +802,19 @@ type OpenTelemetryAccessLog struct { // EnvoyPatchPolicy defines the intermediate representation of the EnvoyPatchPolicy resource. // +k8s:deepcopy-gen=true type EnvoyPatchPolicy struct { + EnvoyPatchPolicyStatus + // JSONPatches are the JSON Patches that + // are to be applied to generaed Xds linked to the gateway. + JSONPatches []*JSONPatchConfig `json:"jsonPatches,omitempty" yaml:"jsonPatches,omitempty"` +} + +// EnvoyPatchPolicyStatus defines the status reference for the EnvoyPatchPolicy resource +// +k8s:deepcopy-gen=true +type EnvoyPatchPolicyStatus struct { Name string `json:"name,omitempty" yaml:"name"` Namespace string `json:"namespace,omitempty" yaml:"namespace"` // Status of the EnvoyPatchPolicy Status *egv1a1.EnvoyPatchPolicyStatus `json:"status,omitempty" yaml:"status,omitempty"` - // JSONPatches are the JSON Patches that - // are to be applied to generaed Xds linked to the gateway. - JSONPatches []*JSONPatchConfig `json:"jsonPatches,omitempty" yaml:"jsonPatches,omitempty"` } // JSONPatchConfig defines the configuration for patching a Envoy xDS Resource diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index bdb7c84067..556b40b619 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -101,11 +101,7 @@ func (in *DirectResponse) DeepCopy() *DirectResponse { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EnvoyPatchPolicy) DeepCopyInto(out *EnvoyPatchPolicy) { *out = *in - if in.Status != nil { - in, out := &in.Status, &out.Status - *out = new(apiv1alpha1.EnvoyPatchPolicyStatus) - (*in).DeepCopyInto(*out) - } + in.EnvoyPatchPolicyStatus.DeepCopyInto(&out.EnvoyPatchPolicyStatus) if in.JSONPatches != nil { in, out := &in.JSONPatches, &out.JSONPatches *out = make([]*JSONPatchConfig, len(*in)) @@ -129,6 +125,26 @@ func (in *EnvoyPatchPolicy) DeepCopy() *EnvoyPatchPolicy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvoyPatchPolicyStatus) DeepCopyInto(out *EnvoyPatchPolicyStatus) { + *out = *in + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(apiv1alpha1.EnvoyPatchPolicyStatus) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyPatchPolicyStatus. +func (in *EnvoyPatchPolicyStatus) DeepCopy() *EnvoyPatchPolicyStatus { + if in == nil { + return nil + } + out := new(EnvoyPatchPolicyStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GlobalRateLimit) DeepCopyInto(out *GlobalRateLimit) { *out = *in diff --git a/internal/xds/translator/jsonpatch.go b/internal/xds/translator/jsonpatch.go index 16ca4a5c14..7216af94e7 100644 --- a/internal/xds/translator/jsonpatch.go +++ b/internal/xds/translator/jsonpatch.go @@ -54,27 +54,27 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* // The patch library expects an array so convert it into one y, err := yaml.Marshal(p.Operation.Value) if err != nil { - err := fmt.Errorf("unable to marshal patch %+v, err: %v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("unable to marshal patch %+v, err: %v", p.Operation.Value, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } jsonBytes, err := yaml.YAMLToJSON(y) if err != nil { - err := fmt.Errorf("unable to convert patch to json %s, err: %v", string(y), err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("unable to convert patch to json %s, err: %v", string(y), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } switch p.Type { case string(resourcev3.ListenerType): temp := &listenerv3.Listener{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -82,39 +82,39 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.RouteType): temp := &routev3.RouteConfiguration{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } tCtx.AddXdsResource(resourcev3.RouteType, temp) case string(resourcev3.ClusterType): temp := &clusterv3.Cluster{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } tCtx.AddXdsResource(resourcev3.ClusterType, temp) case string(resourcev3.EndpointType): temp := &endpointv3.ClusterLoadAssignment{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - err := fmt.Errorf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } tCtx.AddXdsResource(resourcev3.EndpointType, temp) @@ -249,8 +249,8 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ClusterType): temp := &clusterv3.Cluster{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - err := fmt.Errorf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) - errs = multierror.Append(errs, err) + msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) + status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { @@ -285,7 +285,10 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* // Set Programmed condition if not yet set status.SetEnvoyPatchPolicyProgrammedIfUnset(e.Status, "successfully applied patches.") - } + // Set output context + + tCtx.EnvoyPatchPolicyStatuses = append(tCtx.EnvoyPatchPolicyStatuses, &e.EnvoyPatchPolicyStatus) + } return errs } diff --git a/internal/xds/translator/runner/runner.go b/internal/xds/translator/runner/runner.go index f8a9aee6a2..118f7f8590 100644 --- a/internal/xds/translator/runner/runner.go +++ b/internal/xds/translator/runner/runner.go @@ -82,7 +82,7 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { } // Publish EnvoyPatchPolicyStatus - for _, e := range val.EnvoyPatchPolicies { + for _, e := range result.EnvoyPatchPolicyStatuses { key := ktypes.NamespacedName{ Name: e.Name, Namespace: e.Namespace, diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml index ea662f3916..d784f55070 100644 --- a/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml @@ -1,5 +1,7 @@ envoyPatchPolicies: - status: {} + name: "first-policy" + namespace: "default" jsonPatches: - type: "type.googleapis.com/envoy.config.listener.v3.Listener" name: "first-listener" diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch.envoypatchpolicies.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.envoypatchpolicies.yaml new file mode 100755 index 0000000000..4043644d4e --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.envoypatchpolicies.yaml @@ -0,0 +1,9 @@ +- name: first-policy + namespace: default + status: + conditions: + - lastTransitionTime: null + message: successfully applied patches. + reason: Programmed + status: "True" + type: Programmed diff --git a/internal/xds/translator/translator_test.go b/internal/xds/translator/translator_test.go index cadee28d01..a9e32e6b3b 100644 --- a/internal/xds/translator/translator_test.go +++ b/internal/xds/translator/translator_test.go @@ -10,18 +10,24 @@ import ( "flag" "path/filepath" "testing" + "time" "github.com/envoyproxy/go-control-plane/pkg/cache/types" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" ratelimitv3 "github.com/envoyproxy/go-control-plane/ratelimit/config/ratelimit/v3" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" "github.com/envoyproxy/gateway/api/config/v1alpha1" "github.com/envoyproxy/gateway/internal/extension/testutils" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/ratelimit" "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/utils/field" "github.com/envoyproxy/gateway/internal/utils/file" + xtypes "github.com/envoyproxy/gateway/internal/xds/types" "github.com/envoyproxy/gateway/internal/xds/utils" ) @@ -30,17 +36,16 @@ var ( outFiles embed.FS //go:embed testdata/in/* inFiles embed.FS -) -var ( overrideTestData = flag.Bool("override-testdata", false, "if override the test output data.") ) func TestTranslateXds(t *testing.T) { testCases := []struct { - name string - dnsDomain string - requireSecrets bool + name string + dnsDomain string + requireSecrets bool + requireEnvoyPatchPolicies bool }{ { name: "empty", @@ -151,7 +156,8 @@ func TestTranslateXds(t *testing.T) { name: "tracing", }, { - name: "jsonpatch", + name: "jsonpatch", + requireEnvoyPatchPolicies: true, }, } @@ -192,6 +198,23 @@ func TestTranslateXds(t *testing.T) { } require.Equal(t, requireTestDataOutFile(t, "xds-ir", tc.name+".secrets.yaml"), requireResourcesToYAMLString(t, secrets)) } + if tc.requireEnvoyPatchPolicies { + got := tCtx.EnvoyPatchPolicyStatuses + for _, e := range got { + require.NoError(t, field.SetValue(e, "LastTransitionTime", metav1.NewTime(time.Time{}))) + } + if *overrideTestData { + out, err := yaml.Marshal(got) + require.NoError(t, err) + require.NoError(t, file.Write(string(out), filepath.Join("testdata", "out", "xds-ir", tc.name+".envoypatchpolicies.yaml"))) + } + + in := requireTestDataOutFile(t, "xds-ir", tc.name+".envoypatchpolicies.yaml") + want := xtypes.EnvoyPatchPolicyStatuses{} + require.NoError(t, yaml.Unmarshal([]byte(in), &want)) + opts := cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime") + require.Empty(t, cmp.Diff(want, got, opts)) + } }) } } diff --git a/internal/xds/types/resourceversiontable.go b/internal/xds/types/resourceversiontable.go index ec080e2855..0aca38e221 100644 --- a/internal/xds/types/resourceversiontable.go +++ b/internal/xds/types/resourceversiontable.go @@ -9,14 +9,19 @@ import ( "github.com/envoyproxy/go-control-plane/pkg/cache/types" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" "google.golang.org/protobuf/proto" + + "github.com/envoyproxy/gateway/internal/ir" ) // XdsResources represents all the xds resources type XdsResources = map[resourcev3.Type][]types.Resource +type EnvoyPatchPolicyStatuses []*ir.EnvoyPatchPolicyStatus + // ResourceVersionTable holds all the translated xds resources type ResourceVersionTable struct { XdsResources + EnvoyPatchPolicyStatuses } // DeepCopyInto copies the contents into the output object From 60db42c314518785cbc4ffc52157bf4e1f0db15c Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Mon, 24 Jul 2023 17:28:34 -0700 Subject: [PATCH 05/12] more tests Signed-off-by: Arko Dasgupta --- .../in/xds-ir/jsonpatch-invalid-patch.yaml | 37 +++++++++++++++++++ .../in/xds-ir/jsonpatch-missing-resource.yaml | 37 +++++++++++++++++++ .../testdata/in/xds-ir/jsonpatch.yaml | 2 +- .../jsonpatch-invalid-patch.clusters.yaml | 13 +++++++ .../jsonpatch-invalid-patch.endpoints.yaml | 10 +++++ ...atch-invalid-patch.envoypatchpolicies.yaml | 10 +++++ .../jsonpatch-invalid-patch.listeners.yaml | 33 +++++++++++++++++ .../jsonpatch-invalid-patch.routes.yaml | 16 ++++++++ .../jsonpatch-missing-resource.clusters.yaml | 13 +++++++ .../jsonpatch-missing-resource.endpoints.yaml | 10 +++++ ...h-missing-resource.envoypatchpolicies.yaml | 10 +++++ .../jsonpatch-missing-resource.listeners.yaml | 33 +++++++++++++++++ .../jsonpatch-missing-resource.routes.yaml | 16 ++++++++ internal/xds/translator/translator_test.go | 8 ++++ 14 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml create mode 100644 internal/xds/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.listeners.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.envoypatchpolicies.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.listeners.yaml create mode 100755 internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml new file mode 100644 index 0000000000..57b3f73a65 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml @@ -0,0 +1,37 @@ +envoyPatchPolicies: +- status: {} + name: "first-policy" + namespace: "default" + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "first-listener" + operation: + op: "add" + path: "/this/path/doesnt/exist" + value: + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eg-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destinations: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml new file mode 100644 index 0000000000..8e96f03ee4 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-missing-resource.yaml @@ -0,0 +1,37 @@ +envoyPatchPolicies: +- status: {} + name: "first-policy" + namespace: "default" + jsonPatches: + - type: "type.googleapis.com/envoy.config.listener.v3.Listener" + name: "non-existant-listener" + operation: + op: "add" + path: "/default_filter_chain/filters/0/typed_config/http_filters/0" + value: + name: "envoy.filters.http.ratelimit" + typed_config: + "@type": "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit" + domain: "eg-ratelimit" + failure_mode_deny: true + timeout: 1s + rate_limit_service: + grpc_service: + envoy_grpc: + cluster_name: rate-limit-cluster + transport_api_version: V3 +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + routes: + - name: "first-route" + headerMatches: + - name: user + stringMatch: + exact: "jason" + destinations: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml index d784f55070..4ce37b015b 100644 --- a/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch.yaml @@ -1,7 +1,7 @@ envoyPatchPolicies: - status: {} name: "first-policy" - namespace: "default" + namespace: "default" jsonPatches: - type: "type.googleapis.com/envoy.config.listener.v3.Listener" name: "first-listener" diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml new file mode 100755 index 0000000000..2c908f7ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.clusters.yaml @@ -0,0 +1,13 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route + name: first-route + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml new file mode 100755 index 0000000000..f5f137f937 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.endpoints.yaml @@ -0,0 +1,10 @@ +- clusterName: first-route + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + locality: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml new file mode 100755 index 0000000000..48cf63cc77 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml @@ -0,0 +1,10 @@ +- name: first-policy + namespace: default + status: + conditions: + - lastTransitionTime: null + message: 'unable to unmarshal xds resource {"name":"first-listener","address":{"socket_address":{"address":"0.0.0.0","port_value":10080}},"default_filter_chain":{"filters":[{"name":"envoy.filters.network.http_connection_manager","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager","stat_prefix":"http","rds":{"config_source":{"ads":{},"resource_api_version":"V3"},"route_config_name":"first-listener"},"http_filters":[{"name":"envoy.filters.http.router","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"}}],"common_http_protocol_options":{"headers_with_underscores_action":"REJECT_REQUEST"},"http2_protocol_options":{"max_concurrent_streams":100,"initial_stream_window_size":65536,"initial_connection_window_size":1048576},"use_remote_address":true,"upgrade_configs":[{"upgrade_type":"websocket"}],"normalize_path":true,"merge_slashes":true,"path_with_escaped_slashes_action":"UNESCAPE_AND_REDIRECT"}}]},"per_connection_buffer_limit_bytes":32768,"this":{"path":{"doesnt":{"exist":{"name":"envoy.filters.http.ratelimit","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit","domain":"eg-ratelimit","failure_mode_deny":true,"rate_limit_service":{"grpc_service":{"envoy_grpc":{"cluster_name":"rate-limit-cluster"}},"transport_api_version":"V3"},"timeout":"1s"}}}}}}, + err:proto: (line 1:1023): unknown field "this"' + reason: Invalid + status: "False" + type: Programmed diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.listeners.yaml new file mode 100755 index 0000000000..73ee1b42ef --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml new file mode 100755 index 0000000000..60009c41c8 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.routes.yaml @@ -0,0 +1,16 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + prefix: / + name: first-route + route: + cluster: first-route diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml new file mode 100755 index 0000000000..2c908f7ef6 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.clusters.yaml @@ -0,0 +1,13 @@ +- commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route + name: first-route + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml new file mode 100755 index 0000000000..f5f137f937 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.endpoints.yaml @@ -0,0 +1,10 @@ +- clusterName: first-route + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + locality: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.envoypatchpolicies.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.envoypatchpolicies.yaml new file mode 100755 index 0000000000..26d7c0af42 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.envoypatchpolicies.yaml @@ -0,0 +1,10 @@ +- name: first-policy + namespace: default + status: + conditions: + - lastTransitionTime: null + message: 'unable to find xds resource type.googleapis.com/envoy.config.listener.v3.Listener: + non-existant-listener' + reason: ResourceNotFound + status: "False" + type: Programmed diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.listeners.yaml new file mode 100755 index 0000000000..73ee1b42ef --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.listeners.yaml @@ -0,0 +1,33 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + statPrefix: http + upgradeConfigs: + - upgradeType: websocket + useRemoteAddress: true + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml new file mode 100755 index 0000000000..60009c41c8 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-missing-resource.routes.yaml @@ -0,0 +1,16 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener + routes: + - match: + headers: + - name: user + stringMatch: + exact: jason + prefix: / + name: first-route + route: + cluster: first-route diff --git a/internal/xds/translator/translator_test.go b/internal/xds/translator/translator_test.go index a9e32e6b3b..af7f0a4dd8 100644 --- a/internal/xds/translator/translator_test.go +++ b/internal/xds/translator/translator_test.go @@ -159,6 +159,14 @@ func TestTranslateXds(t *testing.T) { name: "jsonpatch", requireEnvoyPatchPolicies: true, }, + { + name: "jsonpatch-missing-resource", + requireEnvoyPatchPolicies: true, + }, + { + name: "jsonpatch-invalid-patch", + requireEnvoyPatchPolicies: true, + }, } for _, tc := range testCases { From 415587988bf69a5476f120ac116577e698d5462e Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Mon, 24 Jul 2023 18:05:23 -0700 Subject: [PATCH 06/12] fix test Signed-off-by: Arko Dasgupta --- .../out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml index 48cf63cc77..2e24913aa1 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml @@ -4,7 +4,7 @@ conditions: - lastTransitionTime: null message: 'unable to unmarshal xds resource {"name":"first-listener","address":{"socket_address":{"address":"0.0.0.0","port_value":10080}},"default_filter_chain":{"filters":[{"name":"envoy.filters.network.http_connection_manager","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager","stat_prefix":"http","rds":{"config_source":{"ads":{},"resource_api_version":"V3"},"route_config_name":"first-listener"},"http_filters":[{"name":"envoy.filters.http.router","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"}}],"common_http_protocol_options":{"headers_with_underscores_action":"REJECT_REQUEST"},"http2_protocol_options":{"max_concurrent_streams":100,"initial_stream_window_size":65536,"initial_connection_window_size":1048576},"use_remote_address":true,"upgrade_configs":[{"upgrade_type":"websocket"}],"normalize_path":true,"merge_slashes":true,"path_with_escaped_slashes_action":"UNESCAPE_AND_REDIRECT"}}]},"per_connection_buffer_limit_bytes":32768,"this":{"path":{"doesnt":{"exist":{"name":"envoy.filters.http.ratelimit","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit","domain":"eg-ratelimit","failure_mode_deny":true,"rate_limit_service":{"grpc_service":{"envoy_grpc":{"cluster_name":"rate-limit-cluster"}},"transport_api_version":"V3"},"timeout":"1s"}}}}}}, - err:proto: (line 1:1023): unknown field "this"' + err:proto: (line 1:1023): unknown field "this"' reason: Invalid status: "False" type: Programmed From f81ffdcc6eee6a5675eb30086c1616144dea89f9 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Mon, 24 Jul 2023 19:13:37 -0700 Subject: [PATCH 07/12] fix status rbac Signed-off-by: Arko Dasgupta --- .../templates/generated/rbac/roles.yaml | 6 ++++++ internal/provider/kubernetes/rbac.go | 3 ++- internal/status/status.go | 21 ++++++++++++------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/charts/gateway-helm/templates/generated/rbac/roles.yaml b/charts/gateway-helm/templates/generated/rbac/roles.yaml index 9122278022..05fed3d75a 100644 --- a/charts/gateway-helm/templates/generated/rbac/roles.yaml +++ b/charts/gateway-helm/templates/generated/rbac/roles.yaml @@ -52,6 +52,12 @@ rules: - list - update - watch +- apiGroups: + - gateway.envoyproxy.io + resources: + - envoypatchpolicies/status + verbs: + - update - apiGroups: - gateway.networking.k8s.io resources: diff --git a/internal/provider/kubernetes/rbac.go b/internal/provider/kubernetes/rbac.go index 9be36d6428..d9fcc2c6ee 100644 --- a/internal/provider/kubernetes/rbac.go +++ b/internal/provider/kubernetes/rbac.go @@ -11,7 +11,8 @@ package kubernetes // RBAC for Envoy Gateway custom resources. // +kubebuilder:rbac:groups="config.gateway.envoyproxy.io",resources=envoyproxies,verbs=get;list;watch;update -// +kubebuilder:rbac:groups="gateway.envoyproxy.io",resources=authenticationfilters;ratelimitfilters;envoypatchpolicies,verbs=get;list;watch;update +// +kubebuilder:rbac:groups="gateway.envoyproxy.io",resources=authenticationfilters;ratelimitfilters;envoypatchpolicies,verbs=get;list;watch;update;patch +// +kubebuilder:rbac:groups="gateway.envoyproxy.io",resources=envoypatchpolicies/status,verbs=update // RBAC for watched resources of Gateway API controllers. // +kubebuilder:rbac:groups="",resources=secrets;services;namespaces;nodes,verbs=get;list;watch diff --git a/internal/status/status.go b/internal/status/status.go index 904a4eed21..0d5ec0e6dd 100644 --- a/internal/status/status.go +++ b/internal/status/status.go @@ -158,13 +158,14 @@ func (u *UpdateWriter) Send(update Update) { // // Supported objects: // -// GatewayClasses -// Gateway -// HTTPRoute -// TLSRoute -// TCPRoute -// UDPRoute -// GRPCRoute +// GatewayClasses +// Gateway +// HTTPRoute +// TLSRoute +// TCPRoute +// UDPRoute +// GRPCRoute +// EnvoyPatchPolicy func isStatusEqual(objA, objB interface{}) bool { opts := cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime") switch a := objA.(type) { @@ -210,6 +211,12 @@ func isStatusEqual(objA, objB interface{}) bool { return true } } + case *egv1a1.EnvoyPatchPolicy: + if b, ok := objB.(*egv1a1.EnvoyPatchPolicy); ok { + if cmp.Equal(a.Status, b.Status, opts) { + return true + } + } } return false } From 4cd4a77b478bf221bbe379bf20ab0b2be6e31b21 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Tue, 25 Jul 2023 14:53:40 -0700 Subject: [PATCH 08/12] generate missing crd status subresource Signed-off-by: Arko Dasgupta --- api/v1alpha1/envoypatchpolicy_types.go | 1 + ...teway.envoyproxy.io_envoypatchpolicies.yaml | 2 ++ .../templates/generated/rbac/roles.yaml | 1 + internal/status/status.go | 18 ++++++++++-------- internal/xds/translator/runner/runner.go | 15 +++++++++------ 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/api/v1alpha1/envoypatchpolicy_types.go b/api/v1alpha1/envoypatchpolicy_types.go index e8f820b397..cab756b69c 100644 --- a/api/v1alpha1/envoypatchpolicy_types.go +++ b/api/v1alpha1/envoypatchpolicy_types.go @@ -17,6 +17,7 @@ const ( ) // +kubebuilder:object:root=true +// +kubebuilder:subresource:status // EnvoyPatchPolicy allows the user to modify the generated Envoy xDS // resources by Envoy Gateway using this patch API diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml index abaa01e7c8..9249db2e4e 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml @@ -221,3 +221,5 @@ spec: type: object served: true storage: true + subresources: + status: {} diff --git a/charts/gateway-helm/templates/generated/rbac/roles.yaml b/charts/gateway-helm/templates/generated/rbac/roles.yaml index 05fed3d75a..602b9f4322 100644 --- a/charts/gateway-helm/templates/generated/rbac/roles.yaml +++ b/charts/gateway-helm/templates/generated/rbac/roles.yaml @@ -50,6 +50,7 @@ rules: verbs: - get - list + - patch - update - watch - apiGroups: diff --git a/internal/status/status.go b/internal/status/status.go index 0d5ec0e6dd..1af10030ad 100644 --- a/internal/status/status.go +++ b/internal/status/status.go @@ -26,6 +26,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) // Update contains an all the information needed to update an object's status. @@ -158,14 +160,14 @@ func (u *UpdateWriter) Send(update Update) { // // Supported objects: // -// GatewayClasses -// Gateway -// HTTPRoute -// TLSRoute -// TCPRoute -// UDPRoute -// GRPCRoute -// EnvoyPatchPolicy +// GatewayClasses +// Gateway +// HTTPRoute +// TLSRoute +// TCPRoute +// UDPRoute +// GRPCRoute +// EnvoyPatchPolicy func isStatusEqual(objA, objB interface{}) bool { opts := cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime") switch a := objA.(type) { diff --git a/internal/xds/translator/runner/runner.go b/internal/xds/translator/runner/runner.go index 118f7f8590..defbc7fc51 100644 --- a/internal/xds/translator/runner/runner.go +++ b/internal/xds/translator/runner/runner.go @@ -74,12 +74,6 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { } result, err := t.Translate(val) - if err != nil { - r.Logger.Error(err, "failed to translate xds ir") - } else { - // Publish - r.Xds.Store(key, result) - } // Publish EnvoyPatchPolicyStatus for _, e := range result.EnvoyPatchPolicyStatuses { @@ -89,6 +83,15 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { } r.EnvoyPatchPolicyStatuses.Store(key, e.Status) } + // Discard the EnvoyPatchPolicyStatuses to reduce memory footprint + result.EnvoyPatchPolicyStatuses = nil + + if err != nil { + r.Logger.Error(err, "failed to translate xds ir") + } else { + // Publish + r.Xds.Store(key, result) + } } }, ) From 248c383726eab387d21461367a66ef4abc0db65c Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Tue, 25 Jul 2023 15:12:51 -0700 Subject: [PATCH 09/12] fix lint Signed-off-by: Arko Dasgupta --- .../translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml | 2 +- .../out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml index 57b3f73a65..10bc99336b 100644 --- a/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/jsonpatch-invalid-patch.yaml @@ -7,7 +7,7 @@ envoyPatchPolicies: name: "first-listener" operation: op: "add" - path: "/this/path/doesnt/exist" + path: "/this/path/never/existed" value: name: "envoy.filters.http.ratelimit" typed_config: diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml index 2e24913aa1..9ff692421b 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml @@ -3,7 +3,7 @@ status: conditions: - lastTransitionTime: null - message: 'unable to unmarshal xds resource {"name":"first-listener","address":{"socket_address":{"address":"0.0.0.0","port_value":10080}},"default_filter_chain":{"filters":[{"name":"envoy.filters.network.http_connection_manager","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager","stat_prefix":"http","rds":{"config_source":{"ads":{},"resource_api_version":"V3"},"route_config_name":"first-listener"},"http_filters":[{"name":"envoy.filters.http.router","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"}}],"common_http_protocol_options":{"headers_with_underscores_action":"REJECT_REQUEST"},"http2_protocol_options":{"max_concurrent_streams":100,"initial_stream_window_size":65536,"initial_connection_window_size":1048576},"use_remote_address":true,"upgrade_configs":[{"upgrade_type":"websocket"}],"normalize_path":true,"merge_slashes":true,"path_with_escaped_slashes_action":"UNESCAPE_AND_REDIRECT"}}]},"per_connection_buffer_limit_bytes":32768,"this":{"path":{"doesnt":{"exist":{"name":"envoy.filters.http.ratelimit","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit","domain":"eg-ratelimit","failure_mode_deny":true,"rate_limit_service":{"grpc_service":{"envoy_grpc":{"cluster_name":"rate-limit-cluster"}},"transport_api_version":"V3"},"timeout":"1s"}}}}}}, + message: 'unable to unmarshal xds resource {"name":"first-listener","address":{"socket_address":{"address":"0.0.0.0","port_value":10080}},"default_filter_chain":{"filters":[{"name":"envoy.filters.network.http_connection_manager","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager","stat_prefix":"http","rds":{"config_source":{"ads":{},"resource_api_version":"V3"},"route_config_name":"first-listener"},"http_filters":[{"name":"envoy.filters.http.router","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"}}],"common_http_protocol_options":{"headers_with_underscores_action":"REJECT_REQUEST"},"http2_protocol_options":{"max_concurrent_streams":100,"initial_stream_window_size":65536,"initial_connection_window_size":1048576},"use_remote_address":true,"upgrade_configs":[{"upgrade_type":"websocket"}],"normalize_path":true,"merge_slashes":true,"path_with_escaped_slashes_action":"UNESCAPE_AND_REDIRECT"}}]},"per_connection_buffer_limit_bytes":32768,"this":{"path":{"never":{"existed":{"name":"envoy.filters.http.ratelimit","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit","domain":"eg-ratelimit","failure_mode_deny":true,"rate_limit_service":{"grpc_service":{"envoy_grpc":{"cluster_name":"rate-limit-cluster"}},"transport_api_version":"V3"},"timeout":"1s"}}}}}}, err:proto: (line 1:1023): unknown field "this"' reason: Invalid status: "False" From e86b1e12b84e250a5d56df0fb68f075b964113f3 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Tue, 25 Jul 2023 15:27:22 -0700 Subject: [PATCH 10/12] fix err Signed-off-by: Arko Dasgupta --- internal/xds/translator/jsonpatch.go | 44 ++++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/internal/xds/translator/jsonpatch.go b/internal/xds/translator/jsonpatch.go index 7216af94e7..b585a9cefa 100644 --- a/internal/xds/translator/jsonpatch.go +++ b/internal/xds/translator/jsonpatch.go @@ -54,13 +54,13 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* // The patch library expects an array so convert it into one y, err := yaml.Marshal(p.Operation.Value) if err != nil { - msg := fmt.Sprintf("unable to marshal patch %+v, err: %v", p.Operation.Value, err) + msg := fmt.Sprintf("unable to marshal patch %+v, err: %s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } jsonBytes, err := yaml.YAMLToJSON(y) if err != nil { - msg := fmt.Sprintf("unable to convert patch to json %s, err: %v", string(y), err) + msg := fmt.Sprintf("unable to convert patch to json %s, err: %s", string(y), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -68,12 +68,12 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ListenerType): temp := &listenerv3.Listener{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - msg := fmt.Sprintf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("validation failed for xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -82,12 +82,12 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.RouteType): temp := &routev3.RouteConfiguration{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - msg := fmt.Sprintf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("validation failed for xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -95,12 +95,12 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ClusterType): temp := &clusterv3.Cluster{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - msg := fmt.Sprintf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("validation failed for xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -108,12 +108,12 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.EndpointType): temp := &endpointv3.ClusterLoadAssignment{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - msg := fmt.Sprintf("validation failed for xds resource %+v, err:%v", p.Operation.Value, err) + msg := fmt.Sprintf("validation failed for xds resource %+v, err:%s", p.Operation.Value, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -180,20 +180,20 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* // The patch library expects an array so convert it into one y, err := yaml.Marshal([]ir.JSONPatchOperation{p.Operation}) if err != nil { - msg := fmt.Sprintf("unable to marshal patch %+v, err: %v", p.Operation, err) + msg := fmt.Sprintf("unable to marshal patch %+v, err: %s", p.Operation, err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } jsonBytes, err := yaml.YAMLToJSON(y) if err != nil { - msg := fmt.Sprintf("unable to convert patch to json %s, err: %v", string(y), err) + msg := fmt.Sprintf("unable to convert patch to json %s, err: %s", string(y), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } patchObj, err := jsonpatchv5.DecodePatch(jsonBytes) if err != nil { - msg := fmt.Sprintf("unable to decode patch %s, err: %v", string(jsonBytes), err) + msg := fmt.Sprintf("unable to decode patch %s, err: %s", string(jsonBytes), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -203,7 +203,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* opts.EnsurePathExistsOnAdd = true modifiedJSON, err := patchObj.ApplyWithOptions(resourceJSON, opts) if err != nil { - msg := fmt.Sprintf("unable to apply patch:\n%s on resource:\n%s, err: %v", string(jsonBytes), string(resourceJSON), err) + msg := fmt.Sprintf("unable to apply patch:\n%s on resource:\n%s, err: %s", string(jsonBytes), string(resourceJSON), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -215,12 +215,12 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ListenerType): temp := &listenerv3.Listener{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) + msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%s", string(modifiedJSON), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - msg := fmt.Sprintf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) + msg := fmt.Sprintf("validation failed for xds resource %s, err:%s", string(modifiedJSON), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -232,12 +232,12 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.RouteType): temp := &routev3.RouteConfiguration{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) + msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%s", string(modifiedJSON), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - msg := fmt.Sprintf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) + msg := fmt.Sprintf("validation failed for xds resource %s, err:%s", string(modifiedJSON), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -249,12 +249,12 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ClusterType): temp := &clusterv3.Cluster{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) + msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%s", string(modifiedJSON), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - msg := fmt.Sprintf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) + msg := fmt.Sprintf("validation failed for xds resource %s, err:%s", string(modifiedJSON), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -266,12 +266,12 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.EndpointType): temp := &endpointv3.ClusterLoadAssignment{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%v", string(modifiedJSON), err) + msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%s", string(modifiedJSON), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } if err = temp.Validate(); err != nil { - msg := fmt.Sprintf("validation failed for xds resource %s, err:%v", string(modifiedJSON), err) + msg := fmt.Sprintf("validation failed for xds resource %s, err:%s", string(modifiedJSON), err.Error()) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } From e5845d0aa3a9764020d9c170796f9741163b3ef9 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Tue, 25 Jul 2023 15:54:53 -0700 Subject: [PATCH 11/12] fix test Signed-off-by: Arko Dasgupta --- internal/xds/translator/jsonpatch.go | 16 ++++++++-------- ...onpatch-invalid-patch.envoypatchpolicies.yaml | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/internal/xds/translator/jsonpatch.go b/internal/xds/translator/jsonpatch.go index b585a9cefa..f0c3929c2b 100644 --- a/internal/xds/translator/jsonpatch.go +++ b/internal/xds/translator/jsonpatch.go @@ -68,7 +68,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ListenerType): temp := &listenerv3.Listener{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%s", p.Operation.Value, err.Error()) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v", p.Operation.Value) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -82,7 +82,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.RouteType): temp := &routev3.RouteConfiguration{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%s", p.Operation.Value, err.Error()) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v", p.Operation.Value) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -95,7 +95,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ClusterType): temp := &clusterv3.Cluster{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%s", p.Operation.Value, err.Error()) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v", p.Operation.Value) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -108,7 +108,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.EndpointType): temp := &endpointv3.ClusterLoadAssignment{} if err = protojson.Unmarshal(jsonBytes, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %+v, err:%s", p.Operation.Value, err.Error()) + msg := fmt.Sprintf("unable to unmarshal xds resource %+v", p.Operation.Value) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -215,7 +215,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ListenerType): temp := &listenerv3.Listener{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%s", string(modifiedJSON), err.Error()) + msg := fmt.Sprintf("unable to unmarshal xds resource %s", string(modifiedJSON)) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -232,7 +232,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.RouteType): temp := &routev3.RouteConfiguration{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%s", string(modifiedJSON), err.Error()) + msg := fmt.Sprintf("unable to unmarshal xds resource %s", string(modifiedJSON)) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -249,7 +249,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.ClusterType): temp := &clusterv3.Cluster{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%s", string(modifiedJSON), err.Error()) + msg := fmt.Sprintf("unable to unmarshal xds resource %s", string(modifiedJSON)) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } @@ -266,7 +266,7 @@ func processJSONPatches(tCtx *types.ResourceVersionTable, envoyPatchPolicies []* case string(resourcev3.EndpointType): temp := &endpointv3.ClusterLoadAssignment{} if err = protojson.Unmarshal(modifiedJSON, temp); err != nil { - msg := fmt.Sprintf("unable to unmarshal xds resource %s, err:%s", string(modifiedJSON), err.Error()) + msg := fmt.Sprintf("unable to unmarshal xds resource %s", string(modifiedJSON)) status.SetEnvoyPatchPolicyInvalid(e.Status, msg) continue } diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml index 9ff692421b..47fecd8f84 100755 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-invalid-patch.envoypatchpolicies.yaml @@ -3,8 +3,7 @@ status: conditions: - lastTransitionTime: null - message: 'unable to unmarshal xds resource {"name":"first-listener","address":{"socket_address":{"address":"0.0.0.0","port_value":10080}},"default_filter_chain":{"filters":[{"name":"envoy.filters.network.http_connection_manager","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager","stat_prefix":"http","rds":{"config_source":{"ads":{},"resource_api_version":"V3"},"route_config_name":"first-listener"},"http_filters":[{"name":"envoy.filters.http.router","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"}}],"common_http_protocol_options":{"headers_with_underscores_action":"REJECT_REQUEST"},"http2_protocol_options":{"max_concurrent_streams":100,"initial_stream_window_size":65536,"initial_connection_window_size":1048576},"use_remote_address":true,"upgrade_configs":[{"upgrade_type":"websocket"}],"normalize_path":true,"merge_slashes":true,"path_with_escaped_slashes_action":"UNESCAPE_AND_REDIRECT"}}]},"per_connection_buffer_limit_bytes":32768,"this":{"path":{"never":{"existed":{"name":"envoy.filters.http.ratelimit","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit","domain":"eg-ratelimit","failure_mode_deny":true,"rate_limit_service":{"grpc_service":{"envoy_grpc":{"cluster_name":"rate-limit-cluster"}},"transport_api_version":"V3"},"timeout":"1s"}}}}}}, - err:proto: (line 1:1023): unknown field "this"' + message: unable to unmarshal xds resource {"name":"first-listener","address":{"socket_address":{"address":"0.0.0.0","port_value":10080}},"default_filter_chain":{"filters":[{"name":"envoy.filters.network.http_connection_manager","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager","stat_prefix":"http","rds":{"config_source":{"ads":{},"resource_api_version":"V3"},"route_config_name":"first-listener"},"http_filters":[{"name":"envoy.filters.http.router","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"}}],"common_http_protocol_options":{"headers_with_underscores_action":"REJECT_REQUEST"},"http2_protocol_options":{"max_concurrent_streams":100,"initial_stream_window_size":65536,"initial_connection_window_size":1048576},"use_remote_address":true,"upgrade_configs":[{"upgrade_type":"websocket"}],"normalize_path":true,"merge_slashes":true,"path_with_escaped_slashes_action":"UNESCAPE_AND_REDIRECT"}}]},"per_connection_buffer_limit_bytes":32768,"this":{"path":{"never":{"existed":{"name":"envoy.filters.http.ratelimit","typed_config":{"@type":"type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit","domain":"eg-ratelimit","failure_mode_deny":true,"rate_limit_service":{"grpc_service":{"envoy_grpc":{"cluster_name":"rate-limit-cluster"}},"transport_api_version":"V3"},"timeout":"1s"}}}}}} reason: Invalid status: "False" type: Programmed From 82ebfe5efc4c26e4f0b775eff744a176db22176d Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Tue, 25 Jul 2023 16:45:45 -0700 Subject: [PATCH 12/12] crd printcolumn tags Signed-off-by: Arko Dasgupta --- api/v1alpha1/authenticationfilter_types.go | 3 ++- api/v1alpha1/envoypatchpolicy_types.go | 2 ++ api/v1alpha1/ratelimitfilter_types.go | 1 + .../gateway.envoyproxy.io_authenticationfilters.yaml | 7 ++++++- .../gateway.envoyproxy.io_envoypatchpolicies.yaml | 9 ++++++++- .../gateway.envoyproxy.io_ratelimitfilters.yaml | 7 ++++++- 6 files changed, 25 insertions(+), 4 deletions(-) diff --git a/api/v1alpha1/authenticationfilter_types.go b/api/v1alpha1/authenticationfilter_types.go index 779163366b..7907ddef2e 100644 --- a/api/v1alpha1/authenticationfilter_types.go +++ b/api/v1alpha1/authenticationfilter_types.go @@ -14,7 +14,8 @@ const ( KindAuthenticationFilter = "AuthenticationFilter" ) -//+kubebuilder:object:root=true +// +kubebuilder:object:root=true +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` type AuthenticationFilter struct { metav1.TypeMeta `json:",inline"` diff --git a/api/v1alpha1/envoypatchpolicy_types.go b/api/v1alpha1/envoypatchpolicy_types.go index cab756b69c..8234ead224 100644 --- a/api/v1alpha1/envoypatchpolicy_types.go +++ b/api/v1alpha1/envoypatchpolicy_types.go @@ -18,6 +18,8 @@ const ( // +kubebuilder:object:root=true // +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Programmed")].reason` +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // EnvoyPatchPolicy allows the user to modify the generated Envoy xDS // resources by Envoy Gateway using this patch API diff --git a/api/v1alpha1/ratelimitfilter_types.go b/api/v1alpha1/ratelimitfilter_types.go index 9544fc638b..320d934c9d 100644 --- a/api/v1alpha1/ratelimitfilter_types.go +++ b/api/v1alpha1/ratelimitfilter_types.go @@ -15,6 +15,7 @@ const ( ) // +kubebuilder:object:root=true +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // RateLimitFilter allows the user to limit the number of incoming requests // to a predefined value based on attributes within the traffic flow. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_authenticationfilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_authenticationfilters.yaml index f44264c226..e8e6077364 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_authenticationfilters.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_authenticationfilters.yaml @@ -15,7 +15,11 @@ spec: singular: authenticationfilter scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: properties: @@ -125,3 +129,4 @@ spec: type: object served: true storage: true + subresources: {} diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml index 9249db2e4e..c336131fcc 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoypatchpolicies.yaml @@ -15,7 +15,14 @@ spec: singular: envoypatchpolicy scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Programmed")].reason + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: description: EnvoyPatchPolicy allows the user to modify the generated Envoy diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_ratelimitfilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_ratelimitfilters.yaml index 9d66793488..7bfc7554cb 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_ratelimitfilters.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_ratelimitfilters.yaml @@ -15,7 +15,11 @@ spec: singular: ratelimitfilter scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 schema: openAPIV3Schema: description: RateLimitFilter allows the user to limit the number of incoming @@ -177,3 +181,4 @@ spec: type: object served: true storage: true + subresources: {}