Skip to content

Commit

Permalink
add controllers for processing dependent resource deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
AshleyDumaine committed Dec 11, 2024
1 parent d29849c commit 5f257ea
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 35 deletions.
2 changes: 2 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: cluster.x-k8s.io
group: infrastructure
kind: AddressSet
Expand All @@ -124,6 +125,7 @@ resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: cluster.x-k8s.io
group: infrastructure
kind: FirewallRule
Expand Down
18 changes: 18 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,24 @@ func setupControllers(mgr manager.Manager, flags flagVars, linodeClientConfig, d
setupLog.Error(err, "unable to create controller", "controller", "LinodeFirewall")
os.Exit(1)
}

// FirewallRule Controller
if err := (&controller.FirewallRuleReconciler{
Client: mgr.GetClient(),
WatchFilterValue: flags.clusterWatchFilter,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "FirewallRule")
os.Exit(1)
}

// AddressSet Controller
if err := (&controller.AddressSetReconciler{
Client: mgr.GetClient(),
WatchFilterValue: flags.clusterWatchFilter,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "AddressSet")
os.Exit(1)
}
}

// setupWebhooks initializes webhooks for the specified resources in the manager.
Expand Down
29 changes: 11 additions & 18 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,16 @@ rules:
resources:
- addresssets
- firewallrules
- linodeclusters
- linodefirewalls
- linodemachines
- linodeobjectstoragebuckets
- linodeobjectstoragekeys
- linodeplacementgroups
- linodevpcs
verbs:
- create
- delete
- get
- list
- patch
Expand All @@ -54,24 +63,8 @@ rules:
- apiGroups:
- infrastructure.cluster.x-k8s.io
resources:
- linodeclusters
- linodefirewalls
- linodemachines
- linodeobjectstoragebuckets
- linodeobjectstoragekeys
- linodeplacementgroups
- linodevpcs
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- infrastructure.cluster.x-k8s.io
resources:
- addresssets/status
- firewallrules/status
- linodeclusters/status
- linodefirewalls/status
- linodemachines/status
Expand Down
142 changes: 142 additions & 0 deletions internal/controller/addressset_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
Copyright 2023 Akamai Technologies, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controller

import (
"context"
wrappedruntimeclient "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimeclient"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"strings"
"time"

wrappedruntimereconciler "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimereconciler"
"github.com/linode/cluster-api-provider-linode/util/reconciler"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"

infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2"
)

// AddressSetReconciler reconciles a FirewallRule object
type AddressSetReconciler struct {
client.Client
Scheme *runtime.Scheme
WatchFilterValue string
ReconcileTimeout time.Duration
}

//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=addresssets,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=addresssets/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=addresssets/finalizers,verbs=update
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=linodefirewalls,verbs=get;list;watch

