Skip to content

Commit

Permalink
Envoygateway wasm controller (#848)
Browse files Browse the repository at this point in the history
* envoygateway controllers to setup wasm module

Limitador cluster controller based on EnvoyPatchPolicy
Wasm controller based on EnvoyExtensionPolicy

Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>

* envoygateway: enable envoypatchpolicy

Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>

* envoygateway: wasm module tests

Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>

---------

Signed-off-by: Eguzki Astiz Lezaun <eastizle@redhat.com>
  • Loading branch information
eguzki committed Sep 12, 2024
1 parent 9d29fcc commit 223ccd0
Show file tree
Hide file tree
Showing 25 changed files with 1,552 additions and 328 deletions.
14 changes: 13 additions & 1 deletion bundle/manifests/kuadrant-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ metadata:
capabilities: Basic Install
categories: Integration & Delivery
containerImage: quay.io/kuadrant/kuadrant-operator:latest
createdAt: "2024-08-20T09:51:49Z"
createdAt: "2024-09-05T18:00:43Z"
operators.operatorframework.io/builder: operator-sdk-v1.32.0
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
repository: https://github.com/Kuadrant/kuadrant-operator
Expand Down Expand Up @@ -294,6 +294,18 @@ spec:
- patch
- update
- watch
- apiGroups:
- gateway.envoyproxy.io
resources:
- envoypatchpolicies
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- gateway.envoyproxy.io
resources:
Expand Down
12 changes: 12 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ rules:
- patch
- update
- watch
- apiGroups:
- gateway.envoyproxy.io
resources:
- envoypatchpolicies
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- gateway.envoyproxy.io
resources:
Expand Down
224 changes: 224 additions & 0 deletions controllers/envoygateway_limitador_cluster_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package controllers

import (
"context"
"encoding/json"
"fmt"

egv1alpha1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/go-logr/logr"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

kuadrantv1beta1 "github.com/kuadrant/kuadrant-operator/api/v1beta1"
"github.com/kuadrant/kuadrant-operator/pkg/common"
kuadrantenvoygateway "github.com/kuadrant/kuadrant-operator/pkg/envoygateway"
kuadrantgatewayapi "github.com/kuadrant/kuadrant-operator/pkg/library/gatewayapi"
"github.com/kuadrant/kuadrant-operator/pkg/library/reconcilers"
limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1"
)

// EnvoyGatewayLimitadorClusterReconciler reconciles an EnvoyGateway EnvoyPatchPolicy object
// to setup limitador's cluster on the gateway. It is a requirement for the wasm module to work.
// https://gateway.envoyproxy.io/latest/api/extension_types/#envoypatchpolicy
type EnvoyGatewayLimitadorClusterReconciler struct {
*reconcilers.BaseReconciler
}

//+kubebuilder:rbac:groups=gateway.envoyproxy.io,resources=envoypatchpolicies,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=gateway.envoyproxy.io,resources=envoyextensionpolicies,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gateways,verbs=get;list;watch;update;patch

// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *EnvoyGatewayLimitadorClusterReconciler) Reconcile(eventCtx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := r.Logger().WithValues("envoyExtensionPolicy", req.NamespacedName)
logger.V(1).Info("Reconciling limitador cluster")
ctx := logr.NewContext(eventCtx, logger)

Check warning on line 41 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L38-L41

Added lines #L38 - L41 were not covered by tests

extPolicy := &egv1alpha1.EnvoyExtensionPolicy{}
if err := r.Client().Get(ctx, req.NamespacedName, extPolicy); err != nil {
if apierrors.IsNotFound(err) {
logger.Info("no envoygateway extension policy object found")
return ctrl.Result{}, nil

Check warning on line 47 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L43-L47

Added lines #L43 - L47 were not covered by tests
}
logger.Error(err, "failed to get envoygateway extension policy object")
return ctrl.Result{}, err

Check warning on line 50 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L49-L50

Added lines #L49 - L50 were not covered by tests
}

if logger.V(1).Enabled() {
jsonData, err := json.MarshalIndent(extPolicy.Spec.PolicyTargetReferences, "", " ")
if err != nil {
return ctrl.Result{}, err

Check warning on line 56 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L53-L56

Added lines #L53 - L56 were not covered by tests
}
logger.V(1).Info(string(jsonData))

Check warning on line 58 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L58

Added line #L58 was not covered by tests
}

if extPolicy.DeletionTimestamp != nil {

Check warning on line 61 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L61

Added line #L61 was not covered by tests
// no need to handle deletion
// ownerrefs will do the job
return ctrl.Result{}, nil

Check warning on line 64 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L64

Added line #L64 was not covered by tests
}

//
// Get kuadrant
//
kuadrantList := &kuadrantv1beta1.KuadrantList{}
err := r.Client().List(ctx, kuadrantList)
if err != nil {
return ctrl.Result{}, err

Check warning on line 73 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L70-L73

Added lines #L70 - L73 were not covered by tests
}
if len(kuadrantList.Items) == 0 {
logger.Info("kuadrant object not found. Nothing to do")
return ctrl.Result{}, nil

Check warning on line 77 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L75-L77

Added lines #L75 - L77 were not covered by tests
}

kObj := kuadrantList.Items[0]

Check warning on line 80 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L80

Added line #L80 was not covered by tests

//
// Get limitador
//
limitadorKey := client.ObjectKey{Name: common.LimitadorName, Namespace: kObj.Namespace}
limitador := &limitadorv1alpha1.Limitador{}
err = r.Client().Get(ctx, limitadorKey, limitador)
logger.V(1).Info("read limitador", "key", limitadorKey, "err", err)
if err != nil {
if apierrors.IsNotFound(err) {
logger.Info("limitador object not found. Nothing to do")
return ctrl.Result{}, nil

Check warning on line 92 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L85-L92

Added lines #L85 - L92 were not covered by tests
}
return ctrl.Result{}, err

Check warning on line 94 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L94

Added line #L94 was not covered by tests
}

if !meta.IsStatusConditionTrue(limitador.Status.Conditions, "Ready") {
logger.Info("limitador status reports not ready. Retrying")
return ctrl.Result{Requeue: true}, nil

Check warning on line 99 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L97-L99

Added lines #L97 - L99 were not covered by tests
}

limitadorClusterPatchPolicy, err := r.desiredLimitadorClusterPatchPolicy(ctx, extPolicy, limitador)
if err != nil {
return ctrl.Result{}, err

Check warning on line 104 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L102-L104

Added lines #L102 - L104 were not covered by tests
}
err = r.ReconcileResource(ctx, &egv1alpha1.EnvoyPatchPolicy{}, limitadorClusterPatchPolicy, reconcilers.CreateOnlyMutator)
if err != nil {
return ctrl.Result{}, err

Check warning on line 108 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L106-L108

Added lines #L106 - L108 were not covered by tests
}

logger.V(1).Info("Envoygateway limitador cluster reconciled successfully")

Check warning on line 111 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L111

Added line #L111 was not covered by tests

return ctrl.Result{}, nil

Check warning on line 113 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L113

Added line #L113 was not covered by tests
}

