github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controllerutil/volumesnapshot.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 controllerutil 21 22 import ( 23 "context" 24 "encoding/json" 25 26 snapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v3/apis/volumesnapshot/v1beta1" 27 snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" 28 corev1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/types" 30 "sigs.k8s.io/controller-runtime/pkg/client" 31 32 roclient "github.com/1aal/kubeblocks/pkg/controller/client" 33 viper "github.com/1aal/kubeblocks/pkg/viperx" 34 ) 35 36 func InVolumeSnapshotV1Beta1() bool { 37 return viper.GetBool("VOLUMESNAPSHOT_API_BETA") 38 } 39 40 // VolumeSnapshotCompatClient client is compatible with VolumeSnapshot v1 and v1beta1 41 type VolumeSnapshotCompatClient struct { 42 client.Client 43 roclient.ReadonlyClient 44 Ctx context.Context 45 } 46 47 func (c *VolumeSnapshotCompatClient) Create(obj client.Object, opts ...client.CreateOption) error { 48 if InVolumeSnapshotV1Beta1() { 49 objV1Beta1 := typeofV1Beta1(obj).(client.Object) 50 if err := convertObjectBetweenAPIVersion(obj, objV1Beta1); err != nil { 51 return err 52 } 53 return c.Client.Create(c.Ctx, objV1Beta1, opts...) 54 } 55 return c.Client.Create(c.Ctx, obj, opts...) 56 } 57 58 func (c *VolumeSnapshotCompatClient) Get(key client.ObjectKey, snapshot client.Object, opts ...client.GetOption) error { 59 if c.ReadonlyClient == nil { 60 c.ReadonlyClient = c.Client 61 } 62 if InVolumeSnapshotV1Beta1() { 63 snapshotV1Beta1 := typeofV1Beta1(snapshot).(client.Object) 64 err := c.ReadonlyClient.Get(c.Ctx, key, snapshotV1Beta1, opts...) 65 if err != nil { 66 return err 67 } 68 if err = convertObjectBetweenAPIVersion(snapshotV1Beta1, snapshot); err != nil { 69 return err 70 } 71 return nil 72 } 73 return c.ReadonlyClient.Get(c.Ctx, key, snapshot, opts...) 74 } 75 76 func (c *VolumeSnapshotCompatClient) Delete(snapshot client.Object) error { 77 if InVolumeSnapshotV1Beta1() { 78 snapshotV1Beta1 := typeofV1Beta1(snapshot).(client.Object) 79 if err := convertObjectBetweenAPIVersion(snapshot, snapshotV1Beta1); err != nil { 80 return err 81 } 82 return BackgroundDeleteObject(c.Client, c.Ctx, snapshotV1Beta1) 83 } 84 return BackgroundDeleteObject(c.Client, c.Ctx, snapshot) 85 } 86 87 func (c *VolumeSnapshotCompatClient) Patch(snapshot client.Object, deepCopy client.Object, opts ...client.PatchOption) error { 88 if InVolumeSnapshotV1Beta1() { 89 snapshotV1Beta1 := typeofV1Beta1(snapshot).(client.Object) 90 if err := convertObjectBetweenAPIVersion(snapshot, snapshotV1Beta1); err != nil { 91 return err 92 } 93 snapshotV1Beta1Patch := typeofV1Beta1(deepCopy).(client.Object) 94 if err := convertObjectBetweenAPIVersion(deepCopy, snapshotV1Beta1Patch); err != nil { 95 return err 96 } 97 patch := client.MergeFrom(snapshotV1Beta1Patch) 98 return c.Client.Patch(c.Ctx, snapshotV1Beta1, patch, opts...) 99 } 100 snapPatch := client.MergeFrom(deepCopy) 101 return c.Client.Patch(c.Ctx, snapshot, snapPatch, opts...) 102 } 103 104 func (c *VolumeSnapshotCompatClient) List(objList client.ObjectList, opts ...client.ListOption) error { 105 if c.ReadonlyClient == nil { 106 c.ReadonlyClient = c.Client 107 } 108 if InVolumeSnapshotV1Beta1() { 109 objV1Beta1List := typeofV1Beta1(objList).(client.ObjectList) 110 err := c.ReadonlyClient.List(c.Ctx, objV1Beta1List, opts...) 111 if err != nil { 112 return err 113 } 114 if err = convertObjectBetweenAPIVersion(objV1Beta1List, objList); err != nil { 115 return err 116 } 117 return nil 118 } 119 return c.ReadonlyClient.List(c.Ctx, objList, opts...) 120 } 121 122 // CheckResourceExists checks whether resource exist or not. 123 func (c *VolumeSnapshotCompatClient) CheckResourceExists(key client.ObjectKey, obj client.Object) (bool, error) { 124 if err := c.Get(key, obj); err != nil { 125 return false, client.IgnoreNotFound(err) 126 } 127 // if found, return true 128 return true, nil 129 } 130 131 func convertObjectBetweenAPIVersion[T1 any, T2 any](from T1, to T2) error { 132 fromJSONBytes, err := json.Marshal(from) 133 if err != nil { 134 return err 135 } 136 if err = json.Unmarshal(fromJSONBytes, to); err != nil { 137 return err 138 } 139 return nil 140 } 141 142 func typeofV1Beta1(v any) any { 143 switch v.(type) { 144 // object 145 case *snapshotv1.VolumeSnapshot: 146 return &snapshotv1beta1.VolumeSnapshot{} 147 case *snapshotv1.VolumeSnapshotClass: 148 return &snapshotv1beta1.VolumeSnapshotClass{} 149 case *snapshotv1beta1.VolumeSnapshot: 150 return &snapshotv1.VolumeSnapshot{} 151 case *snapshotv1beta1.VolumeSnapshotClass: 152 return &snapshotv1.VolumeSnapshotClass{} 153 // object list 154 case *snapshotv1.VolumeSnapshotList: 155 return &snapshotv1beta1.VolumeSnapshotList{} 156 case *snapshotv1.VolumeSnapshotClassList: 157 return &snapshotv1beta1.VolumeSnapshotClassList{} 158 case *snapshotv1beta1.VolumeSnapshotList: 159 return &snapshotv1.VolumeSnapshotList{} 160 case *snapshotv1beta1.VolumeSnapshotClassList: 161 return &snapshotv1.VolumeSnapshotClassList{} 162 default: 163 return nil 164 } 165 } 166 167 // IsVolumeSnapshotEnabled checks if the CSI supports the volume snapshot. 168 func IsVolumeSnapshotEnabled(ctx context.Context, cli client.Client, pvName string) (bool, error) { 169 if len(pvName) == 0 { 170 return false, nil 171 } 172 pv := &corev1.PersistentVolume{} 173 if err := cli.Get(ctx, types.NamespacedName{Name: pvName}, pv); err != nil { 174 return false, err 175 } 176 if pv.Spec.CSI == nil { 177 return false, nil 178 } 179 vsCli := VolumeSnapshotCompatClient{ 180 Client: cli, 181 Ctx: ctx, 182 } 183 vscList := snapshotv1.VolumeSnapshotClassList{} 184 if err := vsCli.List(&vscList); err != nil { 185 return false, err 186 } 187 for _, vsc := range vscList.Items { 188 if vsc.Driver == pv.Spec.CSI.Driver { 189 return true, nil 190 } 191 } 192 return false, nil 193 }