From b9a77bc5b0a5c50e3c518e21ca5dadee3e0513ab Mon Sep 17 00:00:00 2001 From: zongzw Date: Tue, 22 Nov 2022 22:36:03 +0800 Subject: [PATCH 1/4] refactor deploying process after adding gatewayclass. --- controllers/gatewayclass_controller.go | 98 +++++++++++-------- go.mod | 2 +- go.sum | 4 +- main.go | 3 +- pkg/deployer.go | 6 +- pkg/parser.go | 2 +- pkg/types.go | 1 + pkg/utils.go | 27 ++++- .../hrs-filters-extensionref.yaml.j2 | 2 +- 9 files changed, 92 insertions(+), 53 deletions(-) diff --git a/controllers/gatewayclass_controller.go b/controllers/gatewayclass_controller.go index 55189a4..1ad8f1e 100644 --- a/controllers/gatewayclass_controller.go +++ b/controllers/gatewayclass_controller.go @@ -18,10 +18,12 @@ package controllers import ( "context" + "fmt" "time" "gitee.com/zongzw/bigip-kubernetes-gateway/pkg" - corev1 "k8s.io/api/core/v1" + "gitee.com/zongzw/f5-bigip-rest/utils" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -57,9 +59,22 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request zlog.V(1).Info("is handling " + req.NamespacedName.String()) if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { - // seems safe to unset even if this deleted gwc is not controlled by this controller - zlog.V(1).Info("just unset or ignored " + req.NamespacedName.String()) - pkg.ActiveSIGs.UnsetGatewayClass(req.NamespacedName.String()) + if gwc := pkg.ActiveSIGs.GetGatewayClass(req.NamespacedName.String()); gwc != nil { + gws := pkg.ActiveSIGs.AttachedGateways(gwc) + if ocfgs, err := pkg.ParseRelated(gws, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { + return ctrl.Result{}, err + } else { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("clearing gateways for gatewayclass '%s'", req.NamespacedName.String()), + From: &ocfgs, + To: nil, + StatusFunc: func() { + pkg.ActiveSIGs.UnsetGatewayClass(req.NamespacedName.String()) + }, + Partition: req.NamespacedName.String(), + } + } + } return ctrl.Result{}, nil } else { return ctrl.Result{}, err @@ -67,7 +82,6 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request } else { // upsert gatewayclass zlog.V(1).Info("upserting " + req.NamespacedName.String()) - ngwc := obj.DeepCopy() if ngwc.Spec.ControllerName != gatewayv1beta1.GatewayController(pkg.ActiveSIGs.ControllerName) { @@ -75,42 +89,33 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, nil } - // ogwc := pkg.ActiveSIGs.GetGatewayClass(req.NamespacedName.String()) - pkg.ActiveSIGs.SetGatewayClass(ngwc) - - // TODO: add logic more here. but don't want to compare configmap modifications and execute each time? - // create partiton in the bigip here since we consider gwc name to be partiton name - if ngwc.Spec.ParametersRef == nil { - ngwc.Status.Conditions = []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: string(gatewayv1beta1.GatewayClassReasonAccepted), - Message: "Accepted message", - LastTransitionTime: metav1.NewTime(time.Now()), - }, - } + ogwc := pkg.ActiveSIGs.GetGatewayClass(req.NamespacedName.String()) - if err := r.Status().Update(ctx, ngwc); err != nil { - zlog.V(1).Error(err, "unable to update status") - return ctrl.Result{}, err - } else { - zlog.V(1).Info("status updated") - return ctrl.Result{}, nil - } - } else { - if string(ngwc.Spec.ParametersRef.Group) != "core" { - zlog.V(1).Info("not core") + ocfgs := map[string]interface{}{} + ncfgs := map[string]interface{}{} + var err error + if ogwc != nil { + gws := pkg.ActiveSIGs.AttachedGateways(ogwc) + if ocfgs, err = pkg.ParseRelated(gws, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { return ctrl.Result{}, err } - - if string(ngwc.Spec.ParametersRef.Kind) != "ConfigMap" { - zlog.V(1).Info("not ConfigMap") - return ctrl.Result{}, err + } + pkg.ActiveSIGs.SetGatewayClass(ngwc) + gws := pkg.ActiveSIGs.AttachedGateways(ngwc) + if ncfgs, err = pkg.ParseRelated(gws, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { + return ctrl.Result{}, err + } + // TODO: add logic more here. but don't want to compare configmap modifications and execute each time? + // create partiton in the bigip here since we consider gwc name to be partiton name + if ngwc.Spec.ParametersRef != nil { + if string(ngwc.Spec.ParametersRef.Group) != "" || + string(ngwc.Spec.ParametersRef.Kind) != "ConfigMap" { + return ctrl.Result{}, fmt.Errorf("not supported parameter ref type: %v/%v", + ngwc.Spec.ParametersRef.Group, ngwc.Spec.ParametersRef.Kind) } if ngwc.Spec.ParametersRef.Namespace == nil { - zlog.V(1).Info("ns nil") + zlog.V(1).Info("parameterRef's namespace cannot be nil") return ctrl.Result{}, err } @@ -119,35 +124,44 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request Name: ngwc.Spec.ParametersRef.Name, } - cm := &corev1.ConfigMap{} + cm := &v1.ConfigMap{} if err := r.Get(ctx, key, cm); err != nil { return ctrl.Result{}, err } else { - zlog.V(1).Info("to handle configmap here " + cm.Name) + zlog.V(1).Info("to handle configmap: " + utils.Keyname(cm.Namespace, cm.Name)) // TODO: add more config for bigip here // e.g. pkg.ActiveSIGs.Bigip.CreateVxlanTunnel(cm.Data["flannel_vxlan_tunnel_name"], cm.Data["flannel_vxlan_tunnel_port"]) + } + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("refreshing gateways for gatewayclass '%s'", req.NamespacedName.String()), + From: &ocfgs, + To: &ncfgs, + StatusFunc: func() { ngwc.Status.Conditions = []metav1.Condition{ { Type: "Accepted", Status: metav1.ConditionTrue, Reason: string(gatewayv1beta1.GatewayClassReasonAccepted), - Message: "handled configmap", + Message: "Accepted message", LastTransitionTime: metav1.NewTime(time.Now()), }, } if err := r.Status().Update(ctx, ngwc); err != nil { zlog.V(1).Error(err, "unable to update status") - return ctrl.Result{}, err + // return ctrl.Result{}, err } else { zlog.V(1).Info("status updated") - return ctrl.Result{}, nil + // return ctrl.Result{}, nil } - } - + }, + Partition: req.NamespacedName.String(), } } + return ctrl.Result{}, nil } // SetupWithManager sets up the controller with the Manager. diff --git a/go.mod b/go.mod index e5b039d..89a2f3e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module gitee.com/zongzw/bigip-kubernetes-gateway go 1.19 require ( - gitee.com/zongzw/f5-bigip-rest v0.0.0-20221117140648-4705d0a2b544 + gitee.com/zongzw/f5-bigip-rest v0.0.0-20221121060005-0cad3f2776a4 github.com/onsi/ginkgo/v2 v2.3.1 github.com/onsi/gomega v1.22.1 github.com/prometheus/client_golang v1.13.0 diff --git a/go.sum b/go.sum index 9a4d324..cbc3bfb 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gitee.com/zongzw/f5-bigip-rest v0.0.0-20221117140648-4705d0a2b544 h1:OBHMeTaiJmEbsMLyEvshbJ/A16i/VHA4t2fKp2Fz1dc= -gitee.com/zongzw/f5-bigip-rest v0.0.0-20221117140648-4705d0a2b544/go.mod h1:jx0Y6qhir/J/75V5tCYrLlmKPaZUEfbCg8fS6Xo6Syw= +gitee.com/zongzw/f5-bigip-rest v0.0.0-20221121060005-0cad3f2776a4 h1:hlPfKafUdIROQWbTm6S6ggUrmJ/sO6JFgWYq9GHApX8= +gitee.com/zongzw/f5-bigip-rest v0.0.0-20221121060005-0cad3f2776a4/go.mod h1:jx0Y6qhir/J/75V5tCYrLlmKPaZUEfbCg8fS6Xo6Syw= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= diff --git a/main.go b/main.go index 77d13cf..8ad2eca 100644 --- a/main.go +++ b/main.go @@ -136,7 +136,8 @@ func main() { os.Exit(1) } - prometheus.MustRegister(utils.FunctionDurationTimeCost) + prometheus.MustRegister(utils.FunctionDurationTimeCostCount) + prometheus.MustRegister(utils.FunctionDurationTimeCostTotal) prometheus.MustRegister(f5_bigip.BIGIPiControlTimeCostCount) prometheus.MustRegister(f5_bigip.BIGIPiControlTimeCostTotal) diff --git a/pkg/deployer.go b/pkg/deployer.go index f5437a9..b14e5be 100644 --- a/pkg/deployer.go +++ b/pkg/deployer.go @@ -5,10 +5,10 @@ import ( "gitee.com/zongzw/f5-bigip-rest/utils" ) -func deploy(bigip *f5_bigip.BIGIP, ocfgs, ncfgs *map[string]interface{}) error { +func deploy(bigip *f5_bigip.BIGIP, partition string, ocfgs, ncfgs *map[string]interface{}) error { defer utils.TimeItToPrometheus()() - cmds, err := bigip.GenRestRequests("cis-c-tenant", ocfgs, ncfgs) + cmds, err := bigip.GenRestRequests(partition, ocfgs, ncfgs) if err != nil { return err } @@ -22,7 +22,7 @@ func Deployer(stopCh chan struct{}, bigip *f5_bigip.BIGIP) { return case r := <-PendingDeploys: slog.Debugf("Processing request: %s", r.Meta) - err := deploy(bigip, r.From, r.To) + err := deploy(bigip, r.Partition, r.From, r.To) if err != nil { // report the error to status or ... slog.Errorf("failed to do deployment: %s", err.Error()) diff --git a/pkg/parser.go b/pkg/parser.go index e4bb8d4..061e730 100644 --- a/pkg/parser.go +++ b/pkg/parser.go @@ -186,7 +186,7 @@ func parsePoolsFrom(hr *gatewayv1beta1.HTTPRoute, rlt map[string]interface{}) er for _, fl := range rl.Filters { if fl.Type == gatewayv1beta1.HTTPRouteFilterExtensionRef && fl.ExtensionRef != nil { er := fl.ExtensionRef - if er.Group != "v1" || er.Kind != "Service" { + if er.Group != "" || er.Kind != "Service" { return fmt.Errorf("resource %s of '%s' not supported", er.Name, utils.Keyname(string(er.Group), string(er.Kind))) } else { if err := creatPool(hr.Namespace, string(er.Name), rlt); err != nil { diff --git a/pkg/types.go b/pkg/types.go index 835f4b2..7102e32 100644 --- a/pkg/types.go +++ b/pkg/types.go @@ -12,6 +12,7 @@ type DeployRequest struct { Meta string From *map[string]interface{} To *map[string]interface{} + Partition string StatusFunc func() } diff --git a/pkg/utils.go b/pkg/utils.go index fb7c54d..d979228 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -178,6 +178,29 @@ func (c *SIGCache) UnsetService(keyname string) { // return ips // } +func (c *SIGCache) AttachedGateways(gtw *gatewayv1beta1.GatewayClass) []*gatewayv1beta1.Gateway { + defer utils.TimeItToPrometheus()() + + c.mutex.RLock() + defer c.mutex.RUnlock() + + return c._attachedGateways(gtw) +} + +func (c *SIGCache) _attachedGateways(gwc *gatewayv1beta1.GatewayClass) []*gatewayv1beta1.Gateway { + if gwc == nil { + return []*gatewayv1beta1.Gateway{} + } + + gws := []*gatewayv1beta1.Gateway{} + for _, gw := range c.Gateway { + if gw.Spec.GatewayClassName == gatewayv1beta1.ObjectName(gwc.Name) { + gws = append(gws, gw) + } + } + return gws +} + func (c *SIGCache) GatewayRefsOf(hr *gatewayv1beta1.HTTPRoute) []*gatewayv1beta1.Gateway { defer utils.TimeItToPrometheus()() @@ -264,7 +287,7 @@ func (c *SIGCache) _serviceRefsOf(hr *gatewayv1beta1.HTTPRoute) []*v1.Service { for _, fl := range rl.Filters { if fl.Type == gatewayv1beta1.HTTPRouteFilterExtensionRef && fl.ExtensionRef != nil { er := fl.ExtensionRef - if er.Group == "v1" && er.Kind == "Service" { + if er.Group == "" && er.Kind == "Service" { if svc, ok := c.Service[utils.Keyname(hr.Namespace, string(er.Name))]; ok { svcs = append(svcs, svc) } @@ -305,7 +328,7 @@ func (c *SIGCache) _HTTPRoutesRefsOf(svc *v1.Service) []*gatewayv1beta1.HTTPRout for _, fl := range rl.Filters { if fl.Type == gatewayv1beta1.HTTPRouteFilterExtensionRef && fl.ExtensionRef != nil { er := fl.ExtensionRef - if er.Group == "v1" && er.Kind == "Service" { + if er.Group == "" && er.Kind == "Service" { if utils.Keyname(hr.Namespace, string(er.Name)) == utils.Keyname(svc.Namespace, svc.Name) { return true } diff --git a/tests/templates/hrs-filters-extensionref.yaml.j2 b/tests/templates/hrs-filters-extensionref.yaml.j2 index bfcb0f0..0ae88e6 100644 --- a/tests/templates/hrs-filters-extensionref.yaml.j2 +++ b/tests/templates/hrs-filters-extensionref.yaml.j2 @@ -15,6 +15,6 @@ spec: - filters: - type: ExtensionRef extensionRef: - group: v1 + group: "" kind: Service name: test-service \ No newline at end of file From 9892eb4d8f159c62a3d684f14ff8e587eb5699de Mon Sep 17 00:00:00 2001 From: zongzw Date: Thu, 24 Nov 2022 19:42:50 +0800 Subject: [PATCH 2/4] make gatewayclass and gateway ready. --- .vscode/launch.json | 3 +- controllers/gateway_controller.go | 76 +++++++-- controllers/gatewayclass_controller.go | 70 ++++---- controllers/httproute_controller.go | 107 +++++++++--- controllers/v1_controller.go | 221 ++++++++++++++++++++----- pkg/deployer.go | 3 + pkg/parser.go | 72 +++++++- pkg/utils.go | 64 +++++-- 8 files changed, 473 insertions(+), 143 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 429230b..8cd2eca 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -22,7 +22,8 @@ // "--bigip-url", "https://10.250.15.180", "--bigip-url", "https://10.250.18.105:8443", "--bigip-username", "admin", - "--bigip-password", "P@ssw0rd123" + "--bigip-password", "P@ssw0rd123", + "--controller-name", "f5.io/gateway-controller-name" ] } ] diff --git a/controllers/gateway_controller.go b/controllers/gateway_controller.go index 288d958..ac7ad0b 100644 --- a/controllers/gateway_controller.go +++ b/controllers/gateway_controller.go @@ -21,7 +21,6 @@ import ( "fmt" "time" - v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -60,13 +59,13 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if client.IgnoreNotFound(err) == nil { // delete resources gw := pkg.ActiveSIGs.GetGateway(req.NamespacedName.String()) - if ocfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{gw}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { + if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{gw}, nil, nil); err != nil { return ctrl.Result{}, err } else { zlog.V(1).Info("handling + deleting " + req.NamespacedName.String()) hrs := pkg.ActiveSIGs.AttachedHTTPRoutes(gw) pkg.ActiveSIGs.UnsetGateway(req.NamespacedName.String()) - if ncfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, hrs, []*v1.Service{}); err != nil { + if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, hrs, nil); err != nil { return ctrl.Result{}, err } else { pkg.PendingDeploys <- pkg.DeployRequest{ @@ -76,6 +75,7 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct StatusFunc: func() { // do something }, + Partition: string(gw.Spec.GatewayClassName), } } } @@ -88,23 +88,69 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct // upsert resources zlog.V(1).Info("handling + upserting " + req.NamespacedName.String()) ogw := pkg.ActiveSIGs.GetGateway(req.NamespacedName.String()) - if ocfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{ogw}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { + if ogw == nil { + ogw = &obj + pkg.ActiveSIGs.SetGateway(obj.DeepCopy()) + } + + ohrs := pkg.ActiveSIGs.AttachedHTTPRoutes(ogw) + if ocfgs, err := pkg.ParseRelatedForClass(string(ogw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ogw}, nil, nil); err != nil { zlog.Error(err, "handling + upserting + parse related ocfgs "+req.NamespacedName.String()) return ctrl.Result{}, err } else { ngw := obj.DeepCopy() - pkg.ActiveSIGs.SetGateway(ngw) - if ncfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{ngw}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { - zlog.Error(err, "handling + upserting + parse related ncfgs "+req.NamespacedName.String()) - return ctrl.Result{}, err + if ngw.Spec.GatewayClassName == ogw.Spec.GatewayClassName { + pkg.ActiveSIGs.SetGateway(ngw) + if ncfgs, err := pkg.ParseRelatedForClass(string(ngw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ngw}, nil, nil); err != nil { + zlog.Error(err, "handling + upserting + parse related ncfgs "+req.NamespacedName.String()) + return ctrl.Result{}, err + } else { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), + From: &ocfgs, + To: &ncfgs, + StatusFunc: func() { + // do something + }, + Partition: string(ngw.Spec.GatewayClassName), + } + return ctrl.Result{}, nil + } } else { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() { - // do something - }, + // original state of new gatewayclass env + ocfgsN, err := pkg.ParseRelatedForClass(string(ngw.Spec.GatewayClassName), nil, ohrs, nil) + if err != nil { + return ctrl.Result{}, err + } + pkg.ActiveSIGs.SetGateway(ngw) + + // gateway is go away + if ncfgsO, err := pkg.ParseRelatedForClass(string(ogw.Spec.GatewayClassName), nil, ohrs, nil); err != nil { + return ctrl.Result{}, err + } else { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), + From: &ocfgs, + To: &ncfgsO, + StatusFunc: func() { + // do something + }, + Partition: string(ogw.Spec.GatewayClassName), + } + } + + if ncfgs, err := pkg.ParseRelatedForClass(string(ngw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ngw}, nil, nil); err != nil { + return ctrl.Result{}, err + } else { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), + From: &ocfgsN, + To: &ncfgs, + StatusFunc: func() { + // do something + }, + Partition: string(ngw.Spec.GatewayClassName), + } } return ctrl.Result{}, nil } diff --git a/controllers/gatewayclass_controller.go b/controllers/gatewayclass_controller.go index 1ad8f1e..a5a6458 100644 --- a/controllers/gatewayclass_controller.go +++ b/controllers/gatewayclass_controller.go @@ -49,6 +49,9 @@ type GatewayClassReconciler struct { // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.0/pkg/reconcile func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { zlog := log.FromContext(ctx) + if req.Namespace != "" { + return ctrl.Result{}, fmt.Errorf("gateway class namespace must be ''") + } if !pkg.ActiveSIGs.SyncedAtStart { <-time.After(100 * time.Millisecond) @@ -56,22 +59,23 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request } var obj gatewayv1beta1.GatewayClass - zlog.V(1).Info("is handling " + req.NamespacedName.String()) + zlog.V(1).Info("handling gatewayclass " + req.Name) if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { - if gwc := pkg.ActiveSIGs.GetGatewayClass(req.NamespacedName.String()); gwc != nil { + zlog.V(1).Info("deleting gatewayclass " + req.Name) + if gwc := pkg.ActiveSIGs.GetGatewayClass(req.Name); gwc != nil { gws := pkg.ActiveSIGs.AttachedGateways(gwc) - if ocfgs, err := pkg.ParseRelated(gws, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { + if ocfgs, err := pkg.ParseGatewayRelated(gws); err != nil { return ctrl.Result{}, err } else { pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("clearing gateways for gatewayclass '%s'", req.NamespacedName.String()), + Meta: fmt.Sprintf("clearing gateways for gatewayclass '%s'", req.Name), From: &ocfgs, To: nil, StatusFunc: func() { - pkg.ActiveSIGs.UnsetGatewayClass(req.NamespacedName.String()) + pkg.ActiveSIGs.UnsetGatewayClass(req.Name) }, - Partition: req.NamespacedName.String(), + Partition: req.Name, } } } @@ -81,28 +85,28 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request } } else { // upsert gatewayclass - zlog.V(1).Info("upserting " + req.NamespacedName.String()) + zlog.V(1).Info("upserting gatewayclass " + req.Name) ngwc := obj.DeepCopy() if ngwc.Spec.ControllerName != gatewayv1beta1.GatewayController(pkg.ActiveSIGs.ControllerName) { - zlog.V(1).Info("ignore this gwc as its controllerName does not match this controller" + req.NamespacedName.String()) + zlog.V(1).Info("ignore this gwc " + req.Name + " as its controllerName does not match " + pkg.ActiveSIGs.ControllerName) return ctrl.Result{}, nil } - ogwc := pkg.ActiveSIGs.GetGatewayClass(req.NamespacedName.String()) + ogwc := pkg.ActiveSIGs.GetGatewayClass(req.Name) ocfgs := map[string]interface{}{} ncfgs := map[string]interface{}{} var err error if ogwc != nil { gws := pkg.ActiveSIGs.AttachedGateways(ogwc) - if ocfgs, err = pkg.ParseRelated(gws, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { + if ocfgs, err = pkg.ParseGatewayRelated(gws); err != nil { return ctrl.Result{}, err } } pkg.ActiveSIGs.SetGatewayClass(ngwc) gws := pkg.ActiveSIGs.AttachedGateways(ngwc) - if ncfgs, err = pkg.ParseRelated(gws, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { + if ncfgs, err = pkg.ParseGatewayRelated(gws); err != nil { return ctrl.Result{}, err } // TODO: add logic more here. but don't want to compare configmap modifications and execute each time? @@ -135,30 +139,28 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request } } - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("refreshing gateways for gatewayclass '%s'", req.NamespacedName.String()), - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() { - ngwc.Status.Conditions = []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - Reason: string(gatewayv1beta1.GatewayClassReasonAccepted), - Message: "Accepted message", - LastTransitionTime: metav1.NewTime(time.Now()), - }, - } - - if err := r.Status().Update(ctx, ngwc); err != nil { - zlog.V(1).Error(err, "unable to update status") - // return ctrl.Result{}, err - } else { - zlog.V(1).Info("status updated") - // return ctrl.Result{}, nil - } + ngwc.Status.Conditions = []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, + Reason: string(gatewayv1beta1.GatewayClassReasonAccepted), + Message: "Accepted message", + LastTransitionTime: metav1.NewTime(time.Now()), }, - Partition: req.NamespacedName.String(), + } + + if err := r.Status().Update(ctx, ngwc); err != nil { + zlog.V(1).Error(err, "unable to update status") + return ctrl.Result{}, err + } else { + zlog.V(1).Info("status updated") + } + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("refreshing gateways for gatewayclass '%s'", req.Name), + From: &ocfgs, + To: &ncfgs, + StatusFunc: func() {}, + Partition: req.Name, } } return ctrl.Result{}, nil diff --git a/controllers/httproute_controller.go b/controllers/httproute_controller.go index 88a3d1a..e2b4bfa 100644 --- a/controllers/httproute_controller.go +++ b/controllers/httproute_controller.go @@ -59,25 +59,52 @@ func (r *HttpRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if client.IgnoreNotFound(err) == nil { // delete resources hr := pkg.ActiveSIGs.GetHTTPRoute(req.NamespacedName.String()) - if ocfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{hr}, []*v1.Service{}); err != nil { - return ctrl.Result{}, err - } else { - gws := pkg.ActiveSIGs.GatewayRefsOf(hr) - pkg.ActiveSIGs.UnsetHTTPRoute(req.NamespacedName.String()) - if ncfgs, err := pkg.ParseRelated(gws, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}); err != nil { + gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} + pkg.ActiveSIGs.GetRelatedObjs(nil, []*gatewayv1beta1.HTTPRoute{hr}, nil, &gwmap, &hrmap, &svcmap) + + drs := map[string]pkg.DeployRequest{} + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting httproute '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, []*gatewayv1beta1.HTTPRoute{hr}, nil); err != nil { return ctrl.Result{}, err } else { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("deleting httproute '%s'", req.NamespacedName.String()), - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() { - - }, + dr.From = &ocfgs + } + } + gws := pkg.ActiveSIGs.GatewayRefsOf(hr) + pkg.ActiveSIGs.UnsetHTTPRoute(req.NamespacedName.String()) + for _, gw := range gwmap { + + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting httproute '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), } } - + dr := drs[string(gw.Spec.GatewayClassName)] + if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), gws, nil, nil); err != nil { + return ctrl.Result{}, err + } else { + dr.To = &ncfgs + } + } + for _, dr := range drs { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: dr.Meta, + From: dr.From, + To: dr.To, + StatusFunc: func() { + }, + Partition: dr.Partition, + } } + return ctrl.Result{}, nil } else { return ctrl.Result{}, err @@ -86,24 +113,50 @@ func (r *HttpRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // upsert resources zlog.V(1).Info("upserting " + req.NamespacedName.String()) hr := pkg.ActiveSIGs.GetHTTPRoute(req.NamespacedName.String()) - if ocfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{hr}, []*v1.Service{}); err != nil { - return ctrl.Result{}, err - } else { - nhr := obj.DeepCopy() - pkg.ActiveSIGs.SetHTTPRoute(nhr) - if ncfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{nhr}, []*v1.Service{}); err != nil { + gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} + pkg.ActiveSIGs.GetRelatedObjs(nil, []*gatewayv1beta1.HTTPRoute{hr}, nil, &gwmap, &hrmap, &svcmap) + drs := map[string]pkg.DeployRequest{} + + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting httproute '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, []*gatewayv1beta1.HTTPRoute{hr}, nil); err != nil { return ctrl.Result{}, err } else { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting httproute '%s'", req.NamespacedName.String()), - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() { - - }, + dr.From = &ocfgs + } + } + nhr := obj.DeepCopy() + pkg.ActiveSIGs.SetHTTPRoute(nhr) + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting httproute '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), } } + dr := drs[string(gw.Spec.GatewayClassName)] + if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, []*gatewayv1beta1.HTTPRoute{nhr}, nil); err != nil { + return ctrl.Result{}, err + } else { + dr.To = &ncfgs + } + } + for _, dr := range drs { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: dr.Meta, + From: dr.From, + To: dr.To, + StatusFunc: func() { + }, + Partition: dr.Partition, + } } return ctrl.Result{}, nil } diff --git a/controllers/v1_controller.go b/controllers/v1_controller.go index ad03c30..1052d7a 100644 --- a/controllers/v1_controller.go +++ b/controllers/v1_controller.go @@ -19,7 +19,6 @@ package controllers import ( "context" "fmt" - "reflect" "gitee.com/zongzw/bigip-kubernetes-gateway/k8s" "gitee.com/zongzw/bigip-kubernetes-gateway/pkg" @@ -57,33 +56,100 @@ func (r *EndpointsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - ocfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{svc}) - if err != nil { - return ctrl.Result{}, err + gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} + pkg.ActiveSIGs.GetRelatedObjs(nil, nil, []*v1.Service{svc}, &gwmap, &hrmap, &svcmap) + drs := map[string]pkg.DeployRequest{} + + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting endpoints '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + return ctrl.Result{}, err + } else { + dr.From = &ocfgs + } } hrs := pkg.ActiveSIGs.HTTPRoutesRefsOf(svc) pkg.ActiveSIGs.UnsetEndpoints(req.NamespacedName.String()) - ncfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, hrs, []*v1.Service{}) - if err != nil { - return ctrl.Result{}, err + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting endpoints '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, hrs, nil); err != nil { + return ctrl.Result{}, err + } else { + dr.To = &ncfgs + } + } + for _, dr := range drs { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: dr.Meta, + From: dr.From, + To: dr.To, + StatusFunc: func() { + }, + Partition: dr.Partition, + } } - applyCfgs(ocfgs, ncfgs) return ctrl.Result{}, nil } else { return ctrl.Result{}, err } } else { svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - ocfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{svc}) - if err != nil { - return ctrl.Result{}, err + gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} + pkg.ActiveSIGs.GetRelatedObjs(nil, nil, []*v1.Service{svc}, &gwmap, &hrmap, &svcmap) + + drs := map[string]pkg.DeployRequest{} + + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting endpoints '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + return ctrl.Result{}, err + } else { + dr.From = &ocfgs + } } pkg.ActiveSIGs.SetEndpoints(obj.DeepCopy()) - ncfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{svc}) - if err != nil { - return ctrl.Result{}, err + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting endpoints '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + return ctrl.Result{}, err + } else { + dr.To = &ncfgs + } + } + for _, dr := range drs { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: dr.Meta, + From: dr.From, + To: dr.To, + StatusFunc: func() { + }, + Partition: dr.Partition, + } } - applyCfgs(ocfgs, ncfgs) return ctrl.Result{}, nil } } @@ -96,33 +162,103 @@ func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - ocfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{svc}) - if err != nil { - return ctrl.Result{}, err + + gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} + pkg.ActiveSIGs.GetRelatedObjs(nil, nil, []*v1.Service{svc}, &gwmap, &hrmap, &svcmap) + drs := map[string]pkg.DeployRequest{} + + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting service '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + return ctrl.Result{}, err + } else { + dr.From = &ocfgs + } } hrs := pkg.ActiveSIGs.HTTPRoutesRefsOf(svc) pkg.ActiveSIGs.UnsetService(req.NamespacedName.String()) - ncfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, hrs, []*v1.Service{}) - if err != nil { - return ctrl.Result{}, err + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting service '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, hrs, nil); err != nil { + return ctrl.Result{}, err + } else { + dr.To = &ncfgs + } + } + for _, dr := range drs { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: dr.Meta, + From: dr.From, + To: dr.To, + StatusFunc: func() { + }, + Partition: dr.Partition, + } } - applyCfgs(ocfgs, ncfgs) + return ctrl.Result{}, nil } else { return ctrl.Result{}, err } } else { svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - ocfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{svc}) - if err != nil { - return ctrl.Result{}, err + gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} + pkg.ActiveSIGs.GetRelatedObjs(nil, nil, []*v1.Service{svc}, &gwmap, &hrmap, &svcmap) + + drs := map[string]pkg.DeployRequest{} + + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting service '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + return ctrl.Result{}, err + } else { + dr.From = &ocfgs + } } pkg.ActiveSIGs.SetService(obj.DeepCopy()) - ncfgs, err := pkg.ParseRelated([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{svc}) - if err != nil { - return ctrl.Result{}, err + for _, gw := range gwmap { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting service '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + return ctrl.Result{}, err + } else { + dr.To = &ncfgs + } + } + for _, dr := range drs { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: dr.Meta, + From: dr.From, + To: dr.To, + StatusFunc: func() { + }, + Partition: dr.Partition, + } } - applyCfgs(ocfgs, ncfgs) + return ctrl.Result{}, nil } } @@ -169,16 +305,17 @@ func SetupReconcilerForCoreV1WithManager(mgr ctrl.Manager) error { } } -func applyCfgs(ocfgs, ncfgs map[string]interface{}) { - if reflect.DeepEqual(ocfgs, ncfgs) { - return - } - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: "upserting svc/eps", - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() { - // do something - }, - } -} +// func applyCfgs(gwc string, ocfgs, ncfgs map[string]interface{}) { +// if reflect.DeepEqual(ocfgs, ncfgs) { +// return +// } +// pkg.PendingDeploys <- pkg.DeployRequest{ +// Meta: "upserting svc/eps", +// From: &ocfgs, +// To: &ncfgs, +// StatusFunc: func() { +// // do something +// }, +// Partition: gwc, +// } +// } diff --git a/pkg/deployer.go b/pkg/deployer.go index b14e5be..0ebaedd 100644 --- a/pkg/deployer.go +++ b/pkg/deployer.go @@ -8,6 +8,9 @@ import ( func deploy(bigip *f5_bigip.BIGIP, partition string, ocfgs, ncfgs *map[string]interface{}) error { defer utils.TimeItToPrometheus()() + if err := bigip.DeployPartition(partition); err != nil { + return err + } cmds, err := bigip.GenRestRequests(partition, ocfgs, ncfgs) if err != nil { return err diff --git a/pkg/parser.go b/pkg/parser.go index 061e730..0947d57 100644 --- a/pkg/parser.go +++ b/pkg/parser.go @@ -10,7 +10,7 @@ import ( gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -func ParseHTTPRoute(hr *gatewayv1beta1.HTTPRoute) (map[string]interface{}, error) { +func ParseHTTPRoute(className string, hr *gatewayv1beta1.HTTPRoute) (map[string]interface{}, error) { defer utils.TimeItToPrometheus()() if hr == nil { @@ -22,7 +22,7 @@ func ParseHTTPRoute(hr *gatewayv1beta1.HTTPRoute) (map[string]interface{}, error return map[string]interface{}{}, err } - if err := parseiRulesFrom(hr, rlt); err != nil { + if err := parseiRulesFrom(className, hr, rlt); err != nil { return map[string]interface{}{}, err } @@ -104,12 +104,62 @@ func ParseGateway(gw *gatewayv1beta1.Gateway) (map[string]interface{}, error) { return rlt, nil } -func ParseRelated(gwObjs []*gatewayv1beta1.Gateway, hrObjs []*gatewayv1beta1.HTTPRoute, svcObjs []*v1.Service) (map[string]interface{}, error) { +// func ParseRelated(gwObjs []*gatewayv1beta1.Gateway, hrObjs []*gatewayv1beta1.HTTPRoute, svcObjs []*v1.Service) (map[string]interface{}, error) { +// defer utils.TimeItToPrometheus()() + +// gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} +// ActiveSIGs.GetRelatedObjs(gwObjs, hrObjs, svcObjs, &gwmap, &hrmap, &svcmap) + +// rlt := map[string]interface{}{} +// for _, gw := range gwmap { +// if cfgs, err := ParseGateway(gw); err != nil { +// return map[string]interface{}{}, err +// } else { +// for k, v := range cfgs { +// rlt[k] = v +// } +// } +// } +// for _, hr := range hrmap { +// if cfgs, err := ParseHTTPRoute(hr); err != nil { +// return map[string]interface{}{}, err +// } else { +// for k, v := range cfgs { +// rlt[k] = v +// } +// } +// } + +// return map[string]interface{}{ +// "": rlt, +// }, nil +// } + +func ParseRelatedForClass(className string, gwObjs []*gatewayv1beta1.Gateway, hrObjs []*gatewayv1beta1.HTTPRoute, svcObjs []*v1.Service) (map[string]interface{}, error) { defer utils.TimeItToPrometheus()() + if ActiveSIGs.GetGatewayClass(className) == nil { + return map[string]interface{}{}, nil + } gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} ActiveSIGs.GetRelatedObjs(gwObjs, hrObjs, svcObjs, &gwmap, &hrmap, &svcmap) + cgwObjs := []*gatewayv1beta1.Gateway{} + for _, gw := range gwmap { + if gw.Spec.GatewayClassName == gatewayv1beta1.ObjectName(className) { + cgwObjs = append(cgwObjs, gw) + } + } + + return ParseGatewayRelated(cgwObjs) +} + +func ParseGatewayRelated(gwObjs []*gatewayv1beta1.Gateway) (map[string]interface{}, error) { + defer utils.TimeItToPrometheus()() + + gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} + ActiveSIGs.GetGatewayRelated(gwObjs, &gwmap, &hrmap, &svcmap) + rlt := map[string]interface{}{} for _, gw := range gwmap { if cfgs, err := ParseGateway(gw); err != nil { @@ -120,8 +170,16 @@ func ParseRelated(gwObjs []*gatewayv1beta1.Gateway, hrObjs []*gatewayv1beta1.HTT } } } + + // TODO: tune it. + className := "" + if len(gwObjs) == 0 { + return map[string]interface{}{}, fmt.Errorf("should not happen here") + } else { + className = string(gwObjs[0].Spec.GatewayClassName) + } for _, hr := range hrmap { - if cfgs, err := ParseHTTPRoute(hr); err != nil { + if cfgs, err := ParseHTTPRoute(className, hr); err != nil { return map[string]interface{}{}, err } else { for k, v := range cfgs { @@ -275,7 +333,7 @@ func parseNodesFrom(svcNamespace, svcName string, rlt map[string]interface{}) er return nil } -func parseiRulesFrom(hr *gatewayv1beta1.HTTPRoute, rlt map[string]interface{}) error { +func parseiRulesFrom(className string, hr *gatewayv1beta1.HTTPRoute, rlt map[string]interface{}) error { // irules name := strings.Join([]string{hr.Namespace, hr.Name}, ".") hostnameConditions := []string{} @@ -424,7 +482,7 @@ func parseiRulesFrom(hr *gatewayv1beta1.HTTPRoute, rlt map[string]interface{}) e case gatewayv1beta1.HTTPRouteFilterExtensionRef: if er := filter.ExtensionRef; er != nil { pool := fmt.Sprintf("%s.%s", hr.Namespace, er.Name) - filterActions = append(filterActions, fmt.Sprintf("pool /cis-c-tenant/%s", pool)) + filterActions = append(filterActions, fmt.Sprintf("pool /%s/%s", className, pool)) } } } @@ -437,7 +495,7 @@ func parseiRulesFrom(hr *gatewayv1beta1.HTTPRoute, rlt map[string]interface{}) e ns = string(*br.Namespace) } pn := strings.Join([]string{ns, string(br.Name)}, ".") - pool = fmt.Sprintf("pool /cis-c-tenant/%s", pn) + pool = fmt.Sprintf("pool /%s/%s", className, pn) } rules = append(rules, fmt.Sprintf(` if { %s } { diff --git a/pkg/utils.go b/pkg/utils.go index d979228..43f69be 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -42,7 +42,7 @@ func (c *SIGCache) SetGatewayClass(obj *gatewayv1beta1.GatewayClass) { defer c.mutex.Unlock() if obj != nil { - c.GatewayClasses[utils.Keyname(obj.Namespace, obj.Name)] = obj + c.GatewayClasses[obj.Name] = obj } } @@ -65,13 +65,7 @@ func (c *SIGCache) SetGateway(obj *gatewayv1beta1.Gateway) { defer c.mutex.Unlock() if obj != nil { - keyname := utils.Keyname(obj.Namespace, obj.Name) - if _, f := c.GatewayClasses[string(obj.Spec.GatewayClassName)]; f { - slog.Debugf("set this gw %s", keyname) - c.Gateway[keyname] = obj - } else { - delete(c.Gateway, keyname) - } + c.Gateway[utils.Keyname(obj.Namespace, obj.Name)] = obj } } @@ -432,6 +426,47 @@ func (c *SIGCache) _getRelatedObjs( } } +func (c *SIGCache) GetGatewayRelated( + gwObjs []*gatewayv1beta1.Gateway, + gwmap *map[string]*gatewayv1beta1.Gateway, + hrmap *map[string]*gatewayv1beta1.HTTPRoute, + svcmap *map[string]*v1.Service) { + + defer utils.TimeItToPrometheus()() + + c.mutex.RLock() + defer c.mutex.RUnlock() + + c._getGatewayRelated(gwObjs, gwmap, hrmap, svcmap) +} + +func (c *SIGCache) _getGatewayRelated( + gwObjs []*gatewayv1beta1.Gateway, + gwmap *map[string]*gatewayv1beta1.Gateway, + hrmap *map[string]*gatewayv1beta1.HTTPRoute, + svcmap *map[string]*v1.Service) { + for _, gwObj := range gwObjs { + if gwObj != nil { + name := utils.Keyname(gwObj.Namespace, gwObj.Name) + (*gwmap)[name] = c.Gateway[name] + } + hrs := c._attachedHTTPRoutes(gwObj) + for _, hr := range hrs { + name := utils.Keyname(hr.Namespace, hr.Name) + if _, ok := (*hrmap)[name]; !ok { + (*hrmap)[name] = hr + svcs := c._serviceRefsOf(hr) + for _, svc := range svcs { + name := utils.Keyname(svc.Namespace, svc.Name) + if _, ok := (*svcmap)[name]; !ok { + (*svcmap)[name] = svc + } + } + } + } + } +} + func (c *SIGCache) syncCoreV1Resources(kubeClient kubernetes.Interface) error { defer utils.TimeItToPrometheus()() @@ -498,8 +533,8 @@ func (c *SIGCache) syncGatewayResources(mgr manager.Manager) error { } else { for _, gwc := range gwcList.Items { if gwc.Spec.ControllerName == gatewayv1beta1.GatewayController(ActiveSIGs.ControllerName) { - slog.Infof("found gatewayclass %s", utils.Keyname(gwc.Namespace, gwc.Name)) - c.GatewayClasses[utils.Keyname(gwc.Namespace, gwc.Name)] = gwc.DeepCopy() + slog.Infof("found gatewayclass %s", gwc.Name) + c.GatewayClasses[gwc.Name] = gwc.DeepCopy() } else { slog.Infof("This gwc's ControllerName %s not equal to this controller. Ignore.", gwc.Spec.ControllerName) } @@ -510,13 +545,8 @@ func (c *SIGCache) syncGatewayResources(mgr manager.Manager) error { return err } else { for _, gw := range gtwList.Items { - if _, f := c.GatewayClasses[string(gw.Spec.GatewayClassName)]; f { - slog.Debugf("found gateway %s", utils.Keyname(gw.Namespace, gw.Name)) - c.Gateway[utils.Keyname(gw.Namespace, gw.Name)] = gw.DeepCopy() - } else { - slog.Infof("This gw's GatewayClassName %s not belong to this controller. Ignore.", gw.Spec.GatewayClassName) - delete(c.Gateway, utils.Keyname(gw.Namespace, gw.Name)) - } + slog.Debugf("found gateway %s", utils.Keyname(gw.Namespace, gw.Name)) + c.Gateway[utils.Keyname(gw.Namespace, gw.Name)] = gw.DeepCopy() } } if err := mgr.GetCache().List(context.TODO(), &hrList, &client.ListOptions{}); err != nil { From 6710dd2fca4c817abf8476bd618ca5f84133a92b Mon Sep 17 00:00:00 2001 From: zongzw Date: Thu, 24 Nov 2022 23:31:14 +0800 Subject: [PATCH 3/4] make httproute works. --- controllers/gateway_controller.go | 52 +++--- controllers/gatewayclass_controller.go | 6 +- controllers/httproute_controller.go | 30 ++- controllers/v1_controller.go | 51 +++--- pkg/parser.go | 151 ++++++++------- pkg/utils.go | 243 +++++++++++++++---------- 6 files changed, 302 insertions(+), 231 deletions(-) diff --git a/controllers/gateway_controller.go b/controllers/gateway_controller.go index ac7ad0b..9ed7b44 100644 --- a/controllers/gateway_controller.go +++ b/controllers/gateway_controller.go @@ -59,13 +59,14 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if client.IgnoreNotFound(err) == nil { // delete resources gw := pkg.ActiveSIGs.GetGateway(req.NamespacedName.String()) - if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{gw}, nil, nil); err != nil { + // Only when we know all the gateways can we know exactly which routes need to be cleared because of this gateway event. + gws := pkg.ActiveSIGs.GetNeighborGateways(gw) + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), append(gws, gw)); err != nil { return ctrl.Result{}, err } else { zlog.V(1).Info("handling + deleting " + req.NamespacedName.String()) - hrs := pkg.ActiveSIGs.AttachedHTTPRoutes(gw) pkg.ActiveSIGs.UnsetGateway(req.NamespacedName.String()) - if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, hrs, nil); err != nil { + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { pkg.PendingDeploys <- pkg.DeployRequest{ @@ -93,15 +94,14 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct pkg.ActiveSIGs.SetGateway(obj.DeepCopy()) } - ohrs := pkg.ActiveSIGs.AttachedHTTPRoutes(ogw) - if ocfgs, err := pkg.ParseRelatedForClass(string(ogw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ogw}, nil, nil); err != nil { + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(ogw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ogw}); err != nil { zlog.Error(err, "handling + upserting + parse related ocfgs "+req.NamespacedName.String()) return ctrl.Result{}, err } else { ngw := obj.DeepCopy() if ngw.Spec.GatewayClassName == ogw.Spec.GatewayClassName { pkg.ActiveSIGs.SetGateway(ngw) - if ncfgs, err := pkg.ParseRelatedForClass(string(ngw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ngw}, nil, nil); err != nil { + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(ngw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ngw}); err != nil { zlog.Error(err, "handling + upserting + parse related ncfgs "+req.NamespacedName.String()) return ctrl.Result{}, err } else { @@ -118,20 +118,21 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } } else { // original state of new gatewayclass env - ocfgsN, err := pkg.ParseRelatedForClass(string(ngw.Spec.GatewayClassName), nil, ohrs, nil) + // gateway is go away + ngs := pkg.ActiveSIGs.GetNeighborGateways(ogw) + + ocfgs, err := pkg.ParseGatewayRelatedForClass(string(ngw.Spec.GatewayClassName), append(ngs, ogw)) if err != nil { return ctrl.Result{}, err } - pkg.ActiveSIGs.SetGateway(ngw) - // gateway is go away - if ncfgsO, err := pkg.ParseRelatedForClass(string(ogw.Spec.GatewayClassName), nil, ohrs, nil); err != nil { + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(ogw.Spec.GatewayClassName), ngs); err != nil { return ctrl.Result{}, err } else { pkg.PendingDeploys <- pkg.DeployRequest{ Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), From: &ocfgs, - To: &ncfgsO, + To: &ncfgs, StatusFunc: func() { // do something }, @@ -139,19 +140,26 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } } - if ncfgs, err := pkg.ParseRelatedForClass(string(ngw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ngw}, nil, nil); err != nil { + pkg.ActiveSIGs.SetGateway(ngw) + + ocfgs, err = pkg.ParseGatewayRelatedForClass(string(ngw.Spec.GatewayClassName), ngs) + if err != nil { return ctrl.Result{}, err - } else { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), - From: &ocfgsN, - To: &ncfgs, - StatusFunc: func() { - // do something - }, - Partition: string(ngw.Spec.GatewayClassName), - } } + ncfgs, err := pkg.ParseGatewayRelatedForClass(string(ogw.Spec.GatewayClassName), append(ngs, ngw)) + if err != nil { + return ctrl.Result{}, err + } + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), + From: &ocfgs, + To: &ncfgs, + StatusFunc: func() { + // do something + }, + Partition: string(ngw.Spec.GatewayClassName), + } + return ctrl.Result{}, nil } } diff --git a/controllers/gatewayclass_controller.go b/controllers/gatewayclass_controller.go index a5a6458..994975c 100644 --- a/controllers/gatewayclass_controller.go +++ b/controllers/gatewayclass_controller.go @@ -65,7 +65,7 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request zlog.V(1).Info("deleting gatewayclass " + req.Name) if gwc := pkg.ActiveSIGs.GetGatewayClass(req.Name); gwc != nil { gws := pkg.ActiveSIGs.AttachedGateways(gwc) - if ocfgs, err := pkg.ParseGatewayRelated(gws); err != nil { + if ocfgs, err := pkg.ParseGatewayRelatedForClass(gwc.Name, gws); err != nil { return ctrl.Result{}, err } else { pkg.PendingDeploys <- pkg.DeployRequest{ @@ -100,13 +100,13 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request var err error if ogwc != nil { gws := pkg.ActiveSIGs.AttachedGateways(ogwc) - if ocfgs, err = pkg.ParseGatewayRelated(gws); err != nil { + if ocfgs, err = pkg.ParseGatewayRelatedForClass(ogwc.Name, gws); err != nil { return ctrl.Result{}, err } } pkg.ActiveSIGs.SetGatewayClass(ngwc) gws := pkg.ActiveSIGs.AttachedGateways(ngwc) - if ncfgs, err = pkg.ParseGatewayRelated(gws); err != nil { + if ncfgs, err = pkg.ParseGatewayRelatedForClass(ngwc.Name, gws); err != nil { return ctrl.Result{}, err } // TODO: add logic more here. but don't want to compare configmap modifications and execute each time? diff --git a/controllers/httproute_controller.go b/controllers/httproute_controller.go index e2b4bfa..9675b5d 100644 --- a/controllers/httproute_controller.go +++ b/controllers/httproute_controller.go @@ -22,7 +22,6 @@ import ( "time" "gitee.com/zongzw/bigip-kubernetes-gateway/pkg" - v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -59,11 +58,10 @@ func (r *HttpRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if client.IgnoreNotFound(err) == nil { // delete resources hr := pkg.ActiveSIGs.GetHTTPRoute(req.NamespacedName.String()) - gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} - pkg.ActiveSIGs.GetRelatedObjs(nil, []*gatewayv1beta1.HTTPRoute{hr}, nil, &gwmap, &hrmap, &svcmap) + gws := pkg.ActiveSIGs.GatewayRefsOf(hr) drs := map[string]pkg.DeployRequest{} - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("deleting httproute '%s'", req.NamespacedName.String()), @@ -71,15 +69,15 @@ func (r *HttpRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, []*gatewayv1beta1.HTTPRoute{hr}, nil); err != nil { + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.From = &ocfgs } } - gws := pkg.ActiveSIGs.GatewayRefsOf(hr) + pkg.ActiveSIGs.UnsetHTTPRoute(req.NamespacedName.String()) - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ @@ -88,7 +86,7 @@ func (r *HttpRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), gws, nil, nil); err != nil { + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.To = &ncfgs @@ -113,11 +111,11 @@ func (r *HttpRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // upsert resources zlog.V(1).Info("upserting " + req.NamespacedName.String()) hr := pkg.ActiveSIGs.GetHTTPRoute(req.NamespacedName.String()) - gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} - pkg.ActiveSIGs.GetRelatedObjs(nil, []*gatewayv1beta1.HTTPRoute{hr}, nil, &gwmap, &hrmap, &svcmap) + + gws := pkg.ActiveSIGs.GatewayRefsOf(hr) drs := map[string]pkg.DeployRequest{} - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("upserting httproute '%s'", req.NamespacedName.String()), @@ -125,15 +123,15 @@ func (r *HttpRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, []*gatewayv1beta1.HTTPRoute{hr}, nil); err != nil { + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.From = &ocfgs } } - nhr := obj.DeepCopy() - pkg.ActiveSIGs.SetHTTPRoute(nhr) - for _, gw := range gwmap { + + pkg.ActiveSIGs.SetHTTPRoute(obj.DeepCopy()) + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("upserting httproute '%s'", req.NamespacedName.String()), @@ -141,7 +139,7 @@ func (r *HttpRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, []*gatewayv1beta1.HTTPRoute{nhr}, nil); err != nil { + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.To = &ncfgs diff --git a/controllers/v1_controller.go b/controllers/v1_controller.go index 1052d7a..3c8fcc1 100644 --- a/controllers/v1_controller.go +++ b/controllers/v1_controller.go @@ -28,8 +28,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" v1 "k8s.io/api/core/v1" - - gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) type EndpointsReconciler struct { @@ -56,11 +54,10 @@ func (r *EndpointsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} - pkg.ActiveSIGs.GetRelatedObjs(nil, nil, []*v1.Service{svc}, &gwmap, &hrmap, &svcmap) + gws := pkg.ActiveSIGs.GetRootGateways(svc) drs := map[string]pkg.DeployRequest{} - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("deleting endpoints '%s'", req.NamespacedName.String()), @@ -68,15 +65,14 @@ func (r *EndpointsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.From = &ocfgs } } - hrs := pkg.ActiveSIGs.HTTPRoutesRefsOf(svc) pkg.ActiveSIGs.UnsetEndpoints(req.NamespacedName.String()) - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("deleting endpoints '%s'", req.NamespacedName.String()), @@ -84,7 +80,7 @@ func (r *EndpointsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, hrs, nil); err != nil { + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.To = &ncfgs @@ -106,12 +102,11 @@ func (r *EndpointsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } else { svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} - pkg.ActiveSIGs.GetRelatedObjs(nil, nil, []*v1.Service{svc}, &gwmap, &hrmap, &svcmap) + gws := pkg.ActiveSIGs.GetRootGateways(svc) drs := map[string]pkg.DeployRequest{} - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("upserting endpoints '%s'", req.NamespacedName.String()), @@ -119,14 +114,14 @@ func (r *EndpointsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.From = &ocfgs } } pkg.ActiveSIGs.SetEndpoints(obj.DeepCopy()) - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("upserting endpoints '%s'", req.NamespacedName.String()), @@ -134,7 +129,7 @@ func (r *EndpointsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } } dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.To = &ncfgs @@ -162,12 +157,10 @@ func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - - gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} - pkg.ActiveSIGs.GetRelatedObjs(nil, nil, []*v1.Service{svc}, &gwmap, &hrmap, &svcmap) + gws := pkg.ActiveSIGs.GetRootGateways(svc) drs := map[string]pkg.DeployRequest{} - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("deleting service '%s'", req.NamespacedName.String()), @@ -175,15 +168,16 @@ func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } } dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.From = &ocfgs } } - hrs := pkg.ActiveSIGs.HTTPRoutesRefsOf(svc) + pkg.ActiveSIGs.UnsetService(req.NamespacedName.String()) - for _, gw := range gwmap { + + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("deleting service '%s'", req.NamespacedName.String()), @@ -191,7 +185,7 @@ func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } } dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, hrs, nil); err != nil { + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.To = &ncfgs @@ -214,12 +208,11 @@ func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } } else { svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} - pkg.ActiveSIGs.GetRelatedObjs(nil, nil, []*v1.Service{svc}, &gwmap, &hrmap, &svcmap) + gws := pkg.ActiveSIGs.GetRootGateways(svc) drs := map[string]pkg.DeployRequest{} - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("upserting service '%s'", req.NamespacedName.String()), @@ -227,14 +220,14 @@ func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } } dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.From = &ocfgs } } pkg.ActiveSIGs.SetService(obj.DeepCopy()) - for _, gw := range gwmap { + for _, gw := range gws { if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ Meta: fmt.Sprintf("upserting service '%s'", req.NamespacedName.String()), @@ -242,7 +235,7 @@ func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } } dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseRelatedForClass(string(gw.Spec.GatewayClassName), nil, nil, []*v1.Service{svc}); err != nil { + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { return ctrl.Result{}, err } else { dr.To = &ncfgs diff --git a/pkg/parser.go b/pkg/parser.go index 0947d57..5816bb3 100644 --- a/pkg/parser.go +++ b/pkg/parser.go @@ -6,11 +6,84 @@ import ( "gitee.com/zongzw/bigip-kubernetes-gateway/k8s" "gitee.com/zongzw/f5-bigip-rest/utils" - v1 "k8s.io/api/core/v1" gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -func ParseHTTPRoute(className string, hr *gatewayv1beta1.HTTPRoute) (map[string]interface{}, error) { +// func ParseRelated(gwObjs []*gatewayv1beta1.Gateway, hrObjs []*gatewayv1beta1.HTTPRoute, svcObjs []*v1.Service) (map[string]interface{}, error) { +// defer utils.TimeItToPrometheus()() + +// gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} +// ActiveSIGs.GetRelatedObjs(gwObjs, hrObjs, svcObjs, &gwmap, &hrmap, &svcmap) + +// rlt := map[string]interface{}{} +// for _, gw := range gwmap { +// if cfgs, err := ParseGateway(gw); err != nil { +// return map[string]interface{}{}, err +// } else { +// for k, v := range cfgs { +// rlt[k] = v +// } +// } +// } +// for _, hr := range hrmap { +// if cfgs, err := ParseHTTPRoute(hr); err != nil { +// return map[string]interface{}{}, err +// } else { +// for k, v := range cfgs { +// rlt[k] = v +// } +// } +// } + +// return map[string]interface{}{ +// "": rlt, +// }, nil +// } + +func ParseGatewayRelatedForClass(className string, gwObjs []*gatewayv1beta1.Gateway) (map[string]interface{}, error) { + defer utils.TimeItToPrometheus()() + + if ActiveSIGs.GetGatewayClass(className) == nil { + return map[string]interface{}{}, nil + } + // gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} + // ActiveSIGs.GetRelatedObjs(gwObjs, hrObjs, svcObjs, &gwmap, &hrmap, &svcmap) + + cgwObjs := []*gatewayv1beta1.Gateway{} + for _, gw := range gwObjs { + if gw.Spec.GatewayClassName == gatewayv1beta1.ObjectName(className) { + cgwObjs = append(cgwObjs, gw) + } + } + + // return ParseGatewayRelated(cgwObjs) + + rlt := map[string]interface{}{} + for _, gw := range cgwObjs { + if cfgs, err := parseGateway(gw); err != nil { + return map[string]interface{}{}, err + } else { + for k, v := range cfgs { + rlt[k] = v + } + } + hrs := ActiveSIGs.AttachedHTTPRoutes(gw) + for _, hr := range hrs { + if cfgs, err := parseHTTPRoute(className, hr); err != nil { + return map[string]interface{}{}, err + } else { + for k, v := range cfgs { + rlt[k] = v + } + } + } + } + return map[string]interface{}{ + "": rlt, + }, nil +} + +func parseHTTPRoute(className string, hr *gatewayv1beta1.HTTPRoute) (map[string]interface{}, error) { defer utils.TimeItToPrometheus()() if hr == nil { @@ -29,7 +102,7 @@ func ParseHTTPRoute(className string, hr *gatewayv1beta1.HTTPRoute) (map[string] return rlt, nil } -func ParseGateway(gw *gatewayv1beta1.Gateway) (map[string]interface{}, error) { +func parseGateway(gw *gatewayv1beta1.Gateway) (map[string]interface{}, error) { defer utils.TimeItToPrometheus()() if gw == nil { @@ -104,11 +177,11 @@ func ParseGateway(gw *gatewayv1beta1.Gateway) (map[string]interface{}, error) { return rlt, nil } -// func ParseRelated(gwObjs []*gatewayv1beta1.Gateway, hrObjs []*gatewayv1beta1.HTTPRoute, svcObjs []*v1.Service) (map[string]interface{}, error) { +// func ParseGatewayRelated(gwObjs []*gatewayv1beta1.Gateway) (map[string]interface{}, error) { // defer utils.TimeItToPrometheus()() // gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} -// ActiveSIGs.GetRelatedObjs(gwObjs, hrObjs, svcObjs, &gwmap, &hrmap, &svcmap) +// ActiveSIGs.GetGatewayRelated(gwObjs, &gwmap, &hrmap, &svcmap) // rlt := map[string]interface{}{} // for _, gw := range gwmap { @@ -120,8 +193,16 @@ func ParseGateway(gw *gatewayv1beta1.Gateway) (map[string]interface{}, error) { // } // } // } + +// // TODO: tune it. +// className := "" +// if len(gwObjs) == 0 { +// return map[string]interface{}{}, fmt.Errorf("should not happen here") +// } else { +// className = string(gwObjs[0].Spec.GatewayClassName) +// } // for _, hr := range hrmap { -// if cfgs, err := ParseHTTPRoute(hr); err != nil { +// if cfgs, err := ParseHTTPRoute(className, hr); err != nil { // return map[string]interface{}{}, err // } else { // for k, v := range cfgs { @@ -135,64 +216,6 @@ func ParseGateway(gw *gatewayv1beta1.Gateway) (map[string]interface{}, error) { // }, nil // } -func ParseRelatedForClass(className string, gwObjs []*gatewayv1beta1.Gateway, hrObjs []*gatewayv1beta1.HTTPRoute, svcObjs []*v1.Service) (map[string]interface{}, error) { - defer utils.TimeItToPrometheus()() - - if ActiveSIGs.GetGatewayClass(className) == nil { - return map[string]interface{}{}, nil - } - gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} - ActiveSIGs.GetRelatedObjs(gwObjs, hrObjs, svcObjs, &gwmap, &hrmap, &svcmap) - - cgwObjs := []*gatewayv1beta1.Gateway{} - for _, gw := range gwmap { - if gw.Spec.GatewayClassName == gatewayv1beta1.ObjectName(className) { - cgwObjs = append(cgwObjs, gw) - } - } - - return ParseGatewayRelated(cgwObjs) -} - -func ParseGatewayRelated(gwObjs []*gatewayv1beta1.Gateway) (map[string]interface{}, error) { - defer utils.TimeItToPrometheus()() - - gwmap, hrmap, svcmap := map[string]*gatewayv1beta1.Gateway{}, map[string]*gatewayv1beta1.HTTPRoute{}, map[string]*v1.Service{} - ActiveSIGs.GetGatewayRelated(gwObjs, &gwmap, &hrmap, &svcmap) - - rlt := map[string]interface{}{} - for _, gw := range gwmap { - if cfgs, err := ParseGateway(gw); err != nil { - return map[string]interface{}{}, err - } else { - for k, v := range cfgs { - rlt[k] = v - } - } - } - - // TODO: tune it. - className := "" - if len(gwObjs) == 0 { - return map[string]interface{}{}, fmt.Errorf("should not happen here") - } else { - className = string(gwObjs[0].Spec.GatewayClassName) - } - for _, hr := range hrmap { - if cfgs, err := ParseHTTPRoute(className, hr); err != nil { - return map[string]interface{}{}, err - } else { - for k, v := range cfgs { - rlt[k] = v - } - } - } - - return map[string]interface{}{ - "": rlt, - }, nil -} - func parsePoolsFrom(hr *gatewayv1beta1.HTTPRoute, rlt map[string]interface{}) error { creatPool := func(ns, n string, rlt map[string]interface{}) error { diff --git a/pkg/utils.go b/pkg/utils.go index 43f69be..e3c06f0 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -251,16 +251,16 @@ func (c *SIGCache) _attachedHTTPRoutes(gw *gatewayv1beta1.Gateway) []*gatewayv1b return hrs } -func (c *SIGCache) ServiceRefsOf(hr *gatewayv1beta1.HTTPRoute) []*v1.Service { +func (c *SIGCache) AttachedServices(hr *gatewayv1beta1.HTTPRoute) []*v1.Service { defer utils.TimeItToPrometheus()() c.mutex.RLock() defer c.mutex.RUnlock() - return c._serviceRefsOf(hr) + return c._attachedServices(hr) } -func (c *SIGCache) _serviceRefsOf(hr *gatewayv1beta1.HTTPRoute) []*v1.Service { +func (c *SIGCache) _attachedServices(hr *gatewayv1beta1.HTTPRoute) []*v1.Service { if hr == nil { return []*v1.Service{} } @@ -349,122 +349,171 @@ func (c *SIGCache) _HTTPRoutesRefsOf(svc *v1.Service) []*gatewayv1beta1.HTTPRout return hrs } -func (c *SIGCache) GetRelatedObjs( - gwObjs []*gatewayv1beta1.Gateway, - hrObjs []*gatewayv1beta1.HTTPRoute, - svcObjs []*v1.Service, - gwmap *map[string]*gatewayv1beta1.Gateway, - hrmap *map[string]*gatewayv1beta1.HTTPRoute, - svcmap *map[string]*v1.Service) { +// func (c *SIGCache) GetRelatedObjs( +// gwObjs []*gatewayv1beta1.Gateway, +// hrObjs []*gatewayv1beta1.HTTPRoute, +// svcObjs []*v1.Service, +// gwmap *map[string]*gatewayv1beta1.Gateway, +// hrmap *map[string]*gatewayv1beta1.HTTPRoute, +// svcmap *map[string]*v1.Service) { +// defer utils.TimeItToPrometheus()() + +// c.mutex.RLock() +// defer c.mutex.RUnlock() + +// c._getRelatedObjs(gwObjs, hrObjs, svcObjs, gwmap, hrmap, svcmap) +// } + +// func (c *SIGCache) _getRelatedObjs( +// gwObjs []*gatewayv1beta1.Gateway, +// hrObjs []*gatewayv1beta1.HTTPRoute, +// svcObjs []*v1.Service, +// gwmap *map[string]*gatewayv1beta1.Gateway, +// hrmap *map[string]*gatewayv1beta1.HTTPRoute, +// svcmap *map[string]*v1.Service) { +// for _, gwObj := range gwObjs { +// if gwObj != nil { +// name := utils.Keyname(gwObj.Namespace, gwObj.Name) +// (*gwmap)[name] = c.Gateway[name] +// } +// hrs := c._attachedHTTPRoutes(gwObj) +// for _, hr := range hrs { +// name := utils.Keyname(hr.Namespace, hr.Name) +// if _, ok := (*hrmap)[name]; !ok { +// (*hrmap)[name] = c.HTTPRoute[name] +// c._getRelatedObjs([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{hr}, []*v1.Service{}, gwmap, hrmap, svcmap) +// } +// } +// } + +// for _, hrObj := range hrObjs { +// if hrObj != nil { +// name := utils.Keyname(hrObj.Namespace, hrObj.Name) +// (*hrmap)[name] = c.HTTPRoute[name] +// } +// gws := c._gatewayRefsOf(hrObj) +// for _, gw := range gws { +// name := utils.Keyname(gw.Namespace, gw.Name) +// if _, ok := (*gwmap)[name]; !ok { +// (*gwmap)[name] = c.Gateway[name] +// c._getRelatedObjs([]*gatewayv1beta1.Gateway{gw}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}, gwmap, hrmap, svcmap) +// } +// } +// svcs := c._serviceRefsOf(hrObj) +// for _, svc := range svcs { +// name := utils.Keyname(svc.Namespace, svc.Name) +// if _, ok := (*svcmap)[name]; !ok { +// (*svcmap)[name] = c.Service[name] +// c._getRelatedObjs([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{svc}, gwmap, hrmap, svcmap) +// } +// } +// } + +// for _, svcObj := range svcObjs { +// if svcObj != nil { +// name := utils.Keyname(svcObj.Namespace, svcObj.Name) +// (*svcmap)[name] = c.Service[name] +// } +// hrs := c._HTTPRoutesRefsOf(svcObj) +// for _, hr := range hrs { +// name := utils.Keyname(hr.Namespace, hr.Name) +// if _, ok := (*hrmap)[name]; !ok { +// (*hrmap)[name] = c.HTTPRoute[name] +// c._getRelatedObjs([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{hr}, []*v1.Service{}, gwmap, hrmap, svcmap) +// } +// } +// } +// } + +// func (c *SIGCache) GetGatewayRelated( +// gwObjs []*gatewayv1beta1.Gateway, +// gwmap *map[string]*gatewayv1beta1.Gateway, +// hrmap *map[string]*gatewayv1beta1.HTTPRoute, +// svcmap *map[string]*v1.Service) { + +// defer utils.TimeItToPrometheus()() + +// c.mutex.RLock() +// defer c.mutex.RUnlock() + +// c._getGatewayRelated(gwObjs, gwmap, hrmap, svcmap) +// } + +// func (c *SIGCache) _getGatewayRelated( +// gwObjs []*gatewayv1beta1.Gateway, +// gwmap *map[string]*gatewayv1beta1.Gateway, +// hrmap *map[string]*gatewayv1beta1.HTTPRoute, +// svcmap *map[string]*v1.Service) { +// for _, gwObj := range gwObjs { +// if gwObj != nil { +// name := utils.Keyname(gwObj.Namespace, gwObj.Name) +// (*gwmap)[name] = c.Gateway[name] +// } +// hrs := c._attachedHTTPRoutes(gwObj) +// for _, hr := range hrs { +// name := utils.Keyname(hr.Namespace, hr.Name) +// if _, ok := (*hrmap)[name]; !ok { +// (*hrmap)[name] = hr +// svcs := c._serviceRefsOf(hr) +// for _, svc := range svcs { +// name := utils.Keyname(svc.Namespace, svc.Name) +// if _, ok := (*svcmap)[name]; !ok { +// (*svcmap)[name] = svc +// } +// } +// } +// } +// } +// } + +func (c *SIGCache) GetNeighborGateways(gw *gatewayv1beta1.Gateway) []*gatewayv1beta1.Gateway { defer utils.TimeItToPrometheus()() c.mutex.RLock() defer c.mutex.RUnlock() - c._getRelatedObjs(gwObjs, hrObjs, svcObjs, gwmap, hrmap, svcmap) -} - -func (c *SIGCache) _getRelatedObjs( - gwObjs []*gatewayv1beta1.Gateway, - hrObjs []*gatewayv1beta1.HTTPRoute, - svcObjs []*v1.Service, - gwmap *map[string]*gatewayv1beta1.Gateway, - hrmap *map[string]*gatewayv1beta1.HTTPRoute, - svcmap *map[string]*v1.Service) { - for _, gwObj := range gwObjs { - if gwObj != nil { - name := utils.Keyname(gwObj.Namespace, gwObj.Name) - (*gwmap)[name] = c.Gateway[name] - } - hrs := c._attachedHTTPRoutes(gwObj) - for _, hr := range hrs { - name := utils.Keyname(hr.Namespace, hr.Name) - if _, ok := (*hrmap)[name]; !ok { - (*hrmap)[name] = c.HTTPRoute[name] - c._getRelatedObjs([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{hr}, []*v1.Service{}, gwmap, hrmap, svcmap) + gwmap := map[string]*gatewayv1beta1.Gateway{} + hrs := c._attachedHTTPRoutes(gw) + for _, hr := range hrs { + gws := c._gatewayRefsOf(hr) + for _, ng := range gws { + kn := utils.Keyname(ng.Namespace, ng.Name) + if _, f := gwmap[kn]; !f { + gwmap[kn] = ng } } } - for _, hrObj := range hrObjs { - if hrObj != nil { - name := utils.Keyname(hrObj.Namespace, hrObj.Name) - (*hrmap)[name] = c.HTTPRoute[name] - } - gws := c._gatewayRefsOf(hrObj) - for _, gw := range gws { - name := utils.Keyname(gw.Namespace, gw.Name) - if _, ok := (*gwmap)[name]; !ok { - (*gwmap)[name] = c.Gateway[name] - c._getRelatedObjs([]*gatewayv1beta1.Gateway{gw}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{}, gwmap, hrmap, svcmap) - } - } - svcs := c._serviceRefsOf(hrObj) - for _, svc := range svcs { - name := utils.Keyname(svc.Namespace, svc.Name) - if _, ok := (*svcmap)[name]; !ok { - (*svcmap)[name] = c.Service[name] - c._getRelatedObjs([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{}, []*v1.Service{svc}, gwmap, hrmap, svcmap) - } - } + delete(gwmap, utils.Keyname(gw.Namespace, gw.Name)) + rlt := []*gatewayv1beta1.Gateway{} + for _, gw := range gwmap { + rlt = append(rlt, gw) } - for _, svcObj := range svcObjs { - if svcObj != nil { - name := utils.Keyname(svcObj.Namespace, svcObj.Name) - (*svcmap)[name] = c.Service[name] - } - hrs := c._HTTPRoutesRefsOf(svcObj) - for _, hr := range hrs { - name := utils.Keyname(hr.Namespace, hr.Name) - if _, ok := (*hrmap)[name]; !ok { - (*hrmap)[name] = c.HTTPRoute[name] - c._getRelatedObjs([]*gatewayv1beta1.Gateway{}, []*gatewayv1beta1.HTTPRoute{hr}, []*v1.Service{}, gwmap, hrmap, svcmap) - } - } - } + return rlt } -func (c *SIGCache) GetGatewayRelated( - gwObjs []*gatewayv1beta1.Gateway, - gwmap *map[string]*gatewayv1beta1.Gateway, - hrmap *map[string]*gatewayv1beta1.HTTPRoute, - svcmap *map[string]*v1.Service) { - +func (c *SIGCache) GetRootGateways(svc *v1.Service) []*gatewayv1beta1.Gateway { defer utils.TimeItToPrometheus()() c.mutex.RLock() defer c.mutex.RUnlock() - c._getGatewayRelated(gwObjs, gwmap, hrmap, svcmap) -} + gwmap := map[string]*gatewayv1beta1.Gateway{} -func (c *SIGCache) _getGatewayRelated( - gwObjs []*gatewayv1beta1.Gateway, - gwmap *map[string]*gatewayv1beta1.Gateway, - hrmap *map[string]*gatewayv1beta1.HTTPRoute, - svcmap *map[string]*v1.Service) { - for _, gwObj := range gwObjs { - if gwObj != nil { - name := utils.Keyname(gwObj.Namespace, gwObj.Name) - (*gwmap)[name] = c.Gateway[name] - } - hrs := c._attachedHTTPRoutes(gwObj) - for _, hr := range hrs { - name := utils.Keyname(hr.Namespace, hr.Name) - if _, ok := (*hrmap)[name]; !ok { - (*hrmap)[name] = hr - svcs := c._serviceRefsOf(hr) - for _, svc := range svcs { - name := utils.Keyname(svc.Namespace, svc.Name) - if _, ok := (*svcmap)[name]; !ok { - (*svcmap)[name] = svc - } - } - } + hrs := c._HTTPRoutesRefsOf(svc) + for _, hr := range hrs { + gws := c._gatewayRefsOf(hr) + for _, gw := range gws { + gwmap[utils.Keyname(gw.Namespace, gw.Name)] = gw } } + rlt := []*gatewayv1beta1.Gateway{} + for _, gw := range gwmap { + rlt = append(rlt, gw) + } + return rlt } func (c *SIGCache) syncCoreV1Resources(kubeClient kubernetes.Interface) error { From add5bce4bad0c2788ee7b7d83029f01df784a137 Mon Sep 17 00:00:00 2001 From: zongzw Date: Sun, 27 Nov 2022 15:30:44 +0800 Subject: [PATCH 4/4] make service works. --- controllers/gateway_controller.go | 286 ++++++++++------ controllers/gatewayclass_controller.go | 160 ++++++--- controllers/httproute_controller.go | 264 +++++++++------ controllers/v1_controller.go | 430 ++++++++++++++----------- pkg/deployer.go | 62 ++++ pkg/parser.go | 175 ++++++---- pkg/utils.go | 61 +++- tests/test.py | 2 +- 8 files changed, 944 insertions(+), 496 deletions(-) diff --git a/controllers/gateway_controller.go b/controllers/gateway_controller.go index 9ed7b44..7b2a210 100644 --- a/controllers/gateway_controller.go +++ b/controllers/gateway_controller.go @@ -29,6 +29,7 @@ import ( gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" "gitee.com/zongzw/bigip-kubernetes-gateway/pkg" + "gitee.com/zongzw/f5-bigip-rest/utils" ) type GatewayReconciler struct { @@ -58,111 +59,15 @@ func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { // delete resources - gw := pkg.ActiveSIGs.GetGateway(req.NamespacedName.String()) - // Only when we know all the gateways can we know exactly which routes need to be cleared because of this gateway event. - gws := pkg.ActiveSIGs.GetNeighborGateways(gw) - if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), append(gws, gw)); err != nil { - return ctrl.Result{}, err - } else { - zlog.V(1).Info("handling + deleting " + req.NamespacedName.String()) - pkg.ActiveSIGs.UnsetGateway(req.NamespacedName.String()) - if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("deleting gateway '%s'", req.NamespacedName.String()), - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() { - // do something - }, - Partition: string(gw.Spec.GatewayClassName), - } - } - } - - return ctrl.Result{}, nil + defer pkg.ActiveSIGs.UnsetGateway(req.NamespacedName.String()) + return handleDeletingGateway(ctx, req) } else { return ctrl.Result{}, err } } else { // upsert resources - zlog.V(1).Info("handling + upserting " + req.NamespacedName.String()) - ogw := pkg.ActiveSIGs.GetGateway(req.NamespacedName.String()) - if ogw == nil { - ogw = &obj - pkg.ActiveSIGs.SetGateway(obj.DeepCopy()) - } - - if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(ogw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ogw}); err != nil { - zlog.Error(err, "handling + upserting + parse related ocfgs "+req.NamespacedName.String()) - return ctrl.Result{}, err - } else { - ngw := obj.DeepCopy() - if ngw.Spec.GatewayClassName == ogw.Spec.GatewayClassName { - pkg.ActiveSIGs.SetGateway(ngw) - if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(ngw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ngw}); err != nil { - zlog.Error(err, "handling + upserting + parse related ncfgs "+req.NamespacedName.String()) - return ctrl.Result{}, err - } else { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() { - // do something - }, - Partition: string(ngw.Spec.GatewayClassName), - } - return ctrl.Result{}, nil - } - } else { - // original state of new gatewayclass env - // gateway is go away - ngs := pkg.ActiveSIGs.GetNeighborGateways(ogw) - - ocfgs, err := pkg.ParseGatewayRelatedForClass(string(ngw.Spec.GatewayClassName), append(ngs, ogw)) - if err != nil { - return ctrl.Result{}, err - } - - if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(ogw.Spec.GatewayClassName), ngs); err != nil { - return ctrl.Result{}, err - } else { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() { - // do something - }, - Partition: string(ogw.Spec.GatewayClassName), - } - } - - pkg.ActiveSIGs.SetGateway(ngw) - - ocfgs, err = pkg.ParseGatewayRelatedForClass(string(ngw.Spec.GatewayClassName), ngs) - if err != nil { - return ctrl.Result{}, err - } - ncfgs, err := pkg.ParseGatewayRelatedForClass(string(ogw.Spec.GatewayClassName), append(ngs, ngw)) - if err != nil { - return ctrl.Result{}, err - } - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting gateway '%s'", req.NamespacedName.String()), - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() { - // do something - }, - Partition: string(ngw.Spec.GatewayClassName), - } - - return ctrl.Result{}, nil - } - } + defer pkg.ActiveSIGs.SetGateway(&obj) + return handleUpsertingGateway(ctx, &obj) } } @@ -172,3 +77,184 @@ func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&gatewayv1beta1.Gateway{}). Complete(r) } + +func handleDeletingGateway(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + zlog := log.FromContext(ctx) + + gw := pkg.ActiveSIGs.GetGateway(req.NamespacedName.String()) + // Only when we know all the gateways can we know exactly which routes need to be cleared because of this gateway event. + gws := pkg.ActiveSIGs.GetNeighborGateways(gw) + + ocfgs, ncfgs := map[string]interface{}{}, map[string]interface{}{} + opcfgs, npcfgs := map[string]interface{}{}, map[string]interface{}{} + var err error + + if ocfgs, err = pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), append(gws, gw)); err != nil { + return ctrl.Result{}, err + } + if opcfgs, err = pkg.ParseServicesRelatedForAll(); err != nil { + return ctrl.Result{}, err + } + + zlog.V(1).Info("handling + deleting " + req.NamespacedName.String()) + pkg.ActiveSIGs.UnsetGateway(req.NamespacedName.String()) + + if ncfgs, err = pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { + return ctrl.Result{}, err + } + if npcfgs, err = pkg.ParseServicesRelatedForAll(); err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting gateway '%s'", req.NamespacedName.String()), + From: &ocfgs, + To: &ncfgs, + StatusFunc: func() { + // do something + }, + Partition: string(gw.Spec.GatewayClassName), + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("updating services for event '%s'", req.NamespacedName.String()), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + // do something + }, + Partition: "cis-c-tenant", + } + return ctrl.Result{}, nil +} + +func handleUpsertingGateway(ctx context.Context, obj *gatewayv1beta1.Gateway) (ctrl.Result, error) { + zlog := log.FromContext(ctx) + + reqnsn := utils.Keyname(obj.Namespace, obj.Name) + zlog.V(1).Info("handling + upserting " + reqnsn) + + ogw := pkg.ActiveSIGs.GetGateway(reqnsn) + if ogw == nil { + ogw = obj + pkg.ActiveSIGs.SetGateway(obj.DeepCopy()) + } + + var err error + + ngw := obj.DeepCopy() + if ngw.Spec.GatewayClassName == ogw.Spec.GatewayClassName { + + ocfgs, ncfgs := map[string]interface{}{}, map[string]interface{}{} + opcfgs, npcfgs := map[string]interface{}{}, map[string]interface{}{} + ocfgs, err = pkg.ParseGatewayRelatedForClass(string(ogw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ogw}) + if err != nil { + zlog.Error(err, "handling + upserting + parse related ocfgs "+reqnsn) + return ctrl.Result{}, err + } + opcfgs, err = pkg.ParseServicesRelatedForAll() + if err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.SetGateway(ngw) + + ncfgs, err = pkg.ParseGatewayRelatedForClass(string(ngw.Spec.GatewayClassName), []*gatewayv1beta1.Gateway{ngw}) + if err != nil { + zlog.Error(err, "handling + upserting + parse related ncfgs "+reqnsn) + return ctrl.Result{}, err + } + npcfgs, err = pkg.ParseServicesRelatedForAll() + if err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting services for gateway '%s'", reqnsn), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + // do something + }, + Partition: "cis-c-tenant", + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting gateway '%s'", reqnsn), + From: &ocfgs, + To: &ncfgs, + StatusFunc: func() { + // do something + }, + Partition: string(ngw.Spec.GatewayClassName), + } + return ctrl.Result{}, nil + + } else { + ocfgs1, ncfgs1 := map[string]interface{}{}, map[string]interface{}{} // for original class + ocfgs2, ncfgs2 := map[string]interface{}{}, map[string]interface{}{} // for target class + opcfgs, npcfgs := map[string]interface{}{}, map[string]interface{}{} + + // gateway is go away + ngs := pkg.ActiveSIGs.GetNeighborGateways(ogw) + + if opcfgs, err = pkg.ParseServicesRelatedForAll(); err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.SetGateway(ngw) + + if npcfgs, err = pkg.ParseServicesRelatedForAll(); err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting services for gateway '%s'", reqnsn), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + // do something + }, + Partition: "cis-c-tenant", + } + + ocfgs1, err = pkg.ParseGatewayRelatedForClass(string(ogw.Spec.GatewayClassName), append(ngs, ogw)) + if err != nil { + return ctrl.Result{}, err + } + if ncfgs1, err = pkg.ParseGatewayRelatedForClass(string(ogw.Spec.GatewayClassName), ngs); err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting gateway '%s'", reqnsn), + From: &ocfgs1, + To: &ncfgs1, + StatusFunc: func() { + // do something + }, + Partition: string(ogw.Spec.GatewayClassName), + } + + ocfgs2, err = pkg.ParseGatewayRelatedForClass(string(ngw.Spec.GatewayClassName), ngs) + if err != nil { + return ctrl.Result{}, err + } + ncfgs2, err := pkg.ParseGatewayRelatedForClass(string(ngw.Spec.GatewayClassName), append(ngs, ngw)) + if err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting gateway '%s'", reqnsn), + From: &ocfgs2, + To: &ncfgs2, + StatusFunc: func() { + // do something + }, + Partition: string(ngw.Spec.GatewayClassName), + } + + return ctrl.Result{}, nil + } +} diff --git a/controllers/gatewayclass_controller.go b/controllers/gatewayclass_controller.go index 994975c..7ec46d3 100644 --- a/controllers/gatewayclass_controller.go +++ b/controllers/gatewayclass_controller.go @@ -62,53 +62,13 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request zlog.V(1).Info("handling gatewayclass " + req.Name) if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { - zlog.V(1).Info("deleting gatewayclass " + req.Name) - if gwc := pkg.ActiveSIGs.GetGatewayClass(req.Name); gwc != nil { - gws := pkg.ActiveSIGs.AttachedGateways(gwc) - if ocfgs, err := pkg.ParseGatewayRelatedForClass(gwc.Name, gws); err != nil { - return ctrl.Result{}, err - } else { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("clearing gateways for gatewayclass '%s'", req.Name), - From: &ocfgs, - To: nil, - StatusFunc: func() { - pkg.ActiveSIGs.UnsetGatewayClass(req.Name) - }, - Partition: req.Name, - } - } - } - return ctrl.Result{}, nil + defer pkg.ActiveSIGs.UnsetGatewayClass(req.Name) + return handleDeletingGatewayClass(ctx, req) } else { return ctrl.Result{}, err } } else { - // upsert gatewayclass - zlog.V(1).Info("upserting gatewayclass " + req.Name) ngwc := obj.DeepCopy() - - if ngwc.Spec.ControllerName != gatewayv1beta1.GatewayController(pkg.ActiveSIGs.ControllerName) { - zlog.V(1).Info("ignore this gwc " + req.Name + " as its controllerName does not match " + pkg.ActiveSIGs.ControllerName) - return ctrl.Result{}, nil - } - - ogwc := pkg.ActiveSIGs.GetGatewayClass(req.Name) - - ocfgs := map[string]interface{}{} - ncfgs := map[string]interface{}{} - var err error - if ogwc != nil { - gws := pkg.ActiveSIGs.AttachedGateways(ogwc) - if ocfgs, err = pkg.ParseGatewayRelatedForClass(ogwc.Name, gws); err != nil { - return ctrl.Result{}, err - } - } - pkg.ActiveSIGs.SetGatewayClass(ngwc) - gws := pkg.ActiveSIGs.AttachedGateways(ngwc) - if ncfgs, err = pkg.ParseGatewayRelatedForClass(ngwc.Name, gws); err != nil { - return ctrl.Result{}, err - } // TODO: add logic more here. but don't want to compare configmap modifications and execute each time? // create partiton in the bigip here since we consider gwc name to be partiton name if ngwc.Spec.ParametersRef != nil { @@ -155,15 +115,11 @@ func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request } else { zlog.V(1).Info("status updated") } - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: fmt.Sprintf("refreshing gateways for gatewayclass '%s'", req.Name), - From: &ocfgs, - To: &ncfgs, - StatusFunc: func() {}, - Partition: req.Name, - } + + // upsert gatewayclass + defer pkg.ActiveSIGs.SetGatewayClass(&obj) + return handleUpsertingGatewayClass(ctx, &obj) } - return ctrl.Result{}, nil } // SetupWithManager sets up the controller with the Manager. @@ -172,3 +128,107 @@ func (r *GatewayClassReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&gatewayv1beta1.GatewayClass{}). Complete(r) } + +func handleDeletingGatewayClass(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + zlog := log.FromContext(ctx) + zlog.V(1).Info("deleting gatewayclass " + req.Name) + + gwc := pkg.ActiveSIGs.GetGatewayClass(req.Name) + if gwc == nil { + return ctrl.Result{}, nil + } + ocfgs := map[string]interface{}{} + // ocfgs, ncfgs := map[string]interface{}{}, map[string]interface{}{} + opcfgs, npcfgs := map[string]interface{}{}, map[string]interface{}{} + var err error + + gws := pkg.ActiveSIGs.AttachedGateways(gwc) + if ocfgs, err = pkg.ParseGatewayRelatedForClass(gwc.Name, gws); err != nil { + return ctrl.Result{}, err + } + if opcfgs, err = pkg.ParseServicesRelatedForAll(); err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.UnsetGatewayClass(req.Name) + + if npcfgs, err = pkg.ParseServicesRelatedForAll(); err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("clearing gateways for gatewayclass '%s'", req.Name), + From: &ocfgs, + To: nil, + StatusFunc: func() { + }, + Partition: req.Name, + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("updating services for gatewayclass '%s'", req.Name), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + }, + Partition: "cis-c-tenant", + } + + return ctrl.Result{}, nil +} + +func handleUpsertingGatewayClass(ctx context.Context, obj *gatewayv1beta1.GatewayClass) (ctrl.Result, error) { + zlog := log.FromContext(ctx) + + reqn := utils.Keyname(obj.Namespace, obj.Name) + zlog.V(1).Info("upserting gatewayclass " + reqn) + ngwc := obj.DeepCopy() + + if ngwc.Spec.ControllerName != gatewayv1beta1.GatewayController(pkg.ActiveSIGs.ControllerName) { + zlog.V(1).Info("ignore this gwc " + reqn + " as its controllerName does not match " + pkg.ActiveSIGs.ControllerName) + return ctrl.Result{}, nil + } + + ocfgs, ncfgs := map[string]interface{}{}, map[string]interface{}{} + opcfgs, npcfgs := map[string]interface{}{}, map[string]interface{}{} + var err error + + if ogwc := pkg.ActiveSIGs.GetGatewayClass(reqn); ogwc != nil { + gws := pkg.ActiveSIGs.AttachedGateways(ogwc) + if ocfgs, err = pkg.ParseGatewayRelatedForClass(ogwc.Name, gws); err != nil { + return ctrl.Result{}, err + } + } + if opcfgs, err = pkg.ParseServicesRelatedForAll(); err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.SetGatewayClass(ngwc) + + if npcfgs, err = pkg.ParseServicesRelatedForAll(); err != nil { + return ctrl.Result{}, err + } + + gws := pkg.ActiveSIGs.AttachedGateways(ngwc) + if ncfgs, err = pkg.ParseGatewayRelatedForClass(ngwc.Name, gws); err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("refreshing services for gatewayclass '%s'", reqn), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() {}, + Partition: "cis-c-tenant", + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("refreshing gateways for gatewayclass '%s'", reqn), + From: &ocfgs, + To: &ncfgs, + StatusFunc: func() {}, + Partition: reqn, + } + + return ctrl.Result{}, nil +} diff --git a/controllers/httproute_controller.go b/controllers/httproute_controller.go index 9675b5d..7ae7304 100644 --- a/controllers/httproute_controller.go +++ b/controllers/httproute_controller.go @@ -22,6 +22,7 @@ import ( "time" "gitee.com/zongzw/bigip-kubernetes-gateway/pkg" + "gitee.com/zongzw/f5-bigip-rest/utils" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -57,106 +58,15 @@ func (r *HttpRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { // delete resources - hr := pkg.ActiveSIGs.GetHTTPRoute(req.NamespacedName.String()) - - gws := pkg.ActiveSIGs.GatewayRefsOf(hr) - drs := map[string]pkg.DeployRequest{} - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("deleting httproute '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.From = &ocfgs - } - } - - pkg.ActiveSIGs.UnsetHTTPRoute(req.NamespacedName.String()) - for _, gw := range gws { - - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("deleting httproute '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.To = &ncfgs - } - } - for _, dr := range drs { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: dr.Meta, - From: dr.From, - To: dr.To, - StatusFunc: func() { - }, - Partition: dr.Partition, - } - } - - return ctrl.Result{}, nil + defer pkg.ActiveSIGs.UnsetHTTPRoute(req.NamespacedName.String()) + return handleDeletingHTTPRoute(ctx, req) } else { return ctrl.Result{}, err } } else { // upsert resources - zlog.V(1).Info("upserting " + req.NamespacedName.String()) - hr := pkg.ActiveSIGs.GetHTTPRoute(req.NamespacedName.String()) - - gws := pkg.ActiveSIGs.GatewayRefsOf(hr) - drs := map[string]pkg.DeployRequest{} - - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting httproute '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.From = &ocfgs - } - } - - pkg.ActiveSIGs.SetHTTPRoute(obj.DeepCopy()) - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting httproute '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.To = &ncfgs - } - } - - for _, dr := range drs { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: dr.Meta, - From: dr.From, - To: dr.To, - StatusFunc: func() { - }, - Partition: dr.Partition, - } - } - return ctrl.Result{}, nil + defer pkg.ActiveSIGs.SetHTTPRoute(&obj) + return handleUpsertingHTTPRoute(ctx, &obj) } } @@ -171,3 +81,167 @@ func (r *HttpRouteReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&gatewayv1beta1.HTTPRoute{}). Complete(r) } + +func handleDeletingHTTPRoute(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + // zlog := log.FromContext(ctx) + hr := pkg.ActiveSIGs.GetHTTPRoute(req.NamespacedName.String()) + gws := pkg.ActiveSIGs.GatewayRefsOf(hr) + drs := map[string]*pkg.DeployRequest{} + for _, gw := range gws { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = &pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting httproute '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { + return ctrl.Result{}, err + } else { + dr.From = &ocfgs + } + } + + opcfgs, err := pkg.ParseServicesRelatedForAll() + if err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.UnsetHTTPRoute(req.NamespacedName.String()) + + npcfgs, err := pkg.ParseServicesRelatedForAll() + if err != nil { + return ctrl.Result{}, err + } + + for _, gw := range gws { + + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = &pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting httproute '%s'", req.NamespacedName.String()), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { + return ctrl.Result{}, err + } else { + dr.To = &ncfgs + } + } + + for _, dr := range drs { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: dr.Meta, + From: dr.From, + To: dr.To, + StatusFunc: func() { + }, + Partition: dr.Partition, + } + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("updating services for deleting httproute '%s'", req.NamespacedName.String()), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + }, + Partition: "cis-c-tenant", + } + + return ctrl.Result{}, nil +} + +func handleUpsertingHTTPRoute(ctx context.Context, obj *gatewayv1beta1.HTTPRoute) (ctrl.Result, error) { + zlog := log.FromContext(ctx) + reqnsn := utils.Keyname(obj.Namespace, obj.Name) + zlog.V(1).Info("upserting " + reqnsn) + + hr := pkg.ActiveSIGs.GetHTTPRoute(reqnsn) + gws := pkg.ActiveSIGs.GatewayRefsOf(hr) + drs := map[string]*pkg.DeployRequest{} + + for _, gw := range gws { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = &pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting httproute '%s'", reqnsn), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { + return ctrl.Result{}, err + } else { + dr.From = &ocfgs + } + } + + opcfgs, err := pkg.ParseServicesRelatedForAll() + if err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.SetHTTPRoute(obj.DeepCopy()) + + npcfgs, err := pkg.ParseServicesRelatedForAll() + if err != nil { + return ctrl.Result{}, err + } + + // We still need to consider gateways that were previously associated but are no longer associated, + // Or the previously associated gateways may be recognized as resource deletions. + gws = unifiedGateways(append(gws, pkg.ActiveSIGs.GatewayRefsOf(obj.DeepCopy())...)) + + for _, gw := range gws { + if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { + drs[string(gw.Spec.GatewayClassName)] = &pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting httproute '%s'", reqnsn), + Partition: string(gw.Spec.GatewayClassName), + } + } + dr := drs[string(gw.Spec.GatewayClassName)] + if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { + return ctrl.Result{}, err + } else { + dr.To = &ncfgs + } + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("updating services for upserting httproute '%s'", reqnsn), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + }, + Partition: "cis-c-tenant", + } + + for _, dr := range drs { + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: dr.Meta, + From: dr.From, + To: dr.To, + StatusFunc: func() { + }, + Partition: dr.Partition, + } + } + + return ctrl.Result{}, nil +} + +func unifiedGateways(objs []*gatewayv1beta1.Gateway) []*gatewayv1beta1.Gateway { + + m := map[string]bool{} + rlt := []*gatewayv1beta1.Gateway{} + + for _, obj := range objs { + name := utils.Keyname(obj.Namespace, obj.Name) + if _, f := m[name]; !f { + m[name] = true + rlt = append(rlt, obj) + } + } + return rlt +} diff --git a/controllers/v1_controller.go b/controllers/v1_controller.go index 3c8fcc1..58deeaf 100644 --- a/controllers/v1_controller.go +++ b/controllers/v1_controller.go @@ -22,6 +22,7 @@ import ( "gitee.com/zongzw/bigip-kubernetes-gateway/k8s" "gitee.com/zongzw/bigip-kubernetes-gateway/pkg" + "gitee.com/zongzw/f5-bigip-rest/utils" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -53,99 +54,14 @@ func (r *EndpointsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // zlog.V(1).Info("endpoint event: " + req.NamespacedName.String()) if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { - svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - gws := pkg.ActiveSIGs.GetRootGateways(svc) - drs := map[string]pkg.DeployRequest{} - - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("deleting endpoints '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.From = &ocfgs - } - } - pkg.ActiveSIGs.UnsetEndpoints(req.NamespacedName.String()) - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("deleting endpoints '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.To = &ncfgs - } - } - for _, dr := range drs { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: dr.Meta, - From: dr.From, - To: dr.To, - StatusFunc: func() { - }, - Partition: dr.Partition, - } - } - return ctrl.Result{}, nil + defer pkg.ActiveSIGs.UnsetEndpoints(req.NamespacedName.String()) + return handleDeletingEndpoints(ctx, req) } else { return ctrl.Result{}, err } } else { - svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - gws := pkg.ActiveSIGs.GetRootGateways(svc) - - drs := map[string]pkg.DeployRequest{} - - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting endpoints '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.From = &ocfgs - } - } - pkg.ActiveSIGs.SetEndpoints(obj.DeepCopy()) - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting endpoints '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.To = &ncfgs - } - } - for _, dr := range drs { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: dr.Meta, - From: dr.From, - To: dr.To, - StatusFunc: func() { - }, - Partition: dr.Partition, - } - } - return ctrl.Result{}, nil + defer pkg.ActiveSIGs.SetEndpoints(&obj) + return handleUpsertingEndpoints(ctx, &obj) } } @@ -156,103 +72,14 @@ func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct zlog.V(1).Info("Service event: " + req.NamespacedName.String()) if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { if client.IgnoreNotFound(err) == nil { - svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - gws := pkg.ActiveSIGs.GetRootGateways(svc) - drs := map[string]pkg.DeployRequest{} - - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("deleting service '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.From = &ocfgs - } - } - - pkg.ActiveSIGs.UnsetService(req.NamespacedName.String()) - - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("deleting service '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.To = &ncfgs - } - } - for _, dr := range drs { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: dr.Meta, - From: dr.From, - To: dr.To, - StatusFunc: func() { - }, - Partition: dr.Partition, - } - } - - return ctrl.Result{}, nil + defer pkg.ActiveSIGs.UnsetService(req.NamespacedName.String()) + return handleDeletingService(ctx, req) } else { return ctrl.Result{}, err } } else { - svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) - gws := pkg.ActiveSIGs.GetRootGateways(svc) - - drs := map[string]pkg.DeployRequest{} - - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting service '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ocfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.From = &ocfgs - } - } - pkg.ActiveSIGs.SetService(obj.DeepCopy()) - for _, gw := range gws { - if _, f := drs[string(gw.Spec.GatewayClassName)]; !f { - drs[string(gw.Spec.GatewayClassName)] = pkg.DeployRequest{ - Meta: fmt.Sprintf("upserting service '%s'", req.NamespacedName.String()), - Partition: string(gw.Spec.GatewayClassName), - } - } - dr := drs[string(gw.Spec.GatewayClassName)] - if ncfgs, err := pkg.ParseGatewayRelatedForClass(string(gw.Spec.GatewayClassName), gws); err != nil { - return ctrl.Result{}, err - } else { - dr.To = &ncfgs - } - } - for _, dr := range drs { - pkg.PendingDeploys <- pkg.DeployRequest{ - Meta: dr.Meta, - From: dr.From, - To: dr.To, - StatusFunc: func() { - }, - Partition: dr.Partition, - } - } - - return ctrl.Result{}, nil + defer pkg.ActiveSIGs.SetService(&obj) + return handleUpsertingService(ctx, &obj) } } @@ -312,3 +139,242 @@ func SetupReconcilerForCoreV1WithManager(mgr ctrl.Manager) error { // Partition: gwc, // } // } + +func handleDeletingEndpoints(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + // opcfgs, err := pkg.ParseServicesRelatedForAll() + // if err != nil { + // return ctrl.Result{}, err + // } + + // pkg.ActiveSIGs.UnsetEndpoints(req.NamespacedName.String()) + + // npcfgs, err := pkg.ParseServicesRelatedForAll() + // if err != nil { + // return ctrl.Result{}, err + // } + // pkg.PendingDeploys <- pkg.DeployRequest{ + // Meta: fmt.Sprintf("deleting endpoints '%s'", req.NamespacedName.String()), + // From: &opcfgs, + // To: &npcfgs, + // StatusFunc: func() { + // }, + // Partition: "cis-c-tenant", + // } + + // return ctrl.Result{}, nil + + svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) + + found := false + for _, gw := range pkg.ActiveSIGs.GetRootGateways([]*v1.Service{svc}) { + if pkg.ActiveSIGs.GetGatewayClass(string(gw.Spec.GatewayClassName)) != nil { + found = true + break + } + } + if found { + opcfgs, err := pkg.ParseReferedServiceKeys([]string{req.NamespacedName.String()}) + if err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.UnsetEndpoints(req.NamespacedName.String()) + npcfgs, err := pkg.ParseReferedServiceKeys([]string{req.NamespacedName.String()}) + if err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting endpoints '%s'", req.NamespacedName.String()), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + }, + Partition: "cis-c-tenant", + } + + } + + return ctrl.Result{}, nil +} + +func handleUpsertingEndpoints(ctx context.Context, obj *v1.Endpoints) (ctrl.Result, error) { + + // opcfgs, err := pkg.ParseServicesRelatedForAll() + // if err != nil { + // return ctrl.Result{}, err + // } + + // pkg.ActiveSIGs.SetEndpoints(obj.DeepCopy()) + + // npcfgs, err := pkg.ParseServicesRelatedForAll() + // if err != nil { + // return ctrl.Result{}, err + // } + // pkg.PendingDeploys <- pkg.DeployRequest{ + // Meta: fmt.Sprintf("upserting endpoints '%s'", utils.Keyname(obj.Namespace, obj.Name)), + // From: &opcfgs, + // To: &npcfgs, + // StatusFunc: func() { + // }, + // Partition: "cis-c-tenant", + // } + + // return ctrl.Result{}, nil + + reqnsn := utils.Keyname(obj.Namespace, obj.Name) + svc := pkg.ActiveSIGs.GetService(reqnsn) + + found := false + for _, gw := range pkg.ActiveSIGs.GetRootGateways([]*v1.Service{svc}) { + if pkg.ActiveSIGs.GetGatewayClass(string(gw.Spec.GatewayClassName)) != nil { + found = true + break + } + } + + if found { + opcfgs, err := pkg.ParseReferedServiceKeys([]string{reqnsn}) + if err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.SetEndpoints(obj.DeepCopy()) + npcfgs, err := pkg.ParseReferedServiceKeys([]string{reqnsn}) + if err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting endpoints '%s'", reqnsn), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + }, + Partition: "cis-c-tenant", + } + } + + return ctrl.Result{}, nil +} + +func handleDeletingService(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + // opcfgs, err := pkg.ParseServicesRelatedForAll() + // if err != nil { + // return ctrl.Result{}, err + // } + + // pkg.ActiveSIGs.UnsetService(req.NamespacedName.String()) + + // npcfgs, err := pkg.ParseServicesRelatedForAll() + // if err != nil { + // return ctrl.Result{}, err + // } + // pkg.PendingDeploys <- pkg.DeployRequest{ + // Meta: fmt.Sprintf("deleting service '%s'", req.NamespacedName.String()), + // From: &opcfgs, + // To: &npcfgs, + // StatusFunc: func() { + // }, + // Partition: "cis-c-tenant", + // } + + // return ctrl.Result{}, nil + + svc := pkg.ActiveSIGs.GetService(req.NamespacedName.String()) + + found := false + for _, gw := range pkg.ActiveSIGs.GetRootGateways([]*v1.Service{svc}) { + if pkg.ActiveSIGs.GetGatewayClass(string(gw.Spec.GatewayClassName)) != nil { + found = true + break + } + } + if found { + opcfgs, err := pkg.ParseReferedServiceKeys([]string{req.NamespacedName.String()}) + if err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.UnsetService(req.NamespacedName.String()) + npcfgs, err := pkg.ParseReferedServiceKeys([]string{req.NamespacedName.String()}) + if err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("deleting service '%s'", req.NamespacedName.String()), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + }, + Partition: "cis-c-tenant", + } + + } + + return ctrl.Result{}, nil + +} + +func handleUpsertingService(ctx context.Context, obj *v1.Service) (ctrl.Result, error) { + + // opcfgs, err := pkg.ParseServicesRelatedForAll() + // if err != nil { + // return ctrl.Result{}, err + // } + + // pkg.ActiveSIGs.SetService(obj.DeepCopy()) + + // npcfgs, err := pkg.ParseServicesRelatedForAll() + // if err != nil { + // return ctrl.Result{}, err + // } + // pkg.PendingDeploys <- pkg.DeployRequest{ + // Meta: fmt.Sprintf("upserting service '%s'", utils.Keyname(obj.Namespace, obj.Name)), + // From: &opcfgs, + // To: &npcfgs, + // StatusFunc: func() { + // }, + // Partition: "cis-c-tenant", + // } + + // return ctrl.Result{}, nil + + reqnsn := utils.Keyname(obj.Namespace, obj.Name) + svc := pkg.ActiveSIGs.GetService(reqnsn) + + found := false + for _, gw := range pkg.ActiveSIGs.GetRootGateways([]*v1.Service{svc}) { + if pkg.ActiveSIGs.GetGatewayClass(string(gw.Spec.GatewayClassName)) != nil { + found = true + break + } + } + + if found { + opcfgs, err := pkg.ParseReferedServiceKeys([]string{reqnsn}) + if err != nil { + return ctrl.Result{}, err + } + + pkg.ActiveSIGs.SetService(obj.DeepCopy()) + npcfgs, err := pkg.ParseReferedServiceKeys([]string{reqnsn}) + if err != nil { + return ctrl.Result{}, err + } + + pkg.PendingDeploys <- pkg.DeployRequest{ + Meta: fmt.Sprintf("upserting service '%s'", reqnsn), + From: &opcfgs, + To: &npcfgs, + StatusFunc: func() { + }, + Partition: "cis-c-tenant", + } + } + + return ctrl.Result{}, nil +} diff --git a/pkg/deployer.go b/pkg/deployer.go index 0ebaedd..23d7092 100644 --- a/pkg/deployer.go +++ b/pkg/deployer.go @@ -11,6 +11,26 @@ func deploy(bigip *f5_bigip.BIGIP, partition string, ocfgs, ncfgs *map[string]in if err := bigip.DeployPartition(partition); err != nil { return err } + + // // filter out pools arps nodes from ocfgs and ncfgs + // opcfgs, npcfgs, err := filterPoolCfgs(ocfgs, ncfgs) + // if err != nil { + // return err + // } + + // // case: pools arps and nodes are in cis-c-tenant + // pcmds, err := bigip.GenRestRequests("cis-c-tenant", opcfgs, npcfgs) + // for + // // for pools to delete, check if there's no refs to them, collect arps and nodes, delete them. + // // for pools to create, collect arps and nodes to create them. + + // // case: pools arps and nodes are in namespace partition + // // for pools to delete, check if there's no refs to them, + // // delete pool and nodes from namespace partition + // // delete arps from cis-c-tenant + // // for pools to create, + // // create pool and nodes to namepsace partition + // // create arps to cis-c-tenent cmds, err := bigip.GenRestRequests(partition, ocfgs, ncfgs) if err != nil { return err @@ -18,6 +38,48 @@ func deploy(bigip *f5_bigip.BIGIP, partition string, ocfgs, ncfgs *map[string]in return bigip.DoRestRequests(cmds) } +// func filterPoolCfgs(ocfgs, ncfgs *map[string]interface{}) (*map[string]interface{}, *map[string]interface{}, error) { + +// ocfgsPool := map[string]interface{}{} +// ncfgsPool := map[string]interface{}{} +// if ocfgs != nil { +// for fn, res := range *ocfgs { +// if _, f := ocfgsPool[fn]; !f { +// ocfgsPool[fn] = map[string]interface{}{} +// } +// fnmap := ocfgsPool[fn].(map[string]interface{}) +// if resJson, ok := res.(map[string]interface{}); !ok { +// return nil, nil, fmt.Errorf("invalid resource format, should be json") +// } else { +// for tn, body := range resJson { +// if strings.HasPrefix(tn, "ltm/pool/") || strings.HasPrefix(tn, "ltm/arp/") || strings.HasPrefix(tn, "ltm/node/") { +// fnmap[tn] = body +// } +// } +// } +// } +// } +// if ncfgs != nil { +// for fn, res := range *ncfgs { +// if _, f := ncfgsPool[fn]; !f { +// ncfgsPool[fn] = map[string]interface{}{} +// } +// fnmap := ncfgsPool[fn].(map[string]interface{}) +// if resJson, ok := res.(map[string]interface{}); !ok { +// return nil, nil, fmt.Errorf("invalid resource format, should be json") +// } else { +// for tn, body := range resJson { +// if strings.HasPrefix(tn, "ltm/pool/") || strings.HasPrefix(tn, "ltm/arp/") || strings.HasPrefix(tn, "ltm/node/") { +// fnmap[tn] = body +// } +// } +// } +// } +// } + +// return &ocfgsPool, &ncfgsPool, nil +// } + func Deployer(stopCh chan struct{}, bigip *f5_bigip.BIGIP) { for { select { diff --git a/pkg/parser.go b/pkg/parser.go index 5816bb3..9a51fc9 100644 --- a/pkg/parser.go +++ b/pkg/parser.go @@ -83,6 +83,57 @@ func ParseGatewayRelatedForClass(className string, gwObjs []*gatewayv1beta1.Gate }, nil } +// ParseServicesRelatedForAll parse all refered services +func ParseServicesRelatedForAll() (map[string]interface{}, error) { + + // all services that are referenced but may not exist + svcs := ActiveSIGs.AllAttachedServiceKeys() + + return ParseReferedServiceKeys(svcs) +} + +func ParseReferedServiceKeys(svcs []string) (map[string]interface{}, error) { + rlt := map[string]interface{}{} + for _, svc := range svcs { + + ns := strings.Split(svc, "/")[0] + n := strings.Split(svc, "/")[1] + + name := strings.Join([]string{ns, n}, ".") + rlt["ltm/pool/"+name] = map[string]interface{}{ + "name": name, + "monitor": "min 1 of tcp", + "members": []interface{}{}, + + // "minActiveMembers": 0, + // TODO: there's at least one field for PATCH a pool. or we may need to fix that + // {"code":400,"message":"transaction failed:one or more properties must be specified","errorStack":[],"apiError":2} + } + if fmtmbs, err := parseMembersFrom(ns, n); err != nil { + return rlt, err + } else { + rlt["ltm/pool/"+name].(map[string]interface{})["members"] = fmtmbs + } + + if mon, err := parseMonitorFrom(ns, n); err != nil { + return rlt, err + } else { + rlt["ltm/pool/"+name].(map[string]interface{})["monitor"] = mon + } + + if err := parseArpsFrom(ns, n, rlt); err != nil { + return rlt, err + } + if err := parseNodesFrom(ns, n, rlt); err != nil { + return rlt, err + } + } + + return map[string]interface{}{ + "": rlt, + }, nil +} + func parseHTTPRoute(className string, hr *gatewayv1beta1.HTTPRoute) (map[string]interface{}, error) { defer utils.TimeItToPrometheus()() @@ -91,9 +142,9 @@ func parseHTTPRoute(className string, hr *gatewayv1beta1.HTTPRoute) (map[string] } rlt := map[string]interface{}{} - if err := parsePoolsFrom(hr, rlt); err != nil { - return map[string]interface{}{}, err - } + // if err := parsePoolsFrom(hr, rlt); err != nil { + // return map[string]interface{}{}, err + // } if err := parseiRulesFrom(className, hr, rlt); err != nil { return map[string]interface{}{}, err @@ -216,70 +267,70 @@ func parseGateway(gw *gatewayv1beta1.Gateway) (map[string]interface{}, error) { // }, nil // } -func parsePoolsFrom(hr *gatewayv1beta1.HTTPRoute, rlt map[string]interface{}) error { +// func parsePoolsFrom(hr *gatewayv1beta1.HTTPRoute, rlt map[string]interface{}) error { - creatPool := func(ns, n string, rlt map[string]interface{}) error { - name := strings.Join([]string{ns, n}, ".") - rlt["ltm/pool/"+name] = map[string]interface{}{ - "name": name, - "monitor": "min 1 of tcp", - "members": []interface{}{}, +// creatPool := func(ns, n string, rlt map[string]interface{}) error { +// name := strings.Join([]string{ns, n}, ".") +// rlt["ltm/pool/"+name] = map[string]interface{}{ +// "name": name, +// "monitor": "min 1 of tcp", +// "members": []interface{}{}, - // "minActiveMembers": 0, - // TODO: there's at least one field for PATCH a pool. or we may need to fix that - // {"code":400,"message":"transaction failed:one or more properties must be specified","errorStack":[],"apiError":2} - } - if fmtmbs, err := parseMembersFrom(ns, n); err != nil { - return err - } else { - rlt["ltm/pool/"+name].(map[string]interface{})["members"] = fmtmbs - } +// // "minActiveMembers": 0, +// // TODO: there's at least one field for PATCH a pool. or we may need to fix that +// // {"code":400,"message":"transaction failed:one or more properties must be specified","errorStack":[],"apiError":2} +// } +// if fmtmbs, err := parseMembersFrom(ns, n); err != nil { +// return err +// } else { +// rlt["ltm/pool/"+name].(map[string]interface{})["members"] = fmtmbs +// } - if mon, err := parseMonitorFrom(ns, n); err != nil { - return err - } else { - rlt["ltm/pool/"+name].(map[string]interface{})["monitor"] = mon - } +// if mon, err := parseMonitorFrom(ns, n); err != nil { +// return err +// } else { +// rlt["ltm/pool/"+name].(map[string]interface{})["monitor"] = mon +// } - if err := parseArpsFrom(ns, n, rlt); err != nil { - return err - } - if err := parseNodesFrom(ns, n, rlt); err != nil { - return err - } - return nil - } +// if err := parseArpsFrom(ns, n, rlt); err != nil { +// return err +// } +// if err := parseNodesFrom(ns, n, rlt); err != nil { +// return err +// } +// return nil +// } - for _, rl := range hr.Spec.Rules { - for _, br := range rl.BackendRefs { - ns := hr.Namespace - if br.Namespace != nil { - ns = string(*br.Namespace) - } - if err := creatPool(ns, string(br.Name), rlt); err != nil { - return err - } - } - } +// for _, rl := range hr.Spec.Rules { +// for _, br := range rl.BackendRefs { +// ns := hr.Namespace +// if br.Namespace != nil { +// ns = string(*br.Namespace) +// } +// if err := creatPool(ns, string(br.Name), rlt); err != nil { +// return err +// } +// } +// } - // pools from ExtensionRef as well. - for _, rl := range hr.Spec.Rules { - for _, fl := range rl.Filters { - if fl.Type == gatewayv1beta1.HTTPRouteFilterExtensionRef && fl.ExtensionRef != nil { - er := fl.ExtensionRef - if er.Group != "" || er.Kind != "Service" { - return fmt.Errorf("resource %s of '%s' not supported", er.Name, utils.Keyname(string(er.Group), string(er.Kind))) - } else { - if err := creatPool(hr.Namespace, string(er.Name), rlt); err != nil { - return err - } - } - } - } - } +// // pools from ExtensionRef as well. +// for _, rl := range hr.Spec.Rules { +// for _, fl := range rl.Filters { +// if fl.Type == gatewayv1beta1.HTTPRouteFilterExtensionRef && fl.ExtensionRef != nil { +// er := fl.ExtensionRef +// if er.Group != "" || er.Kind != "Service" { +// return fmt.Errorf("resource %s of '%s' not supported", er.Name, utils.Keyname(string(er.Group), string(er.Kind))) +// } else { +// if err := creatPool(hr.Namespace, string(er.Name), rlt); err != nil { +// return err +// } +// } +// } +// } +// } - return nil -} +// return nil +// } // TODO: find the way to set monitor func parseMonitorFrom(svcNamespace, svcName string) (string, error) { @@ -505,7 +556,7 @@ func parseiRulesFrom(className string, hr *gatewayv1beta1.HTTPRoute, rlt map[str case gatewayv1beta1.HTTPRouteFilterExtensionRef: if er := filter.ExtensionRef; er != nil { pool := fmt.Sprintf("%s.%s", hr.Namespace, er.Name) - filterActions = append(filterActions, fmt.Sprintf("pool /%s/%s", className, pool)) + filterActions = append(filterActions, fmt.Sprintf("pool /%s/%s", "cis-c-tenant", pool)) } } } @@ -518,7 +569,7 @@ func parseiRulesFrom(className string, hr *gatewayv1beta1.HTTPRoute, rlt map[str ns = string(*br.Namespace) } pn := strings.Join([]string{ns, string(br.Name)}, ".") - pool = fmt.Sprintf("pool /%s/%s", className, pn) + pool = fmt.Sprintf("pool /%s/%s", "cis-c-tenant", pn) } rules = append(rules, fmt.Sprintf(` if { %s } { diff --git a/pkg/utils.go b/pkg/utils.go index e3c06f0..0aed1a0 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -292,6 +292,52 @@ func (c *SIGCache) _attachedServices(hr *gatewayv1beta1.HTTPRoute) []*v1.Service return svcs } +func (c *SIGCache) AllAttachedServiceKeys() []string { + defer utils.TimeItToPrometheus()() + + c.mutex.RLock() + defer c.mutex.RUnlock() + + svcs := []string{} + for _, gwc := range c.GatewayClasses { + for _, gw := range c._attachedGateways(gwc) { + for _, hr := range c._attachedHTTPRoutes(gw) { + svcs = append(svcs, c._attachedServiceKeys(hr)...) + } + } + } + return svcs +} + +func (c *SIGCache) _attachedServiceKeys(hr *gatewayv1beta1.HTTPRoute) []string { + if hr == nil { + return []string{} + } + + svcs := []string{} + for _, rl := range hr.Spec.Rules { + for _, br := range rl.BackendRefs { + ns := hr.Namespace + if br.Namespace != nil { + ns = string(*br.Namespace) + } + svcs = append(svcs, utils.Keyname(ns, string(br.Name))) + } + } + for _, rl := range hr.Spec.Rules { + for _, fl := range rl.Filters { + if fl.Type == gatewayv1beta1.HTTPRouteFilterExtensionRef && fl.ExtensionRef != nil { + er := fl.ExtensionRef + if er.Group == "" && er.Kind == "Service" { + svcs = append(svcs, utils.Keyname(hr.Namespace, string(er.Name))) + } + } + } + } + + return utils.Unified(svcs) +} + func (c *SIGCache) HTTPRoutesRefsOf(svc *v1.Service) []*gatewayv1beta1.HTTPRoute { defer utils.TimeItToPrometheus()() @@ -467,6 +513,7 @@ func (c *SIGCache) _HTTPRoutesRefsOf(svc *v1.Service) []*gatewayv1beta1.HTTPRout // } // } +// GetNeighborGateways get neighbor gateways(itself is not included) for all gateway class. func (c *SIGCache) GetNeighborGateways(gw *gatewayv1beta1.Gateway) []*gatewayv1beta1.Gateway { defer utils.TimeItToPrometheus()() @@ -494,7 +541,7 @@ func (c *SIGCache) GetNeighborGateways(gw *gatewayv1beta1.Gateway) []*gatewayv1b return rlt } -func (c *SIGCache) GetRootGateways(svc *v1.Service) []*gatewayv1beta1.Gateway { +func (c *SIGCache) GetRootGateways(svcs []*v1.Service) []*gatewayv1beta1.Gateway { defer utils.TimeItToPrometheus()() c.mutex.RLock() @@ -502,11 +549,13 @@ func (c *SIGCache) GetRootGateways(svc *v1.Service) []*gatewayv1beta1.Gateway { gwmap := map[string]*gatewayv1beta1.Gateway{} - hrs := c._HTTPRoutesRefsOf(svc) - for _, hr := range hrs { - gws := c._gatewayRefsOf(hr) - for _, gw := range gws { - gwmap[utils.Keyname(gw.Namespace, gw.Name)] = gw + for _, svc := range svcs { + hrs := c._HTTPRoutesRefsOf(svc) + for _, hr := range hrs { + gws := c._gatewayRefsOf(hr) + for _, gw := range gws { + gwmap[utils.Keyname(gw.Namespace, gw.Name)] = gw + } } } rlt := []*gatewayv1beta1.Gateway{} diff --git a/tests/test.py b/tests/test.py index 8b113f2..4e04079 100644 --- a/tests/test.py +++ b/tests/test.py @@ -128,7 +128,7 @@ def curl_verify(name, req, expected_resp): expected_body = expected_resp.get('body', {}) try: warn(name, "requesting: %s" % req) - resp = requests.request(method=method, url="%s" % url, params=queries, headers=headers, json=body, allow_redirects=False) + resp = requests.request(method=method, url="%s" % url, params=queries, headers=headers, json=body, allow_redirects=False, timeout=2) try: resp_headers = dict(resp.headers) if type(expected_body) == type({}):