...

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

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

     1  /*
     2  Copyright 2024 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 storage
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"math/rand"
    23  	"time"
    24  
    25  	storagev1 "k8s.io/api/storage/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/labels"
    28  	"k8s.io/apimachinery/pkg/types"
    29  	utilrand "k8s.io/apimachinery/pkg/util/rand"
    30  	"k8s.io/client-go/util/retry"
    31  	"k8s.io/kubernetes/test/e2e/framework"
    32  	"k8s.io/kubernetes/test/e2e/storage/utils"
    33  
    34  	"github.com/onsi/ginkgo/v2"
    35  	"github.com/onsi/gomega"
    36  )
    37  
    38  var _ = utils.SIGDescribe("VolumeAttachment", func() {
    39  
    40  	f := framework.NewDefaultFramework("volumeattachment")
    41  
    42  	/*
    43  		Release: v1.30
    44  		Testname: VolumeAttachment, lifecycle
    45  		Description: Creating an initial VolumeAttachment MUST succeed. Reading the VolumeAttachment
    46  		MUST succeed with with required name retrieved. Patching a VolumeAttachment MUST
    47  		succeed with its new label found. Listing VolumeAttachment with a labelSelector
    48  		MUST succeed with a single item retrieved. Deleting a VolumeAttachment MUST succeed
    49  		and it MUST be confirmed. Creating a second VolumeAttachment MUST succeed. Updating
    50  		the second VolumentAttachment with a new label MUST succeed with its new label
    51  		found. Creating a third VolumeAttachment MUST succeed. Updating the third VolumentAttachment
    52  		with a new label MUST succeed with its new label found. Deleting both VolumeAttachments
    53  		via deleteCollection MUST succeed and it MUST be confirmed.
    54  	*/
    55  	ginkgo.Describe("Conformance", func() {
    56  
    57  		framework.ConformanceIt("should run through the lifecycle of a VolumeAttachment", func(ctx context.Context) {
    58  
    59  			vaClient := f.ClientSet.StorageV1().VolumeAttachments()
    60  
    61  			firstVA, vaNodeName := createVolumeAttachment(f, ctx)
    62  			ginkgo.By(fmt.Sprintf("Get VolumeAttachment %q on node %q", firstVA, vaNodeName))
    63  			retrievedVA, err := vaClient.Get(ctx, firstVA, metav1.GetOptions{})
    64  			framework.ExpectNoError(err, "failed to get VolumeAttachment %q", firstVA)
    65  			gomega.Expect(retrievedVA.Name).To(gomega.Equal(firstVA), "Checking that retrieved VolumeAttachment has the correct name")
    66  
    67  			ginkgo.By(fmt.Sprintf("Patch VolumeAttachment %q on node %q", firstVA, vaNodeName))
    68  			payload := "{\"metadata\":{\"labels\":{\"" + retrievedVA.Name + "\":\"patched\"}}}"
    69  			patchedVA, err := vaClient.Patch(ctx, retrievedVA.Name, types.MergePatchType, []byte(payload), metav1.PatchOptions{})
    70  			framework.ExpectNoError(err, "failed to patch PV %q", firstVA)
    71  			gomega.Expect(patchedVA.Labels).To(gomega.HaveKeyWithValue(patchedVA.Name, "patched"), "Checking that patched label has been applied")
    72  
    73  			patchedSelector := labels.Set{patchedVA.Name: "patched"}.AsSelector().String()
    74  			ginkgo.By(fmt.Sprintf("List VolumeAttachments with %q label", patchedSelector))
    75  			vaList, err := vaClient.List(ctx, metav1.ListOptions{LabelSelector: patchedSelector})
    76  			framework.ExpectNoError(err, "failed to list VolumeAttachments")
    77  			gomega.Expect(vaList.Items).To(gomega.HaveLen(1))
    78  
    79  			ginkgo.By(fmt.Sprintf("Delete VolumeAttachment %q on node %q", firstVA, vaNodeName))
    80  			err = vaClient.Delete(ctx, firstVA, metav1.DeleteOptions{})
    81  			framework.ExpectNoError(err, "failed to delete VolumeAttachment %q", firstVA)
    82  
    83  			ginkgo.By(fmt.Sprintf("Confirm deletion of VolumeAttachment %q on node %q", firstVA, vaNodeName))
    84  
    85  			type state struct {
    86  				VolumeAttachments []storagev1.VolumeAttachment
    87  			}
    88  
    89  			err = framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*state, error) {
    90  				vaList, err := vaClient.List(ctx, metav1.ListOptions{LabelSelector: patchedSelector})
    91  				if err != nil {
    92  					return nil, fmt.Errorf("failed to list VolumeAttachment: %w", err)
    93  				}
    94  				return &state{
    95  					VolumeAttachments: vaList.Items,
    96  				}, nil
    97  			})).WithTimeout(30 * time.Second).Should(framework.MakeMatcher(func(s *state) (func() string, error) {
    98  				if len(s.VolumeAttachments) == 0 {
    99  					return nil, nil
   100  				}
   101  				return func() string {
   102  					return fmt.Sprintf("Expected VolumeAttachment to be deleted, found %q", s.VolumeAttachments[0].Name)
   103  				}, nil
   104  			}))
   105  			framework.ExpectNoError(err, "Timeout while waiting to confirm VolumeAttachment %q deletion", firstVA)
   106  
   107  			secondVA, vaNodeName := createVolumeAttachment(f, ctx)
   108  			updatedLabel := map[string]string{"va-e2e": "updated"}
   109  			updatedSelector := labels.Set{"va-e2e": "updated"}.AsSelector().String()
   110  			ginkgo.By(fmt.Sprintf("Update the VolumeAttachment %q on node %q with label %q", secondVA, vaNodeName, updatedSelector))
   111  			var updatedVA *storagev1.VolumeAttachment
   112  
   113  			err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
   114  				currentVA, err := vaClient.Get(ctx, secondVA, metav1.GetOptions{})
   115  				framework.ExpectNoError(err, "failed to get VolumeAttachment %q", patchedVA.Name)
   116  				currentVA.Labels = updatedLabel
   117  				updatedVA, err = vaClient.Update(ctx, currentVA, metav1.UpdateOptions{})
   118  
   119  				return err
   120  			})
   121  			framework.ExpectNoError(err, "failed to update VolumeAttachment %q on node %q", secondVA, vaNodeName)
   122  			gomega.Expect(updatedVA.Labels).To(gomega.HaveKeyWithValue("va-e2e", "updated"), "Checking that updated label has been applied")
   123  
   124  			thirdVA, vaNodeName := createVolumeAttachment(f, ctx)
   125  			ginkgo.By(fmt.Sprintf("Update the VolumeAttachment %q on node %q with label %q", thirdVA, vaNodeName, updatedSelector))
   126  
   127  			err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
   128  				currentVA, err := vaClient.Get(ctx, thirdVA, metav1.GetOptions{})
   129  				framework.ExpectNoError(err, "failed to get VolumeAttachment %q", patchedVA.Name)
   130  				currentVA.Labels = updatedLabel
   131  				updatedVA, err = vaClient.Update(ctx, currentVA, metav1.UpdateOptions{})
   132  
   133  				return err
   134  			})
   135  			framework.ExpectNoError(err, "failed to update VolumeAttachment %q on node %q", thirdVA, vaNodeName)
   136  			gomega.Expect(updatedVA.Labels).To(gomega.HaveKeyWithValue("va-e2e", "updated"), "Checking that updated label has been applied")
   137  
   138  			ginkgo.By(fmt.Sprintf("DeleteCollection of VolumeAttachments with %q label", updatedSelector))
   139  			err = vaClient.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: updatedSelector})
   140  			framework.ExpectNoError(err, "failed to delete VolumeAttachment collection")
   141  
   142  			ginkgo.By(fmt.Sprintf("Confirm deleteCollection of VolumeAttachments with %q label", updatedSelector))
   143  
   144  			err = framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*state, error) {
   145  				vaList, err := vaClient.List(ctx, metav1.ListOptions{LabelSelector: updatedSelector})
   146  				if err != nil {
   147  					return nil, fmt.Errorf("failed to list VolumeAttachment: %w", err)
   148  				}
   149  				return &state{
   150  					VolumeAttachments: vaList.Items,
   151  				}, nil
   152  			})).WithTimeout(30 * time.Second).Should(framework.MakeMatcher(func(s *state) (func() string, error) {
   153  				if len(s.VolumeAttachments) == 0 {
   154  					return nil, nil
   155  				}
   156  				return func() string {
   157  					list := []string{}
   158  					for _, va := range s.VolumeAttachments {
   159  						list = append(list, va.Name)
   160  					}
   161  
   162  					return fmt.Sprintf("Expected VolumeAttachment(s) to be deleted, found %v", list)
   163  				}, nil
   164  			}))
   165  			framework.ExpectNoError(err, "Timeout while waiting to confirm deletion of all VolumeAttachments")
   166  		})
   167  	})
   168  })
   169  
   170  func NewVolumeAttachment(vaName, pvName, nodeName string, status bool) *storagev1.VolumeAttachment {
   171  	return &storagev1.VolumeAttachment{
   172  
   173  		ObjectMeta: metav1.ObjectMeta{
   174  			UID:  types.UID(vaName),
   175  			Name: vaName,
   176  		},
   177  		Spec: storagev1.VolumeAttachmentSpec{
   178  			Attacher: "e2e-test.storage.k8s.io",
   179  			NodeName: nodeName,
   180  			Source: storagev1.VolumeAttachmentSource{
   181  				PersistentVolumeName: &pvName,
   182  			},
   183  		},
   184  		Status: storagev1.VolumeAttachmentStatus{
   185  			Attached: status,
   186  		},
   187  	}
   188  }
   189  
   190  func createVolumeAttachment(f *framework.Framework, ctx context.Context) (string, string) {
   191  
   192  	randUID := "e2e-" + utilrand.String(5)
   193  	vaName := "va-" + randUID
   194  	pvName := "pv-" + randUID
   195  
   196  	nodes, err := f.ClientSet.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
   197  	framework.ExpectNoError(err, "failed to list nodes")
   198  	randNode := rand.Intn(len(nodes.Items))
   199  	vaNodeName := nodes.Items[randNode].Name
   200  	vaAttachStatus := false
   201  
   202  	ginkgo.By(fmt.Sprintf("Create VolumeAttachment %q on node %q", vaName, vaNodeName))
   203  	va := NewVolumeAttachment(vaName, pvName, vaNodeName, vaAttachStatus)
   204  
   205  	createdVA, err := f.ClientSet.StorageV1().VolumeAttachments().Create(ctx, va, metav1.CreateOptions{})
   206  	framework.ExpectNoError(err, "failed to create VolumeAttachment %q", vaName)
   207  	gomega.Expect(createdVA.Name).To(gomega.Equal(vaName), "Checking that the created VolumeAttachment has the correct name")
   208  
   209  	return createdVA.Name, vaNodeName
   210  }
   211  

View as plain text