func (r *EnvoyGatewayLimitadorClusterReconciler) desiredLimitadorClusterPatchPolicy(
ctx context.Context, extPolicy *egv1alpha1.EnvoyExtensionPolicy,

Check failure on line 117 in controllers/envoygateway_limitador_cluster_controller.go

View workflow job for this annotation

GitHub Actions / Lint

unused-parameter: parameter 'ctx' seems to be unused, consider removing or renaming it as _ (revive)
limitador *limitadorv1alpha1.Limitador) (*egv1alpha1.EnvoyPatchPolicy, error) {

Check warning on line 118 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L118

Added line #L118 was not covered by tests

patchPolicy := &egv1alpha1.EnvoyPatchPolicy{
TypeMeta: metav1.TypeMeta{
Kind: egv1alpha1.KindEnvoyPatchPolicy,
APIVersion: egv1alpha1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: LimitadorClusterEnvoyPatchPolicyName(extPolicy.GetName()),
Namespace: extPolicy.Namespace,
},
Spec: egv1alpha1.EnvoyPatchPolicySpec{

Check warning on line 129 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L120-L129

Added lines #L120 - L129 were not covered by tests
// Same target ref as the associated extension policy
TargetRef: extPolicy.Spec.PolicyTargetReferences.TargetRefs[0].LocalPolicyTargetReference,
Type: egv1alpha1.JSONPatchEnvoyPatchType,
JSONPatches: []egv1alpha1.EnvoyJSONPatchConfig{
limitadorClusterPatch(
limitador.Status.Service.Host,
int(limitador.Status.Service.Ports.GRPC),
),
},
},

Check warning on line 139 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L131-L139

Added lines #L131 - L139 were not covered by tests
}

// controller reference
// patchPolicy has ownerref to extension policy
if err := r.SetOwnerReference(extPolicy, patchPolicy); err != nil {
return nil, err

Check warning on line 145 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L144-L145

Added lines #L144 - L145 were not covered by tests
}

return patchPolicy, nil

Check warning on line 148 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L148

Added line #L148 was not covered by tests
}

