...

Source file src/k8s.io/kubernetes/pkg/volume/csi/expander_test.go

Documentation: k8s.io/kubernetes/pkg/volume/csi

     1  /*
     2  Copyright 2019 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 csi
    18  
    19  import (
    20  	"context"
    21  	"os"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"google.golang.org/grpc/codes"
    26  	"google.golang.org/grpc/status"
    27  	api "k8s.io/api/core/v1"
    28  	"k8s.io/apimachinery/pkg/api/resource"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/kubernetes/pkg/volume"
    31  	volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
    32  )
    33  
    34  func TestNodeExpand(t *testing.T) {
    35  	tests := []struct {
    36  		name                      string
    37  		nodeExpansion             bool
    38  		nodeStageSet              bool
    39  		success                   bool
    40  		fsVolume                  bool
    41  		grpcError                 error
    42  		hasVolumeInUseError       bool
    43  		deviceStagePath           string
    44  		enableCSINodeExpandSecret bool
    45  		secret                    *api.Secret
    46  	}{
    47  		{
    48  			name:    "when node expansion is not set",
    49  			success: false,
    50  		},
    51  		{
    52  			name:            "when nodeExpansion=on, nodeStage=on, volumePhase=staged",
    53  			nodeExpansion:   true,
    54  			nodeStageSet:    true,
    55  			success:         true,
    56  			fsVolume:        true,
    57  			deviceStagePath: "/foo/bar",
    58  		},
    59  		{
    60  			name:          "when nodeExpansion=on, nodeStage=on, volumePhase=published",
    61  			nodeExpansion: true,
    62  			nodeStageSet:  true,
    63  			success:       true,
    64  			fsVolume:      true,
    65  		},
    66  		{
    67  			name:          "when nodeExpansion=on, nodeStage=off, volumePhase=published",
    68  			nodeExpansion: true,
    69  			success:       true,
    70  			fsVolume:      true,
    71  		},
    72  		{
    73  			name:          "when nodeExpansion=on, nodeStage=off, volumePhase=published, fsVolume=false",
    74  			nodeExpansion: true,
    75  			success:       true,
    76  			fsVolume:      false,
    77  		},
    78  		{
    79  			name:                "when nodeExpansion=on, nodeStage=on, volumePhase=published has grpc volume-in-use error",
    80  			nodeExpansion:       true,
    81  			nodeStageSet:        true,
    82  			success:             false,
    83  			fsVolume:            true,
    84  			grpcError:           status.Error(codes.FailedPrecondition, "volume-in-use"),
    85  			hasVolumeInUseError: true,
    86  		},
    87  		{
    88  			name:                "when nodeExpansion=on, nodeStage=on, volumePhase=published has other grpc error",
    89  			nodeExpansion:       true,
    90  			nodeStageSet:        true,
    91  			success:             false,
    92  			fsVolume:            true,
    93  			grpcError:           status.Error(codes.InvalidArgument, "invalid-argument"),
    94  			hasVolumeInUseError: false,
    95  		},
    96  		{
    97  			name:                      "when nodeExpansion=on, nodeStage=on, volumePhase=staged",
    98  			nodeExpansion:             true,
    99  			nodeStageSet:              true,
   100  			success:                   true,
   101  			fsVolume:                  true,
   102  			deviceStagePath:           "/foo/bar",
   103  			enableCSINodeExpandSecret: true,
   104  			secret: &api.Secret{
   105  				ObjectMeta: metav1.ObjectMeta{
   106  					Name:      "expand-secret",
   107  					Namespace: "default",
   108  				},
   109  				Data: map[string][]byte{
   110  					"apiUsername": []byte("csiusername"),
   111  					"apiPassword": []byte("csipassword"),
   112  				},
   113  			},
   114  		},
   115  	}
   116  	for _, tc := range tests {
   117  		t.Run(tc.name, func(t *testing.T) {
   118  			plug, tmpDir := newTestPlugin(t, nil)
   119  			defer os.RemoveAll(tmpDir)
   120  
   121  			spec := volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "expandable", "test-vol"), false)
   122  			if tc.enableCSINodeExpandSecret {
   123  				spec.PersistentVolume.Spec.CSI.NodeExpandSecretRef = &api.SecretReference{
   124  					Name:      tc.secret.Name,
   125  					Namespace: tc.secret.Namespace,
   126  				}
   127  			}
   128  
   129  			newSize, _ := resource.ParseQuantity("20Gi")
   130  
   131  			resizeOptions := volume.NodeResizeOptions{
   132  				VolumeSpec:      spec,
   133  				NewSize:         newSize,
   134  				DeviceMountPath: "/foo/bar",
   135  				DeviceStagePath: "/foo/bar",
   136  				DevicePath:      "/mnt/foobar",
   137  			}
   138  			csiSource, _ := getCSISourceFromSpec(resizeOptions.VolumeSpec)
   139  			csClient := setupClientWithExpansion(t, tc.nodeStageSet, tc.nodeExpansion)
   140  
   141  			fakeCSIClient, _ := csClient.(*fakeCsiDriverClient)
   142  			fakeNodeClient := fakeCSIClient.nodeClient
   143  
   144  			if tc.enableCSINodeExpandSecret {
   145  				_, err := plug.host.GetKubeClient().CoreV1().Secrets(tc.secret.Namespace).Create(context.TODO(), tc.secret, metav1.CreateOptions{})
   146  				if err != nil {
   147  					t.Fatal(err)
   148  				}
   149  			}
   150  
   151  			if tc.grpcError != nil {
   152  				fakeNodeClient.SetNextError(tc.grpcError)
   153  			}
   154  
   155  			ok, err := plug.nodeExpandWithClient(resizeOptions, csiSource, csClient, tc.fsVolume)
   156  
   157  			if tc.hasVolumeInUseError {
   158  				if !volumetypes.IsFailedPreconditionError(err) {
   159  					t.Errorf("expected failed precondition error got: %v", err)
   160  				}
   161  			}
   162  
   163  			// verify device staging target path
   164  			stagingTargetPath := fakeNodeClient.FakeNodeExpansionRequest.GetStagingTargetPath()
   165  			if tc.deviceStagePath != "" && tc.deviceStagePath != stagingTargetPath {
   166  				t.Errorf("For %s: expected staging path %s got %s", tc.name, tc.deviceStagePath, stagingTargetPath)
   167  			}
   168  
   169  			if ok != tc.success {
   170  				if err != nil {
   171  					t.Errorf("For %s : expected %v got %v with %v", tc.name, tc.success, ok, err)
   172  				} else {
   173  					t.Errorf("For %s : expected %v got %v", tc.name, tc.success, ok)
   174  				}
   175  			}
   176  			// verify volume capability received by node expansion request
   177  			if tc.success {
   178  				capability := fakeNodeClient.FakeNodeExpansionRequest.GetVolumeCapability()
   179  				if tc.fsVolume {
   180  					if capability.GetMount() == nil {
   181  						t.Errorf("For %s: expected mount accesstype got: %v", tc.name, capability)
   182  					}
   183  				} else {
   184  					if capability.GetBlock() == nil {
   185  						t.Errorf("For %s: expected block accesstype got: %v", tc.name, capability)
   186  					}
   187  				}
   188  			}
   189  		})
   190  	}
   191  }
   192  
   193  func TestNodeExpandNoClientError(t *testing.T) {
   194  	transientError := volumetypes.NewTransientOperationFailure("")
   195  	plug, tmpDir := newTestPlugin(t, nil)
   196  	defer os.RemoveAll(tmpDir)
   197  	spec := volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "expandable", "test-vol"), false)
   198  
   199  	newSize, _ := resource.ParseQuantity("20Gi")
   200  
   201  	resizeOptions := volume.NodeResizeOptions{
   202  		VolumeSpec:      spec,
   203  		NewSize:         newSize,
   204  		DeviceMountPath: "/foo/bar",
   205  		DeviceStagePath: "/foo/bar",
   206  		DevicePath:      "/mnt/foobar",
   207  	}
   208  
   209  	_, err := plug.NodeExpand(resizeOptions)
   210  
   211  	if err == nil {
   212  		t.Errorf("test should fail, but no error occurred")
   213  	} else if reflect.TypeOf(transientError) != reflect.TypeOf(err) {
   214  		t.Fatalf("expected exitError type: %v got: %v (%v)", reflect.TypeOf(transientError), reflect.TypeOf(err), err)
   215  	}
   216  }
   217  

View as plain text