github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/cleaner/cleaner.go (about)

     1  /*
     2  Copyright 2019 OpenEBS Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  
    16  This pkg is inspired from the deleter pkg in local-static-provisioner
    17  in kubernetes-sigs
    18  	https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner/tree/master/pkg/deleter
    19  */
    20  
    21  package cleaner
    22  
    23  import (
    24  	"context"
    25  	"github.com/openebs/node-disk-manager/api/v1alpha1"
    26  	"sigs.k8s.io/controller-runtime/pkg/client"
    27  
    28  	v1 "k8s.io/api/core/v1"
    29  )
    30  
    31  // CleanupState represents the current state of the cleanup job
    32  type CleanupState int
    33  
    34  const (
    35  	// CleanupStateUnknown represents an unknown state of the cleanup job
    36  	CleanupStateUnknown CleanupState = iota + 1
    37  	// CleanupStateNotFound defines the state when a job does not exist
    38  	CleanupStateNotFound
    39  	// CleanupStateRunning represents a running cleanup job
    40  	CleanupStateRunning
    41  	// CleanupStateSucceeded represents that the cleanup job has been completed successfully
    42  	CleanupStateSucceeded
    43  )
    44  
    45  // VolumeMode defines the volume mode of the BlockDevice. It can be either block mode or
    46  // filesystem mode
    47  type VolumeMode string
    48  
    49  const (
    50  	// VolumeModeBlock defines a raw block volume mode which means the block device should
    51  	// be treated as raw block device
    52  	VolumeModeBlock = "BlockVolumeMode"
    53  	// VolumeModeFileSystem defines that the blockdevice should be treated as a block
    54  	// formatted with filesystem and is mounted
    55  	VolumeModeFileSystem = "FileSystemVolumeMode"
    56  )
    57  
    58  // Cleaner handles BD cleanup
    59  // For filesystem/mount based block devices, it deletes the contents of the directory
    60  // For raw block devices, a `wipefs` command will be issued.
    61  type Cleaner struct {
    62  	Client        client.Client
    63  	Namespace     string
    64  	CleanupStatus *CleanupStatusTracker
    65  }
    66  
    67  // CleanupStatusTracker is used to track the cleanup state using
    68  // info provided by JobController
    69  type CleanupStatusTracker struct {
    70  	JobController JobController
    71  }
    72  
    73  // NewCleaner creates a new cleaner which can be used for cleaning BD, and checking status of the job
    74  func NewCleaner(client client.Client, namespace string, cleanupTracker *CleanupStatusTracker) *Cleaner {
    75  	return &Cleaner{
    76  		Client:        client,
    77  		Namespace:     namespace,
    78  		CleanupStatus: cleanupTracker,
    79  	}
    80  }
    81  
    82  // Clean will launch a job to delete data on the BD depending on the
    83  // volume mode. Job will be launched only if another job is not running or a
    84  // job is in unknown state
    85  func (c *Cleaner) Clean(blockDevice *v1alpha1.BlockDevice) (bool, error) {
    86  	bdName := blockDevice.Name
    87  	// check if a cleanup job for the bd already exists and return
    88  	if c.CleanupStatus.InProgress(bdName) {
    89  		// check if the BD for which the cleanup is performed is still active,
    90  		// if not, cancel the cleanup job
    91  		if blockDevice.Status.State != v1alpha1.BlockDeviceActive {
    92  			// cancel the job
    93  			if err := c.CleanupStatus.CancelJob(bdName); err != nil {
    94  				return false, err
    95  			}
    96  		}
    97  		return false, nil
    98  	}
    99  	// Check if cleaning was just completed. if job was completed, it will be removed,
   100  	// The state of the job will be returned.
   101  	state, err := c.CleanupStatus.RemoveStatus(bdName)
   102  	if err != nil {
   103  		return false, nil
   104  	}
   105  
   106  	switch state {
   107  	case CleanupStateSucceeded:
   108  		return true, nil
   109  	case CleanupStateNotFound:
   110  		// if the BD is not active, do not start the job
   111  		if blockDevice.Status.State != v1alpha1.BlockDeviceActive {
   112  			return false, nil
   113  		}
   114  		// we are starting the job, since no job for the BD was found and
   115  		// BD is in active state
   116  	}
   117  
   118  	volMode := getVolumeMode(blockDevice.Spec)
   119  
   120  	// create a new job for the blockdevice
   121  	err = c.runJob(blockDevice, volMode)
   122  
   123  	return false, err
   124  }
   125  
   126  // InProgress returns whether a cleanup job is currently being done
   127  // for the given BD
   128  func (c *CleanupStatusTracker) InProgress(bdName string) bool {
   129  	return c.JobController.IsCleaningJobRunning(bdName)
   130  }
   131  
   132  // RemoveStatus returns the Cleanupstate of a job. If job is succeeded, it will
   133  // be deleted
   134  func (c *CleanupStatusTracker) RemoveStatus(bdName string) (CleanupState, error) {
   135  	return c.JobController.RemoveJob(bdName)
   136  }
   137  
   138  // CancelJob cancels a job without checking the status of the job.
   139  func (c *CleanupStatusTracker) CancelJob(bdName string) error {
   140  	return c.JobController.CancelJob(bdName)
   141  }
   142  
   143  // runJob creates a new cleanup job in the namespace
   144  func (c *Cleaner) runJob(bd *v1alpha1.BlockDevice, volumeMode VolumeMode) error {
   145  
   146  	// retrieve node Object to pass tolerations to the Job
   147  	nodeName := GetNodeName(bd)
   148  	selectedNode, err := c.getNodeObjectByNodeName(nodeName)
   149  	if err != nil {
   150  		return err
   151  	}
   152  	tolerations := getTolerationsForTaints(selectedNode.Spec.Taints...)
   153  
   154  	job, err := NewCleanupJob(bd, volumeMode, tolerations, c.Namespace)
   155  	if err != nil {
   156  		return err
   157  	}
   158  	return c.Client.Create(context.TODO(), job)
   159  }
   160  
   161  // getNodeObjectByNodeName returns Node Object, using NodeName
   162  func (c *Cleaner) getNodeObjectByNodeName(nodeName string) (*v1.Node, error) {
   163  	node := &v1.Node{}
   164  	err := c.Client.Get(context.TODO(), client.ObjectKey{Namespace: "", Name: nodeName}, node)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	return node, nil
   169  }
   170  
   171  // getTolerationsForTaints returns tolerations, taking input as taints
   172  func getTolerationsForTaints(taints ...v1.Taint) []v1.Toleration {
   173  	tolerations := []v1.Toleration{}
   174  	for i := range taints {
   175  		var toleration v1.Toleration
   176  		toleration.Key = taints[i].Key
   177  		toleration.Effect = taints[i].Effect
   178  		if len(taints[i].Value) == 0 {
   179  			toleration.Operator = v1.TolerationOpExists
   180  		} else {
   181  			toleration.Value = taints[i].Value
   182  			toleration.Operator = v1.TolerationOpEqual
   183  		}
   184  		tolerations = append(tolerations, toleration)
   185  	}
   186  	return tolerations
   187  }
   188  
   189  // getVolumeMode returns the volume mode of the BD that is being released
   190  func getVolumeMode(spec v1alpha1.DeviceSpec) VolumeMode {
   191  	if spec.FileSystem.Mountpoint != "" {
   192  		return VolumeModeFileSystem
   193  	}
   194  	return VolumeModeBlock
   195  }