...

Source file src/k8s.io/kubernetes/test/e2e/storage/utils/deployment.go

Documentation: k8s.io/kubernetes/test/e2e/storage/utils

     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  }
   234  

View as plain text