func LimitadorClusterEnvoyPatchPolicyName(targetName string) string {
return fmt.Sprintf("patch-for-%s", targetName)

Check warning on line 152 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L151-L152

Added lines #L151 - L152 were not covered by tests
}

func limitadorClusterPatch(limitadorSvcHost string, limitadorGRPCPort int) egv1alpha1.EnvoyJSONPatchConfig {

Check warning on line 155 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L155

Added line #L155 was not covered by tests
// The patch defines the rate_limit_cluster, which provides the endpoint location of the external rate limit service.
// TODO(eguzki): Istio EnvoyFilter uses almost the same structure. DRY
patchUnstructured := map[string]any{
"name": common.KuadrantRateLimitClusterName,
"type": "STRICT_DNS",
"connect_timeout": "1s",
"lb_policy": "ROUND_ROBIN",
"http2_protocol_options": map[string]any{},
"load_assignment": map[string]any{
"cluster_name": common.KuadrantRateLimitClusterName,
"endpoints": []map[string]any{

Check warning on line 166 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L158-L166

Added lines #L158 - L166 were not covered by tests
{
"lb_endpoints": []map[string]any{

Check warning on line 168 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L168

Added line #L168 was not covered by tests
{
"endpoint": map[string]any{
"address": map[string]any{
"socket_address": map[string]any{
"address": limitadorSvcHost,
"port_value": limitadorGRPCPort,
},
},
},
},
},
},
},
},

Check warning on line 182 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L170-L182

Added lines #L170 - L182 were not covered by tests
}

patchRaw, _ := json.Marshal(patchUnstructured)
value := &apiextensionsv1.JSON{}
value.UnmarshalJSON(patchRaw)

Check warning on line 187 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L185-L187

Added lines #L185 - L187 were not covered by tests

return egv1alpha1.EnvoyJSONPatchConfig{
Type: egv1alpha1.ClusterEnvoyResourceType,
Name: common.KuadrantRateLimitClusterName,
Operation: egv1alpha1.JSONPatchOperation{
Op: egv1alpha1.JSONPatchOperationType("add"),
Path: "",
Value: value,
},

Check warning on line 196 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L189-L196

Added lines #L189 - L196 were not covered by tests
}
}

// SetupWithManager sets up the controller with the Manager.
func (r *EnvoyGatewayLimitadorClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
ok, err := kuadrantenvoygateway.IsEnvoyGatewayInstalled(mgr.GetRESTMapper())
if err != nil {
return err

Check warning on line 204 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L204

Added line #L204 was not covered by tests
}
if !ok {
r.Logger().Info("EnvoyGateway limitador cluster controller disabled. EnvoyGateway API was not found")
return nil
}

ok, err = kuadrantgatewayapi.IsGatewayAPIInstalled(mgr.GetRESTMapper())
if err != nil {
return err

Check warning on line 213 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L211-L213

Added lines #L211 - L213 were not covered by tests
}
if !ok {
r.Logger().Info("EnvoyGateway limitador cluster disabled. GatewayAPI was not found")
return nil

Check warning on line 217 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L215-L217

Added lines #L215 - L217 were not covered by tests
}

return ctrl.NewControllerManagedBy(mgr).
For(&egv1alpha1.EnvoyExtensionPolicy{}).
Owns(&egv1alpha1.EnvoyPatchPolicy{}).
Complete(r)

Check warning on line 223 in controllers/envoygateway_limitador_cluster_controller.go

View check run for this annotation

Codecov / codecov/patch

controllers/envoygateway_limitador_cluster_controller.go#L220-L223

Added lines #L220 - L223 were not covered by tests
}
Loading

0 comments on commit 223ccd0

Please sign in to comment.