github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/dataprotection/utils/utils.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package utils 21 22 import ( 23 "context" 24 "fmt" 25 "reflect" 26 27 batchv1 "k8s.io/api/batch/v1" 28 corev1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/types" 32 "k8s.io/apimachinery/pkg/util/json" 33 "sigs.k8s.io/controller-runtime/pkg/client" 34 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 35 36 dpv1alpha1 "github.com/1aal/kubeblocks/apis/dataprotection/v1alpha1" 37 "github.com/1aal/kubeblocks/pkg/constant" 38 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 39 dptypes "github.com/1aal/kubeblocks/pkg/dataprotection/types" 40 "github.com/1aal/kubeblocks/pkg/dataprotection/utils/boolptr" 41 viper "github.com/1aal/kubeblocks/pkg/viperx" 42 ) 43 44 func AddTolerations(podSpec *corev1.PodSpec) (err error) { 45 if cmTolerations := viper.GetString(constant.CfgKeyCtrlrMgrTolerations); cmTolerations != "" { 46 if err = json.Unmarshal([]byte(cmTolerations), &podSpec.Tolerations); err != nil { 47 return err 48 } 49 } 50 if cmAffinity := viper.GetString(constant.CfgKeyCtrlrMgrAffinity); cmAffinity != "" { 51 if err = json.Unmarshal([]byte(cmAffinity), &podSpec.Affinity); err != nil { 52 return err 53 } 54 } 55 if cmNodeSelector := viper.GetString(constant.CfgKeyCtrlrMgrNodeSelector); cmNodeSelector != "" { 56 if err = json.Unmarshal([]byte(cmNodeSelector), &podSpec.NodeSelector); err != nil { 57 return err 58 } 59 } 60 return nil 61 } 62 63 // IsJobFinished if the job is completed or failed, return true. 64 // if the job is failed, return the failed message. 65 func IsJobFinished(job *batchv1.Job) (bool, batchv1.JobConditionType, string) { 66 if job == nil { 67 return false, "", "" 68 } 69 for _, c := range job.Status.Conditions { 70 if c.Status != corev1.ConditionTrue { 71 continue 72 } 73 if c.Type == batchv1.JobComplete { 74 return true, c.Type, "" 75 } 76 if c.Type == batchv1.JobFailed { 77 return true, c.Type, c.Reason + ":" + c.Message 78 } 79 } 80 return false, "", "" 81 } 82 83 func GetAssociatedPodsOfJob(ctx context.Context, cli client.Client, namespace, jobName string) (*corev1.PodList, error) { 84 podList := &corev1.PodList{} 85 // from https://github.com/kubernetes/kubernetes/issues/24709 86 err := cli.List(ctx, podList, client.InNamespace(namespace), client.MatchingLabels{ 87 "job-name": jobName, 88 }) 89 return podList, err 90 } 91 92 func RemoveDataProtectionFinalizer(ctx context.Context, cli client.Client, obj client.Object) error { 93 if !controllerutil.ContainsFinalizer(obj, dptypes.DataProtectionFinalizerName) { 94 return nil 95 } 96 patch := client.MergeFrom(obj.DeepCopyObject().(client.Object)) 97 controllerutil.RemoveFinalizer(obj, dptypes.DataProtectionFinalizerName) 98 return cli.Patch(ctx, obj, patch) 99 } 100 101 // GetActionSetByName gets the ActionSet by name. 102 func GetActionSetByName(reqCtx intctrlutil.RequestCtx, 103 cli client.Client, name string) (*dpv1alpha1.ActionSet, error) { 104 if name == "" { 105 return nil, nil 106 } 107 as := &dpv1alpha1.ActionSet{} 108 if err := cli.Get(reqCtx.Ctx, client.ObjectKey{Name: name}, as); err != nil { 109 reqCtx.Log.Error(err, "failed to get ActionSet for backup.", "ActionSet", name) 110 return nil, err 111 } 112 return as, nil 113 } 114 115 func GetPodListByLabelSelector(reqCtx intctrlutil.RequestCtx, 116 cli client.Client, 117 labelSelector metav1.LabelSelector) (*corev1.PodList, error) { 118 selector, err := metav1.LabelSelectorAsSelector(&labelSelector) 119 if err != nil { 120 return nil, err 121 } 122 targetPodList := &corev1.PodList{} 123 if err = cli.List(reqCtx.Ctx, targetPodList, 124 client.InNamespace(reqCtx.Req.Namespace), 125 client.MatchingLabelsSelector{Selector: selector}); err != nil { 126 return nil, err 127 } 128 return targetPodList, nil 129 } 130 131 func GetBackupVolumeSnapshotName(backupName, volumeSource string) string { 132 return fmt.Sprintf("%s-%s", backupName, volumeSource) 133 } 134 135 // MergeEnv merges the targetEnv to original env. if original env exist the same name var, it will be replaced. 136 func MergeEnv(originalEnv, targetEnv []corev1.EnvVar) []corev1.EnvVar { 137 if len(targetEnv) == 0 { 138 return originalEnv 139 } 140 originalEnvIndexMap := map[string]int{} 141 for i := range originalEnv { 142 originalEnvIndexMap[originalEnv[i].Name] = i 143 } 144 for i := range targetEnv { 145 if index, ok := originalEnvIndexMap[targetEnv[i].Name]; ok { 146 originalEnv[index] = targetEnv[i] 147 } else { 148 originalEnv = append(originalEnv, targetEnv[i]) 149 } 150 } 151 return originalEnv 152 } 153 154 // VolumeSnapshotEnabled checks if the volumes support snapshot. 155 func VolumeSnapshotEnabled(ctx context.Context, cli client.Client, pod *corev1.Pod, volumes []string) (bool, error) { 156 if pod == nil { 157 return false, nil 158 } 159 var pvcNames []string 160 // get the pvcs by volumes 161 for _, v := range pod.Spec.Volumes { 162 for i := range volumes { 163 if v.Name != volumes[i] { 164 continue 165 } 166 if v.PersistentVolumeClaim == nil { 167 return false, fmt.Errorf(`the type of volume "%s" is not PersistentVolumeClaim on pod "%s"`, v.Name, pod.Name) 168 } 169 pvcNames = append(pvcNames, v.PersistentVolumeClaim.ClaimName) 170 } 171 } 172 if len(pvcNames) == 0 { 173 return false, fmt.Errorf(`can not find any volume by targetVolumes %v on pod "%s"`, volumes, pod.Name) 174 } 175 // get the storageClass by pvc 176 for i := range pvcNames { 177 pvc := &corev1.PersistentVolumeClaim{} 178 if err := cli.Get(ctx, types.NamespacedName{Name: pvcNames[i], Namespace: pod.Namespace}, pvc); err != nil { 179 return false, nil 180 } 181 enabled, err := intctrlutil.IsVolumeSnapshotEnabled(ctx, cli, pvc.Spec.VolumeName) 182 if err != nil { 183 return false, err 184 } 185 if !enabled { 186 return false, fmt.Errorf(`cannot find any VolumeSnapshotClass of persistentVolumeClaim "%s" to do volume snapshot on pod "%s"`, pvc.Name, pod.Name) 187 } 188 } 189 return true, nil 190 } 191 192 func SetControllerReference(owner, controlled metav1.Object, scheme *runtime.Scheme) error { 193 if owner == nil || reflect.ValueOf(owner).IsNil() { 194 return nil 195 } 196 return controllerutil.SetControllerReference(owner, controlled, scheme) 197 } 198 199 // CovertEnvToMap coverts env array to map. 200 func CovertEnvToMap(env []corev1.EnvVar) map[string]string { 201 envMap := map[string]string{} 202 for _, v := range env { 203 if v.ValueFrom != nil { 204 continue 205 } 206 envMap[v.Name] = v.Value 207 } 208 return envMap 209 } 210 211 func GetBackupType(actionSet *dpv1alpha1.ActionSet, useSnapshot *bool) dpv1alpha1.BackupType { 212 if actionSet != nil { 213 return actionSet.Spec.BackupType 214 } else if boolptr.IsSetToTrue(useSnapshot) { 215 return dpv1alpha1.BackupTypeFull 216 } 217 return "" 218 }