func (r *AddressSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {

Check failure on line 52 in internal/controller/addressset_controller.go

View workflow job for this annotation

GitHub Actions / go-analyze

cognitive complexity 67 of func `(*AddressSetReconciler).Reconcile` is high (> 30) (gocognit)
ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultedLoopTimeout(r.ReconcileTimeout))
defer cancel()

log := ctrl.LoggerFrom(ctx).WithName("LinodeFirewallReconciler").WithValues("name", req.NamespacedName.String())
addrSet := &infrav1alpha2.AddressSet{}
if err := r.TracedClient().Get(ctx, req.NamespacedName, addrSet); err != nil {
if err = client.IgnoreNotFound(err); err != nil {
log.Error(err, "failed to fetch AddressSet")
}

return ctrl.Result{}, client.IgnoreNotFound(err)
}

if !addrSet.DeletionTimestamp.IsZero() {

Check failure on line 66 in internal/controller/addressset_controller.go

View workflow job for this annotation

GitHub Actions / go-analyze

`if !addrSet.DeletionTimestamp.IsZero()` has complex nested blocks (complexity: 16) (nestif)
log.Info("deleting firewallrule", "namespace", addrSet.Namespace, "name", addrSet.Name)
for _, finalizer := range addrSet.Finalizers {
parts := strings.Split(finalizer, ".")
if len(parts) == 3 && parts[0] == "lfw" {
lfw := &infrav1alpha2.LinodeFirewall{}
if err := r.TracedClient().Get(ctx, client.ObjectKey{Namespace: parts[1], Name: parts[2]}, lfw); err != nil {
if apierrors.IsNotFound(err) {
// remove the finalizer since the LinodeFirewall doesn't exist anymore
controllerutil.RemoveFinalizer(addrSet, finalizer)
return ctrl.Result{}, nil
}
log.Error(err, "failed to fetch referenced LinodeFirewall")
return ctrl.Result{}, nil
}
for _, rule := range lfw.Spec.InboundRules {
for _, addrSetRef := range rule.AddressSetRefs {
if addrSetRef.Namespace == "" {
addrSetRef.Namespace = lfw.Namespace
}
if addrSetRef.Namespace == addrSet.Namespace && addrSetRef.Name == addrSet.Name {
// bail out, can't clean up the AddressSet
log.Info("cannot clean up AddressSet, still in use by LinodeFirewall", "namespace", lfw.Namespace, "name", lfw.Name)
return ctrl.Result{}, nil
}
}
}
for _, rule := range lfw.Spec.OutboundRules {
for _, addrSetRef := range rule.AddressSetRefs {
if addrSetRef.Namespace == "" {
addrSetRef.Namespace = lfw.Namespace
}
if addrSetRef.Namespace == addrSet.Namespace && addrSetRef.Name == addrSet.Name {
// bail out, can't clean up the AddressSet
log.Info("cannot clean up AddressSet, still in use by LinodeFirewall", "namespace", lfw.Namespace, "name", lfw.Name)
return ctrl.Result{}, nil
}
}
}
// TODO: check the FWRuleRefs
controllerutil.RemoveFinalizer(addrSet, finalizer)
if retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
return r.Update(ctx, addrSet)
}); retryErr != nil {
log.Error(retryErr, "failed to remove finalizer")
return ctrl.Result{}, nil
}
log.Info("removed AddressSet finalizer", "namespace", addrSet.Namespace, "name", addrSet.Name, "finalizer", finalizer)
}
}
}

return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *AddressSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&infrav1alpha2.AddressSet{}).
WithEventFilter(predicate.Funcs{
// only enqueue delete requests
DeleteFunc: func(e event.DeleteEvent) bool {
return true
},
CreateFunc: func(e event.CreateEvent) bool {
return false
},
UpdateFunc: func(e event.UpdateEvent) bool {
return false
},
}).
Complete(wrappedruntimereconciler.NewRuntimeReconcilerWithTracing(r, wrappedruntimereconciler.DefaultDecorator()))
}

func (r *AddressSetReconciler) TracedClient() client.Client {
return wrappedruntimeclient.NewRuntimeClientWithTracing(r.Client, wrappedruntimereconciler.DefaultDecorator())
}
137 changes: 137 additions & 0 deletions internal/controller/firewallrule_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
Copyright 2023 Akamai Technologies, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controller

import (
"context"
wrappedruntimeclient "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimeclient"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"strings"
"time"

wrappedruntimereconciler "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimereconciler"
"github.com/linode/cluster-api-provider-linode/util/reconciler"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"

infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2"
)

// FirewallRuleReconciler reconciles a FirewallRule object
type FirewallRuleReconciler struct {
client.Client
Scheme *runtime.Scheme
WatchFilterValue string
ReconcileTimeout time.Duration
}

//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=firewallrules,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=firewallrules/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=firewallrules/finalizers,verbs=update
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=linodefirewalls,verbs=get;list;watch

