...

Source file src/github.com/chaos-mesh/chaos-mesh/controllers/common/finalizers/controller.go

Documentation: github.com/chaos-mesh/chaos-mesh/controllers/common/finalizers

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package finalizers
    17  
    18  import (
    19  	"context"
    20  
    21  	"github.com/go-logr/logr"
    22  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/client-go/util/retry"
    25  	ctrl "sigs.k8s.io/controller-runtime"
    26  	"sigs.k8s.io/controller-runtime/pkg/client"
    27  
    28  	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
    29  	"github.com/chaos-mesh/chaos-mesh/controllers/utils/recorder"
    30  )
    31  
    32  const (
    33  	// AnnotationCleanFinalizer key
    34  	AnnotationCleanFinalizer = `chaos-mesh.chaos-mesh.org/cleanFinalizer`
    35  	// AnnotationCleanFinalizerForced value
    36  	AnnotationCleanFinalizerForced = `forced`
    37  
    38  	RecordFinalizer = "chaos-mesh/records"
    39  )
    40  
    41  // ReconcilerMeta defines the meta of InitReconciler and CleanReconciler struct.
    42  type ReconcilerMeta struct {
    43  	// Object is used to mark the target type of this Reconciler
    44  	Object v1alpha1.InnerObject
    45  
    46  	// Client is used to operate on the Kubernetes cluster
    47  	client.Client
    48  
    49  	Recorder recorder.ChaosRecorder
    50  
    51  	Log logr.Logger
    52  }
    53  
    54  // InitReconciler for common chaos to init the finalizer
    55  type InitReconciler struct {
    56  	ReconcilerMeta
    57  }
    58  
    59  // Reconcile the common chaos to init the finalizer
    60  func (r *InitReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    61  	obj := r.Object.DeepCopyObject().(v1alpha1.InnerObject)
    62  
    63  	if err := r.Client.Get(context.TODO(), req.NamespacedName, obj); err != nil {
    64  		if apierrors.IsNotFound(err) {
    65  			r.Log.Info("chaos not found")
    66  		} else {
    67  			// TODO: handle this error
    68  			r.Log.Error(err, "unable to get chaos")
    69  		}
    70  		return ctrl.Result{}, nil
    71  	}
    72  
    73  	if !obj.IsDeleted() {
    74  		if !ContainsFinalizer(obj.(metav1.Object), RecordFinalizer) {
    75  			r.Recorder.Event(obj, recorder.FinalizerInited{})
    76  			finalizers := append(obj.GetFinalizers(), RecordFinalizer)
    77  			return updateFinalizer(r.ReconcilerMeta, obj, req, finalizers)
    78  		}
    79  	}
    80  
    81  	return ctrl.Result{}, nil
    82  }
    83  
    84  // CleanReconciler for common chaos to clean the finalizer
    85  type CleanReconciler struct {
    86  	ReconcilerMeta
    87  }
    88  
    89  // Reconcile the common chaos to clean the finalizer
    90  func (r *CleanReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    91  	obj := r.Object.DeepCopyObject().(v1alpha1.InnerObject)
    92  
    93  	if err := r.Client.Get(context.TODO(), req.NamespacedName, obj); err != nil {
    94  		if apierrors.IsNotFound(err) {
    95  			r.Log.Info("chaos not found")
    96  		} else {
    97  			// TODO: handle this error
    98  			r.Log.Error(err, "unable to get chaos")
    99  		}
   100  		return ctrl.Result{}, nil
   101  	}
   102  
   103  	finalizers := obj.GetFinalizers()
   104  	records := obj.GetStatus().Experiment.Records
   105  	if obj.IsDeleted() {
   106  		resumed := true
   107  		for _, record := range records {
   108  			if record.Phase != v1alpha1.NotInjected {
   109  				resumed = false
   110  			}
   111  		}
   112  
   113  		if obj.GetAnnotations()[AnnotationCleanFinalizer] == AnnotationCleanFinalizerForced || (resumed && len(finalizers) != 0) {
   114  			r.Recorder.Event(obj, recorder.FinalizerRemoved{})
   115  			finalizers = []string{}
   116  			return updateFinalizer(r.ReconcilerMeta, obj, req, finalizers)
   117  		}
   118  	}
   119  
   120  	return ctrl.Result{}, nil
   121  }
   122  
   123  func updateFinalizer(r ReconcilerMeta, obj v1alpha1.InnerObject, req ctrl.Request, finalizers []string) (ctrl.Result, error) {
   124  	updateError := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
   125  		obj := r.Object.DeepCopyObject().(v1alpha1.InnerObject)
   126  
   127  		if err := r.Client.Get(context.TODO(), req.NamespacedName, obj); err != nil {
   128  			r.Log.Error(err, "unable to get chaos")
   129  			return err
   130  		}
   131  
   132  		obj.SetFinalizers(finalizers)
   133  		return r.Client.Update(context.TODO(), obj)
   134  	})
   135  	if updateError != nil {
   136  		// TODO: handle this error
   137  		r.Log.Error(updateError, "fail to update")
   138  		r.Recorder.Event(obj, recorder.Failed{
   139  			Activity: "update finalizer",
   140  			Err:      "updateError.Error()",
   141  		})
   142  		return ctrl.Result{}, nil
   143  	}
   144  
   145  	r.Recorder.Event(obj, recorder.Updated{
   146  		Field: "finalizer",
   147  	})
   148  	return ctrl.Result{}, nil
   149  }
   150  
   151  // ContainsFinalizer checks an Object that the provided finalizer is present.
   152  func ContainsFinalizer(o metav1.Object, finalizer string) bool {
   153  	f := o.GetFinalizers()
   154  	for _, e := range f {
   155  		if e == finalizer {
   156  			return true
   157  		}
   158  	}
   159  	return false
   160  }
   161