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 }