func (r *FirewallRuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {

Check failure on line 52 in internal/controller/firewallrule_controller.go

View workflow job for this annotation

GitHub Actions / go-analyze

cognitive complexity 53 of func `(*FirewallRuleReconciler).Reconcile` is high (> 30) (gocognit)
ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultedLoopTimeout(r.ReconcileTimeout))
defer cancel()

log := ctrl.LoggerFrom(ctx).WithName("LinodeFirewallReconciler").WithValues("name", req.NamespacedName.String())
fwRule := &infrav1alpha2.FirewallRule{}
if err := r.TracedClient().Get(ctx, req.NamespacedName, fwRule); err != nil {
if err = client.IgnoreNotFound(err); err != nil {
log.Error(err, "failed to fetch FirewallRule")
}

return ctrl.Result{}, client.IgnoreNotFound(err)
}

if !fwRule.DeletionTimestamp.IsZero() {

Check failure on line 66 in internal/controller/firewallrule_controller.go

View workflow job for this annotation

GitHub Actions / go-analyze

`if !fwRule.DeletionTimestamp.IsZero()` has complex nested blocks (complexity: 16) (nestif)
log.Info("deleting firewallrule", "namespace", fwRule.Namespace, "name", fwRule.Name)
for _, finalizer := range fwRule.Finalizers {
parts := strings.Split(finalizer, ".")
if len(parts) == 3 && parts[0] == "lfw" {
lfw := &infrav1alpha2.LinodeFirewall{}
if err := r.TracedClient().Get(ctx, client.ObjectKey{Namespace: parts[1], Name: parts[2]}, lfw); err != nil {
if apierrors.IsNotFound(err) {
// remove the finalizer since the LinodeFirewall doesn't exist anymore
controllerutil.RemoveFinalizer(fwRule, finalizer)
return ctrl.Result{}, nil
}
log.Error(err, "failed to fetch referenced LinodeFirewall")
return ctrl.Result{}, nil
}
for _, ruleRef := range lfw.Spec.InboundRuleRefs {
if ruleRef.Namespace == "" {
ruleRef.Namespace = lfw.Namespace
}
if ruleRef.Namespace == fwRule.Namespace && ruleRef.Name == fwRule.Name {
// bail out, can't clean up the FirewallRule
log.Info("cannot clean up FirewallRule, still in use by LinodeFirewall", "namespace", lfw.Namespace, "name", lfw.Name)
return ctrl.Result{}, nil
}
}
for _, ruleRef := range lfw.Spec.OutboundRuleRefs {
if ruleRef.Namespace == "" {
ruleRef.Namespace = lfw.Namespace
}
if ruleRef.Namespace == fwRule.Namespace && ruleRef.Name == fwRule.Name {
// bail out, can't clean up the FirewallRule
log.Info("cannot clean up FirewallRule, still in use by LinodeFirewall", "namespace", lfw.Namespace, "name", lfw.Name)
return ctrl.Result{}, nil
}
}
controllerutil.RemoveFinalizer(fwRule, finalizer)
if retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
return r.Update(ctx, fwRule)
}); retryErr != nil {
log.Error(retryErr, "failed to remove finalizer")
return ctrl.Result{}, nil
}
log.Info("removed FirewallRule finalizer", "namespace", fwRule.Namespace, "name", fwRule.Name, "finalizer", finalizer)
}
}
}

return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *FirewallRuleReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&infrav1alpha2.FirewallRule{}).
WithEventFilter(predicate.Funcs{
// only enqueue delete requests
DeleteFunc: func(e event.DeleteEvent) bool {
return true
},
CreateFunc: func(e event.CreateEvent) bool {
return false
},
UpdateFunc: func(e event.UpdateEvent) bool {
return false
},
}).
Complete(wrappedruntimereconciler.NewRuntimeReconcilerWithTracing(r, wrappedruntimereconciler.DefaultDecorator()))
}

func (r *FirewallRuleReconciler) TracedClient() client.Client {
return wrappedruntimeclient.NewRuntimeClientWithTracing(r.Client, wrappedruntimereconciler.DefaultDecorator())
}
2 changes: 1 addition & 1 deletion internal/controller/linodefirewall_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"errors"
"fmt"
"net/http"
crcontroller "sigs.k8s.io/controller-runtime/pkg/controller"
"time"

"github.com/go-logr/logr"
Expand All @@ -36,7 +37,6 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
crcontroller "sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
Expand Down
Loading

0 comments on commit 5f257ea

Please sign in to comment.