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 12, 2024
1 parent ea49431 commit 90bcdd3
Show file tree
Hide file tree
Showing 6 changed files with 368 additions and 34 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
171 changes: 171 additions & 0 deletions internal/controller/addressset_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
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"
"strings"
"time"

"github.com/go-logr/logr"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/util/retry"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/predicate"

infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2"
wrappedruntimeclient "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimeclient"
wrappedruntimereconciler "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimereconciler"
"github.com/linode/cluster-api-provider-linode/util/reconciler"
)

// 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

//nolint:gocognit,cyclop // as simple as possible
func removeAddrSetRefs(ctx context.Context, k8sClient client.Client, lfw *infrav1alpha2.LinodeFirewall, addrSet *infrav1alpha2.AddressSet, finalizer string, log logr.Logger) {
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
}
}
}
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
}
}
}
for _, ruleRef := range lfw.Spec.InboundRuleRefs {
fwRule := &infrav1alpha2.FirewallRule{}
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: ruleRef.Namespace, Name: ruleRef.Name}, fwRule); err != nil {
if err = client.IgnoreNotFound(err); err != nil {
log.Error(err, "failed to fetch FirewallRule")
}
}
removeFWRuleFinalizer(ctx, k8sClient, finalizer, fwRule, log)
}
for _, ruleRef := range lfw.Spec.OutboundRuleRefs {
fwRule := &infrav1alpha2.FirewallRule{}
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: ruleRef.Namespace, Name: ruleRef.Name}, fwRule); err != nil {
if err = client.IgnoreNotFound(err); err != nil {
log.Error(err, "failed to fetch FirewallRule")
}
}
removeFWRuleFinalizer(ctx, k8sClient, finalizer, fwRule, log)
}
}

func (r *AddressSetReconciler) removeFinalizer(ctx context.Context, finalizer string, addrSet *infrav1alpha2.AddressSet, log logr.Logger) {
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
}
log.Error(err, "failed to fetch referenced LinodeFirewall")
return
}
removeAddrSetRefs(ctx, r.TracedClient(), lfw, addrSet, finalizer, log)
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
}
log.Info("removed AddressSet finalizer", "namespace", addrSet.Namespace, "name", addrSet.Name, "finalizer", finalizer)
}
}

func (r *AddressSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
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() {
return ctrl.Result{}, nil
}

log.Info("deleting firewallrule", "namespace", addrSet.Namespace, "name", addrSet.Name)
for _, finalizer := range addrSet.Finalizers {
r.removeFinalizer(ctx, finalizer, addrSet, log)
}

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())
}
Loading

0 comments on commit 90bcdd3

Please sign in to comment.