From 3148fe844014b40b3aebacbe2db05f330da25004 Mon Sep 17 00:00:00 2001 From: Sameer Shaikh Date: Mon, 27 Jan 2025 15:20:30 +0530 Subject: [PATCH] Support for update Volume tags via RIAAS --- go.mod | 2 +- go.sum | 4 +- .../block/provider/update_volume.go | 56 +++++++++++++++++-- .../block/provider/util.go | 38 +++++++++++++ .../vpcclient/vpcvolume/get_volume_etag.go | 2 +- .../vpcclient/vpcvolume/update_volume.go | 44 +-------------- .../vpcvolume/update_volume_with_etag.go | 56 +++++++++++++++++++ .../vpcclient/vpcvolume/volume_service.go | 6 +- vendor/modules.txt | 2 +- 9 files changed, 157 insertions(+), 53 deletions(-) create mode 100644 vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/update_volume_with_etag.go diff --git a/go.mod b/go.mod index a51ba25e..1ecb900a 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.0 require ( github.com/IBM/ibm-csi-common v1.1.19-0.20250126063306-393291468e53 github.com/IBM/ibmcloud-volume-interface v1.2.8 - github.com/IBM/ibmcloud-volume-vpc v1.1.13-0.20250127070546-255e6fc5f8a7 + github.com/IBM/ibmcloud-volume-vpc v1.1.13-0.20250127094601-d8c3a709bd18 github.com/IBM/secret-utils-lib v1.1.11 github.com/container-storage-interface/spec v1.9.0 github.com/golang/glog v1.2.1 diff --git a/go.sum b/go.sum index d6031007..12fdebe3 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/IBM/ibm-csi-common v1.1.19-0.20250126063306-393291468e53 h1:u0LDzIFa0 github.com/IBM/ibm-csi-common v1.1.19-0.20250126063306-393291468e53/go.mod h1:8bAkk54FT6JJ+OTH97/kaD6AMiXN4tI6oBS1hxZvZSw= github.com/IBM/ibmcloud-volume-interface v1.2.8 h1:1cHqS+sLgcHQ/74gBxyWPe8DzimxvDeODRg0yAzwEhs= github.com/IBM/ibmcloud-volume-interface v1.2.8/go.mod h1:sDeQiPuN8k9yTRl9FbE2GZCXPNg4cV3oldUfL8wwGNA= -github.com/IBM/ibmcloud-volume-vpc v1.1.13-0.20250127070546-255e6fc5f8a7 h1:KUqk5tCDnN3W9WkPsivd7cTSBc0+ckGcf/wzI47B5FQ= -github.com/IBM/ibmcloud-volume-vpc v1.1.13-0.20250127070546-255e6fc5f8a7/go.mod h1:N/m+YQAfiLZWeaKUBf01j5OpAYPsRMLL/ds1Znju47Y= +github.com/IBM/ibmcloud-volume-vpc v1.1.13-0.20250127094601-d8c3a709bd18 h1:dUUTM1uJNMcDkGKuCNpwO4vUmYOouwOTkryfkvL/Tio= +github.com/IBM/ibmcloud-volume-vpc v1.1.13-0.20250127094601-d8c3a709bd18/go.mod h1:N/m+YQAfiLZWeaKUBf01j5OpAYPsRMLL/ds1Znju47Y= github.com/IBM/secret-common-lib v1.1.11 h1:EpfEe1gT1bnFQ3bxQPrh6bzTPeGjUo1NReVkCCP+TOc= github.com/IBM/secret-common-lib v1.1.11/go.mod h1:7YJF0ipT979nHIPkiCpvjFboFoIhrmYnIliE1vjCjZM= github.com/IBM/secret-utils-lib v1.1.11 h1:w87BzkddoFFlhRuWRteuGj3/561lEUg6Oo0RajVC87A= diff --git a/vendor/github.com/IBM/ibmcloud-volume-vpc/block/provider/update_volume.go b/vendor/github.com/IBM/ibmcloud-volume-vpc/block/provider/update_volume.go index 70f63c1d..a436c611 100644 --- a/vendor/github.com/IBM/ibmcloud-volume-vpc/block/provider/update_volume.go +++ b/vendor/github.com/IBM/ibmcloud-volume-vpc/block/provider/update_volume.go @@ -18,6 +18,8 @@ package provider import ( + "strings" + "github.com/IBM/ibmcloud-volume-interface/lib/provider" userError "github.com/IBM/ibmcloud-volume-vpc/common/messages" "github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/models" @@ -26,18 +28,46 @@ import ( // UpdateVolume PATCH to /volumes func (vpcs *VPCSession) UpdateVolume(volumeRequest provider.Volume) error { - var volume *models.Volume + var existVolume *models.Volume var err error + var etag string + + //Fetch existing volume Tags + err = RetryWithMinRetries(vpcs.Logger, func() error { + // Get volume details + existVolume, etag, err = vpcs.Apiclient.VolumeService().GetVolumeEtag(volumeRequest.VolumeID, vpcs.Logger) + + if err != nil { + return err + } + if existVolume != nil && existVolume.Status == validVolumeStatus { + vpcs.Logger.Info("Volume got valid (available) state") + return nil + } + return userError.GetUserError("VolumeNotInValidState", err, volumeRequest.VolumeID) + }) + + if err != nil { + return err + } + + //If tags are equal then skip the UpdateVolume RIAAS API call + if ifTagsEqual(existVolume.UserTags, volumeRequest.VPCVolume.Tags) { + vpcs.Logger.Info("There is no change in user tags for volume, skipping the updateVolume for VPC IaaS... ", zap.Reflect("existVolume", existVolume.Tags), zap.Reflect("volumeRequest", volumeRequest.VPCVolume.Tags)) + return nil + } + + //Append the existing tags with the requested input tags + existVolume.UserTags = append(existVolume.UserTags, volumeRequest.VPCVolume.Tags...) - volume = &models.Volume{ - ID: volumeRequest.VolumeID, - UserTags: volumeRequest.VPCVolume.Tags, + volume := &models.Volume{ + UserTags: existVolume.UserTags, } vpcs.Logger.Info("Calling VPC provider for volume UpdateVolumeWithTags...") - err = retry(vpcs.Logger, func() error { - err = vpcs.Apiclient.VolumeService().UpdateVolume(volume, vpcs.Logger) + err = RetryWithMinRetries(vpcs.Logger, func() error { + err = vpcs.Apiclient.VolumeService().UpdateVolumeWithEtag(volumeRequest.VolumeID, etag, volume, vpcs.Logger) return err }) @@ -48,3 +78,17 @@ func (vpcs *VPCSession) UpdateVolume(volumeRequest provider.Volume) error { return err } + +// ifTagsEqual will check if there is change to existing tags +func ifTagsEqual(existingTags []string, newTags []string) bool { + //Join slice into a string + tags := strings.ToLower(strings.Join(existingTags, ",")) + for _, v := range newTags { + if !strings.Contains(tags, strings.ToLower(v)) { + //Tags are different + return false + } + } + //Tags are equal + return true +} diff --git a/vendor/github.com/IBM/ibmcloud-volume-vpc/block/provider/util.go b/vendor/github.com/IBM/ibmcloud-volume-vpc/block/provider/util.go index 965c506d..e1e9ca8f 100644 --- a/vendor/github.com/IBM/ibmcloud-volume-vpc/block/provider/util.go +++ b/vendor/github.com/IBM/ibmcloud-volume-vpc/block/provider/util.go @@ -31,6 +31,9 @@ import ( // maxRetryAttempt ... var maxRetryAttempt = 10 +// minRetryAttempt ... +var minRetryAttempt = 3 + // maxRetryGap ... var maxRetryGap = 60 @@ -119,6 +122,41 @@ func retry(logger *zap.Logger, retryfunc func() error) error { return err } +// retry ... +func RetryWithMinRetries(logger *zap.Logger, retryfunc func() error) error { + var err error + retryGap := 10 + for i := 0; i < minRetryAttempt; i++ { + if i > 0 { + time.Sleep(time.Duration(retryGap) * time.Second) + } + err = retryfunc() + if err != nil { + logger.Info("err object is not nil", zap.Reflect("ERR", err)) + //Skip retry for the below type of Errors + modelError, ok := err.(*models.Error) + if !ok { + continue + } + if skipRetry(modelError) { + break + } + if i >= 1 { + retryGap = 2 * retryGap + if retryGap > maxRetryGap { + retryGap = maxRetryGap + } + } + if (i + 1) < minRetryAttempt { + logger.Info("Error while executing the function. Re-attempting execution ..", zap.Int("attempt..", i+2), zap.Int("retry-gap", retryGap), zap.Int("max-retry-Attempts", minRetryAttempt), zap.Error(err)) + } + continue + } + return err + } + return err +} + // skipRetry skip retry as per listed error codes func skipRetry(err *models.Error) bool { for _, errorItem := range err.Errors { diff --git a/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/get_volume_etag.go b/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/get_volume_etag.go index 6cf5c4a7..6b27bf72 100644 --- a/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/get_volume_etag.go +++ b/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/get_volume_etag.go @@ -1,5 +1,5 @@ /** - * Copyright 2020 IBM Corp. + * Copyright 2025 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/update_volume.go b/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/update_volume.go index 9b88e0d3..2a8f377d 100644 --- a/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/update_volume.go +++ b/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/update_volume.go @@ -18,51 +18,13 @@ package vpcvolume import ( - "time" + "errors" - util "github.com/IBM/ibmcloud-volume-interface/lib/utils" - "github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/client" "github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/models" "go.uber.org/zap" ) -// UpdateVolume PATCH to /volumes for updating user tags only +// UpdateVolume PATCH to /volumes for updating func (vs *VolumeService) UpdateVolume(volumeTemplate *models.Volume, ctxLogger *zap.Logger) error { - ctxLogger.Debug("Entry Backend UpdateVolume") - defer ctxLogger.Debug("Exit Backend UpdateVolume") - - defer util.TimeTracker("UpdateVolume", time.Now()) - - //First try to get the Etag and user-tags - existingVolume, etag, err := vs.GetVolumeEtag(volumeTemplate.ID, ctxLogger) - - if err != nil { - return err - } - - //Append the existing tags with the requested input tags - volumeTemplate.UserTags = append(volumeTemplate.UserTags, existingVolume.UserTags...) - - operation := &client.Operation{ - Name: "UpdateVolume", - Method: "PATCH", - PathPattern: volumeIDPath, - } - - var apiErr models.Error - - request := vs.client.NewRequest(operation) - request.SetHeader("If-Match", etag) - - req := request.PathParameter(volumeIDParam, volumeTemplate.ID) - //We dont require this as part ot PATCH body lets omit it - volumeTemplate.ID = "" - ctxLogger.Info("Equivalent curl command and payload details", zap.Reflect("URL", req.URL()), zap.Reflect("Payload", volumeTemplate), zap.Reflect("Operation", operation)) - _, err = req.JSONBody(volumeTemplate).JSONError(&apiErr).Invoke() - - if err != nil { - return err - } - - return nil + return errors.New("unsupported Operation") } diff --git a/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/update_volume_with_etag.go b/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/update_volume_with_etag.go new file mode 100644 index 00000000..bf70f0c3 --- /dev/null +++ b/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/update_volume_with_etag.go @@ -0,0 +1,56 @@ +/** + * Copyright 2025 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package vpcvolume ... +package vpcvolume + +import ( + "time" + + util "github.com/IBM/ibmcloud-volume-interface/lib/utils" + "github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/client" + "github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/models" + "go.uber.org/zap" +) + +// UpdateVolume PATCH to /volumes for updating user tags only +func (vs *VolumeService) UpdateVolumeWithEtag(volumeID string, etag string, volumeTemplate *models.Volume, ctxLogger *zap.Logger) error { + ctxLogger.Debug("Entry Backend UpdateVolume") + defer ctxLogger.Debug("Exit Backend UpdateVolume") + + defer util.TimeTracker("UpdateVolume", time.Now()) + + operation := &client.Operation{ + Name: "UpdateVolume", + Method: "PATCH", + PathPattern: volumeIDPath, + } + + var apiErr models.Error + + request := vs.client.NewRequest(operation) + request.SetHeader("If-Match", etag) + + req := request.PathParameter(volumeIDParam, volumeID) + ctxLogger.Info("Equivalent curl command and payload details", zap.Reflect("URL", req.URL()), zap.Reflect("Payload", volumeTemplate), zap.Reflect("Operation", operation)) + _, err := req.JSONBody(volumeTemplate).JSONError(&apiErr).Invoke() + + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/volume_service.go b/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/volume_service.go index f7734c46..9f4537e0 100644 --- a/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/volume_service.go +++ b/vendor/github.com/IBM/ibmcloud-volume-vpc/common/vpcclient/vpcvolume/volume_service.go @@ -25,13 +25,17 @@ import ( // VolumeManager operations // -//go:generate counterfeiter -o vpcvolumefakes/volume_service.go --fake-name VolumeService . VolumeManager +//go:generate counterfeiter -o fakes/volume.go --fake-name VolumeService . VolumeManager type VolumeManager interface { // Create the volume with authorisation by passing required information in the volume object CreateVolume(volumeTemplate *models.Volume, ctxLogger *zap.Logger) (*models.Volume, error) + // UpdateVolume updates the volume with authorisation by passing required information in the volume object UpdateVolume(volumeTemplate *models.Volume, ctxLogger *zap.Logger) error + // UpdateVolumeWithEtag updates the volume with tags by passing etag in header + UpdateVolumeWithEtag(volumeID string, etag string, volumeTemplate *models.Volume, ctxLogger *zap.Logger) error + // ExpandVolume ... ExpandVolume(volumeID string, volumeTemplate *models.Volume, ctxLogger *zap.Logger) (*models.Volume, error) diff --git a/vendor/modules.txt b/vendor/modules.txt index 78154971..65a4598f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -29,7 +29,7 @@ github.com/IBM/ibmcloud-volume-interface/lib/utils/reasoncode github.com/IBM/ibmcloud-volume-interface/provider/auth github.com/IBM/ibmcloud-volume-interface/provider/iam github.com/IBM/ibmcloud-volume-interface/provider/local -# github.com/IBM/ibmcloud-volume-vpc v1.1.13-0.20250127070546-255e6fc5f8a7 +# github.com/IBM/ibmcloud-volume-vpc v1.1.13-0.20250127094601-d8c3a709bd18 ## explicit; go 1.22.0 github.com/IBM/ibmcloud-volume-vpc/block/provider github.com/IBM/ibmcloud-volume-vpc/block/utils