k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/storage/utils/deployment.go (about) 1 /* 2 Copyright 2018 The Kubernetes 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 17 package utils 18 19 import ( 20 "fmt" 21 "path" 22 "strings" 23 24 appsv1 "k8s.io/api/apps/v1" 25 v1 "k8s.io/api/core/v1" 26 storagev1 "k8s.io/api/storage/v1" 27 e2eframework "k8s.io/kubernetes/test/e2e/framework" 28 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 29 ) 30 31 // PatchCSIDeployment modifies the CSI driver deployment: 32 // - replaces the provisioner name 33 // - forces pods onto a specific host 34 // 35 // All of that is optional, see PatchCSIOptions. Just beware 36 // that not renaming the CSI driver deployment can be problematic: 37 // - when multiple tests deploy the driver, they need 38 // to run sequentially 39 // - might conflict with manual deployments 40 // 41 // This function is written so that it works for CSI driver deployments 42 // that follow these conventions: 43 // - driver and provisioner names are identical 44 // - the driver binary accepts a --drivername parameter 45 // - the paths inside the container are either fixed 46 // and don't need to be patch (for example, --csi-address=/csi/csi.sock is 47 // okay) or are specified directly in a parameter (for example, 48 // --kubelet-registration-path=/var/lib/kubelet/plugins/csi-hostpath/csi.sock) 49 // 50 // Driver deployments that are different will have to do the patching 51 // without this function, or skip patching entirely. 52 func PatchCSIDeployment(f *e2eframework.Framework, o PatchCSIOptions, object interface{}) error { 53 rename := o.OldDriverName != "" && o.NewDriverName != "" && 54 o.OldDriverName != o.NewDriverName 55 56 substKubeletRootDir := func(s string) string { 57 return strings.ReplaceAll(s, "/var/lib/kubelet/", e2eframework.TestContext.KubeletRootDir+"/") 58 } 59 60 patchVolumes := func(volumes []v1.Volume) { 61 if !rename { 62 return 63 } 64 for i := range volumes { 65 volume := &volumes[i] 66 if volume.HostPath != nil { 67 // Update paths like /var/lib/kubelet/plugins/<provisioner>. 68 p := &volume.HostPath.Path 69 dir, file := path.Split(*p) 70 if file == o.OldDriverName { 71 *p = path.Join(dir, o.NewDriverName) 72 } 73 // Inject non-standard kubelet path. 74 *p = substKubeletRootDir(*p) 75 } 76 } 77 } 78 79 patchContainers := func(containers []v1.Container) { 80 for i := range containers { 81 container := &containers[i] 82 if rename { 83 for e := range container.Args { 84 // Inject test-specific provider name into paths like this one: 85 // --kubelet-registration-path=/var/lib/kubelet/plugins/csi-hostpath/csi.sock 86 container.Args[e] = strings.Replace(container.Args[e], "/"+o.OldDriverName+"/", "/"+o.NewDriverName+"/", 1) 87 } 88 } 89 90 // Modify --kubelet-registration-path. 91 for e := range container.Args { 92 container.Args[e] = substKubeletRootDir(container.Args[e]) 93 } 94 for e := range container.VolumeMounts { 95 container.VolumeMounts[e].MountPath = substKubeletRootDir(container.VolumeMounts[e].MountPath) 96 } 97 98 if len(o.Features) > 0 && len(o.Features[container.Name]) > 0 { 99 featuregateString := strings.Join(o.Features[container.Name], ",") 100 container.Args = append(container.Args, fmt.Sprintf("--feature-gates=%s", featuregateString)) 101 } 102 103 // Overwrite driver name resp. provider name 104 // by appending a parameter with the right 105 // value. 106 switch container.Name { 107 case o.DriverContainerName: 108 container.Args = append(container.Args, o.DriverContainerArguments...) 109 } 110 } 111 } 112 113 patchPodSpec := func(spec *v1.PodSpec) { 114 patchContainers(spec.Containers) 115 patchVolumes(spec.Volumes) 116 if o.NodeName != "" { 117 e2epod.SetNodeSelection(spec, e2epod.NodeSelection{Name: o.NodeName}) 118 } 119 } 120 121 switch object := object.(type) { 122 case *appsv1.ReplicaSet: 123 patchPodSpec(&object.Spec.Template.Spec) 124 case *appsv1.DaemonSet: 125 patchPodSpec(&object.Spec.Template.Spec) 126 case *appsv1.StatefulSet: 127 patchPodSpec(&object.Spec.Template.Spec) 128 case *appsv1.Deployment: 129 patchPodSpec(&object.Spec.Template.Spec) 130 case *storagev1.StorageClass: 131 if o.NewDriverName != "" { 132 // Driver name is expected to be the same 133 // as the provisioner name here. 134 object.Provisioner = o.NewDriverName 135 } 136 case *storagev1.CSIDriver: 137 if o.NewDriverName != "" { 138 object.Name = o.NewDriverName 139 } 140 if o.PodInfo != nil { 141 object.Spec.PodInfoOnMount = o.PodInfo 142 } 143 if o.StorageCapacity != nil { 144 object.Spec.StorageCapacity = o.StorageCapacity 145 } 146 if o.CanAttach != nil { 147 object.Spec.AttachRequired = o.CanAttach 148 } 149 if o.VolumeLifecycleModes != nil { 150 object.Spec.VolumeLifecycleModes = *o.VolumeLifecycleModes 151 } 152 if o.TokenRequests != nil { 153 object.Spec.TokenRequests = o.TokenRequests 154 } 155 if o.RequiresRepublish != nil { 156 object.Spec.RequiresRepublish = o.RequiresRepublish 157 } 158 if o.FSGroupPolicy != nil { 159 object.Spec.FSGroupPolicy = o.FSGroupPolicy 160 } 161 if o.SELinuxMount != nil { 162 object.Spec.SELinuxMount = o.SELinuxMount 163 } 164 } 165 166 return nil 167 } 168 169 // PatchCSIOptions controls how PatchCSIDeployment patches the objects. 170 type PatchCSIOptions struct { 171 // The original driver name. 172 OldDriverName string 173 // The driver name that replaces the original name. 174 // Can be empty (not used at all) or equal to OldDriverName 175 // (then it will be added were appropriate without renaming 176 // in existing fields). 177 NewDriverName string 178 // The name of the container which has the CSI driver binary. 179 // If non-empty, DriverContainerArguments are added to argument 180 // list in container with that name. 181 DriverContainerName string 182 // List of arguments to add to container with 183 // DriverContainerName. 184 DriverContainerArguments []string 185 // The name of the container which has the provisioner binary. 186 // If non-empty, --provisioner with new name will be appended 187 // to the argument list. 188 ProvisionerContainerName string 189 // The name of the container which has the snapshotter binary. 190 // If non-empty, --snapshotter with new name will be appended 191 // to the argument list. 192 SnapshotterContainerName string 193 // If non-empty, all pods are forced to run on this node. 194 NodeName string 195 // If not nil, the value to use for the CSIDriver.Spec.PodInfo 196 // field *if* the driver deploys a CSIDriver object. Ignored 197 // otherwise. 198 PodInfo *bool 199 // If not nil, the value to use for the CSIDriver.Spec.CanAttach 200 // field *if* the driver deploys a CSIDriver object. Ignored 201 // otherwise. 202 CanAttach *bool 203 // If not nil, the value to use for the CSIDriver.Spec.StorageCapacity 204 // field *if* the driver deploys a CSIDriver object. Ignored 205 // otherwise. 206 StorageCapacity *bool 207 // If not nil, the value to use for the CSIDriver.Spec.VolumeLifecycleModes 208 // field *if* the driver deploys a CSIDriver object. Ignored 209 // otherwise. 210 VolumeLifecycleModes *[]storagev1.VolumeLifecycleMode 211 // If not nil, the value to use for the CSIDriver.Spec.TokenRequests 212 // field *if* the driver deploys a CSIDriver object. Ignored 213 // otherwise. 214 TokenRequests []storagev1.TokenRequest 215 // If not nil, the value to use for the CSIDriver.Spec.RequiresRepublish 216 // field *if* the driver deploys a CSIDriver object. Ignored 217 // otherwise. 218 RequiresRepublish *bool 219 // If not nil, the value to use for the CSIDriver.Spec.FSGroupPolicy 220 // field *if* the driver deploys a CSIDriver object. Ignored 221 // otherwise. 222 FSGroupPolicy *storagev1.FSGroupPolicy 223 // If not nil, the value to use for the CSIDriver.Spec.SELinuxMount 224 // field *if* the driver deploys a CSIDriver object. Ignored 225 // otherwise. 226 SELinuxMount *bool 227 // If not nil, the values will be used for setting feature arguments to 228 // specific sidecar. 229 // Feature is a map - where key is sidecar name such as: 230 // -- key: resizer 231 // -- value: []string{feature-gates} 232 Features map[string][]string 233 }