From bc34fa135e414c2648f62cbfa0307a29dfab5253 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Wed, 7 Feb 2018 13:43:15 -0500 Subject: [PATCH] UPSTREAM: 58415: Improve messaging on resize Fixes : https://bugzilla.redhat.com/show_bug.cgi?id=1532512 --- .../k8s.io/kubernetes/pkg/apis/core/types.go | 2 + .../pkg/controller/volume/expand/BUILD | 2 - .../pkg/controller/volume/expand/cache/BUILD | 3 +- .../volume/expand/cache/volume_resize_map.go | 36 ++-- .../volume/expand/expand_controller.go | 21 ++- .../volume/expand/sync_volume_resize.go | 6 +- .../pkg/controller/volume/expand/util/BUILD | 27 --- .../pkg/controller/volume/expand/util/util.go | 46 ----- .../kubernetes/pkg/kubelet/events/event.go | 1 + .../k8s.io/kubernetes/pkg/volume/util/BUILD | 3 + .../pkg/volume/util/operationexecutor/BUILD | 1 - .../operationexecutor/operation_generator.go | 42 ++--- .../kubernetes/pkg/volume/util/resize_util.go | 125 +++++++++++++ .../pkg/volume/util/resize_util_test.go | 167 ++++++++++++++++++ .../staging/src/k8s.io/api/core/v1/types.go | 2 + 15 files changed, 355 insertions(+), 129 deletions(-) delete mode 100644 vendor/k8s.io/kubernetes/pkg/controller/volume/expand/util/BUILD delete mode 100644 vendor/k8s.io/kubernetes/pkg/controller/volume/expand/util/util.go create mode 100644 vendor/k8s.io/kubernetes/pkg/volume/util/resize_util.go create mode 100644 vendor/k8s.io/kubernetes/pkg/volume/util/resize_util_test.go diff --git a/vendor/k8s.io/kubernetes/pkg/apis/core/types.go b/vendor/k8s.io/kubernetes/pkg/apis/core/types.go index b6b570b06e4c..46b86d6a1dda 100644 --- a/vendor/k8s.io/kubernetes/pkg/apis/core/types.go +++ b/vendor/k8s.io/kubernetes/pkg/apis/core/types.go @@ -576,6 +576,8 @@ type PersistentVolumeClaimConditionType string const ( // An user trigger resize of pvc has been started PersistentVolumeClaimResizing PersistentVolumeClaimConditionType = "Resizing" + // PersistentVolumeClaimFileSystemResizePending - controller resize is finished and a file system resize is pending on node + PersistentVolumeClaimFileSystemResizePending PersistentVolumeClaimConditionType = "FileSystemResizePending" ) type PersistentVolumeClaimCondition struct { diff --git a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/BUILD b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/BUILD index 74ae103b74c2..cb69f9eb50b1 100644 --- a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/BUILD +++ b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/BUILD @@ -17,7 +17,6 @@ go_library( "//pkg/cloudprovider:go_default_library", "//pkg/controller:go_default_library", "//pkg/controller/volume/expand/cache:go_default_library", - "//pkg/controller/volume/expand/util:go_default_library", "//pkg/util/goroutinemap/exponentialbackoff:go_default_library", "//pkg/util/io:go_default_library", "//pkg/util/mount:go_default_library", @@ -53,7 +52,6 @@ filegroup( srcs = [ ":package-srcs", "//pkg/controller/volume/expand/cache:all-srcs", - "//pkg/controller/volume/expand/util:all-srcs", ], tags = ["automanaged"], ) diff --git a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/cache/BUILD b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/cache/BUILD index c9f64da1a972..1c0c40ff3411 100644 --- a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/cache/BUILD +++ b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/cache/BUILD @@ -11,12 +11,13 @@ go_library( srcs = ["volume_resize_map.go"], importpath = "k8s.io/kubernetes/pkg/controller/volume/expand/cache", deps = [ - "//pkg/controller/volume/expand/util:go_default_library", "//pkg/util/strings:go_default_library", + "//pkg/volume/util:go_default_library", "//pkg/volume/util/types:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", diff --git a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/cache/volume_resize_map.go b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/cache/volume_resize_map.go index 2645a294c23b..04a54240d34f 100644 --- a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/cache/volume_resize_map.go +++ b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/cache/volume_resize_map.go @@ -24,11 +24,12 @@ import ( "github.com/golang/glog" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" commontypes "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/strategicpatch" clientset "k8s.io/client-go/kubernetes" - "k8s.io/kubernetes/pkg/controller/volume/expand/util" "k8s.io/kubernetes/pkg/util/strings" + "k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util/types" ) @@ -44,6 +45,8 @@ type VolumeResizeMap interface { MarkAsResized(*PVCWithResizeRequest, resource.Quantity) error // UpdatePVSize updates just pv size after cloudprovider resizing is successful UpdatePVSize(*PVCWithResizeRequest, resource.Quantity) error + // MarkForFSResize updates pvc condition to indicate that a file system resize is pending + MarkForFSResize(*PVCWithResizeRequest) error } type volumeResizeMap struct { @@ -163,6 +166,21 @@ func (resizeMap *volumeResizeMap) MarkAsResized(pvcr *PVCWithResizeRequest, newS return nil } +// MarkForFSResize marks pvc with condition that indicates a fs resize is pending +func (resizeMap *volumeResizeMap) MarkForFSResize(pvcr *PVCWithResizeRequest) error { + pvcCondition := v1.PersistentVolumeClaimCondition{ + Type: v1.PersistentVolumeClaimFileSystemResizePending, + Status: v1.ConditionTrue, + LastTransitionTime: metav1.Now(), + Message: "Waiting for user to (re-)start a pod to finish file system resize of volume on node.", + } + conditions := []v1.PersistentVolumeClaimCondition{pvcCondition} + newPVC := pvcr.PVC.DeepCopy() + newPVC = util.MergeResizeConditionOnPVC(newPVC, conditions) + _, err := util.PatchPVCStatus(pvcr.PVC /*oldPVC*/, newPVC, resizeMap.kubeClient) + return err +} + // UpdatePVSize updates just pv size after cloudprovider resizing is successful func (resizeMap *volumeResizeMap) UpdatePVSize(pvcr *PVCWithResizeRequest, newSize resource.Quantity) error { resizeMap.Lock() @@ -201,16 +219,10 @@ func (resizeMap *volumeResizeMap) UpdatePVSize(pvcr *PVCWithResizeRequest, newSi } func (resizeMap *volumeResizeMap) updatePVCCapacityAndConditions(pvcr *PVCWithResizeRequest, newSize resource.Quantity, pvcConditions []v1.PersistentVolumeClaimCondition) error { + newPVC := pvcr.PVC.DeepCopy() + newPVC.Status.Capacity[v1.ResourceStorage] = newSize + newPVC = util.MergeResizeConditionOnPVC(newPVC, pvcConditions) + _, err := util.PatchPVCStatus(pvcr.PVC /*oldPVC*/, newPVC, resizeMap.kubeClient) + return err - claimClone := pvcr.PVC.DeepCopy() - - claimClone.Status.Capacity[v1.ResourceStorage] = newSize - claimClone.Status.Conditions = pvcConditions - - _, updateErr := resizeMap.kubeClient.CoreV1().PersistentVolumeClaims(claimClone.Namespace).UpdateStatus(claimClone) - if updateErr != nil { - glog.V(4).Infof("updating PersistentVolumeClaim[%s] status: failed: %v", pvcr.QualifiedName(), updateErr) - return updateErr - } - return nil } diff --git a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/expand_controller.go b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/expand_controller.go index 6d6ac0edf9bd..874fea4f994e 100644 --- a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/expand_controller.go +++ b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/expand_controller.go @@ -48,7 +48,7 @@ import ( const ( // How often resizing loop runs - syncLoopPeriod time.Duration = 30 * time.Second + syncLoopPeriod time.Duration = 400 * time.Millisecond // How often pvc populator runs populatorLoopPeriod time.Duration = 2 * time.Minute ) @@ -182,12 +182,21 @@ func (expc *expandController) pvcUpdate(oldObj, newObj interface{}) { if newPVC == nil || !ok { return } - pv, err := getPersistentVolume(newPVC, expc.pvLister) - if err != nil { - glog.V(5).Infof("Error getting Persistent Volume for pvc %q : %v", newPVC.UID, err) - return + + newSize := newPVC.Spec.Resources.Requests[v1.ResourceStorage] + oldSize := oldPvc.Spec.Resources.Requests[v1.ResourceStorage] + + // We perform additional checks inside resizeMap.AddPVCUpdate function + // this check here exists to ensure - we do not consider every + // PVC update event for resizing, just those where the PVC size changes + if newSize.Cmp(oldSize) > 0 { + pv, err := getPersistentVolume(newPVC, expc.pvLister) + if err != nil { + glog.V(5).Infof("Error getting Persistent Volume for pvc %q : %v", newPVC.UID, err) + return + } + expc.resizeMap.AddPVCUpdate(newPVC, pv) } - expc.resizeMap.AddPVCUpdate(newPVC, pv) } func getPersistentVolume(pvc *v1.PersistentVolumeClaim, pvLister corelisters.PersistentVolumeLister) (*v1.PersistentVolume, error) { diff --git a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/sync_volume_resize.go b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/sync_volume_resize.go index 06565775e63d..17ecba5330f5 100644 --- a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/sync_volume_resize.go +++ b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/sync_volume_resize.go @@ -25,8 +25,8 @@ import ( "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/controller/volume/expand/cache" - "k8s.io/kubernetes/pkg/controller/volume/expand/util" "k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff" + "k8s.io/kubernetes/pkg/volume/util" "k8s.io/kubernetes/pkg/volume/util/operationexecutor" ) @@ -96,6 +96,8 @@ func markPVCResizeInProgress(pvcWithResizeRequest *cache.PVCWithResizeRequest, k LastTransitionTime: metav1.Now(), } conditions := []v1.PersistentVolumeClaimCondition{progressCondition} + newPVC := pvcWithResizeRequest.PVC.DeepCopy() + newPVC = util.MergeResizeConditionOnPVC(newPVC, conditions) - return util.UpdatePVCCondition(pvcWithResizeRequest.PVC, conditions, kubeClient) + return util.PatchPVCStatus(pvcWithResizeRequest.PVC /*oldPVC*/, newPVC, kubeClient) } diff --git a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/util/BUILD b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/util/BUILD deleted file mode 100644 index b7bb8c69f0ac..000000000000 --- a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/util/BUILD +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = ["util.go"], - importpath = "k8s.io/kubernetes/pkg/controller/volume/expand/util", - visibility = ["//visibility:public"], - deps = [ - "//vendor/github.com/golang/glog:go_default_library", - "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/client-go/kubernetes:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], - visibility = ["//visibility:public"], -) diff --git a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/util/util.go b/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/util/util.go deleted file mode 100644 index bc6795c4fa75..000000000000 --- a/vendor/k8s.io/kubernetes/pkg/controller/volume/expand/util/util.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -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 util - -import ( - "fmt" - - "github.com/golang/glog" - - "k8s.io/api/core/v1" - clientset "k8s.io/client-go/kubernetes" -) - -// ClaimToClaimKey return namespace/name string for pvc -func ClaimToClaimKey(claim *v1.PersistentVolumeClaim) string { - return fmt.Sprintf("%s/%s", claim.Namespace, claim.Name) -} - -// UpdatePVCCondition updates pvc with given condition status -func UpdatePVCCondition(pvc *v1.PersistentVolumeClaim, - pvcConditions []v1.PersistentVolumeClaimCondition, - kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) { - - claimClone := pvc.DeepCopy() - claimClone.Status.Conditions = pvcConditions - updatedClaim, updateErr := kubeClient.CoreV1().PersistentVolumeClaims(claimClone.Namespace).UpdateStatus(claimClone) - if updateErr != nil { - glog.V(4).Infof("updating PersistentVolumeClaim[%s] status: failed: %v", ClaimToClaimKey(pvc), updateErr) - return nil, updateErr - } - return updatedClaim, nil -} diff --git a/vendor/k8s.io/kubernetes/pkg/kubelet/events/event.go b/vendor/k8s.io/kubernetes/pkg/kubelet/events/event.go index 127405738117..dbc0f9bf1eb1 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubelet/events/event.go +++ b/vendor/k8s.io/kubernetes/pkg/kubelet/events/event.go @@ -52,6 +52,7 @@ const ( FailedDetachVolume = "FailedDetachVolume" FailedMountVolume = "FailedMount" VolumeResizeFailed = "VolumeResizeFailed" + VolumeResizeSuccess = "VolumeResizeSuccessful" FileSystemResizeFailed = "FileSystemResizeFailed" FileSystemResizeSuccess = "FileSystemResizeSuccessful" FailedUnMountVolume = "FailedUnMount" diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/BUILD b/vendor/k8s.io/kubernetes/pkg/volume/util/BUILD index 48a0b0ee6bf3..f7f4fed92ba5 100644 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/BUILD +++ b/vendor/k8s.io/kubernetes/pkg/volume/util/BUILD @@ -18,6 +18,7 @@ go_library( "fs_unsupported.go", "io_util.go", "metrics.go", + "resize_util.go", "util.go", "util_unsupported.go", ] + select({ @@ -47,6 +48,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:darwin_amd64": [ @@ -78,6 +80,7 @@ go_test( "//pkg/apis/core/v1/helper:go_default_library", "//vendor/github.com/davecgh/go-spew/spew:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", ] + select({ diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/BUILD b/vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/BUILD index 15c2ac27d837..13e6ab531dab 100644 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/BUILD +++ b/vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/BUILD @@ -29,7 +29,6 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/tools/record:go_default_library", diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go b/vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go index a322ed4ca3d8..97ff25bc5cfb 100644 --- a/vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go +++ b/vendor/k8s.io/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go @@ -17,7 +17,6 @@ limitations under the License. package operationexecutor import ( - "encoding/json" "fmt" "strings" "time" @@ -27,7 +26,6 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/strategicpatch" utilfeature "k8s.io/apiserver/pkg/util/feature" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/record" @@ -637,7 +635,7 @@ func (og *operationGenerator) resizeFileSystem(volumeToMount VolumeToMount, devi } // File system resize succeeded, now update the PVC's Capacity to match the PV's - err = updatePVCStatusCapacity(pvc.Name, pvc, pv.Spec.Capacity, og.kubeClient) + err = util.MarkFSResizeFinished(pvc, pv.Spec.Capacity, og.kubeClient) if err != nil { // On retry, resizeFileSystem will be called again but do nothing return volumeToMount.GenerateErrorDetailed("MountVolume update PVC status failed", err) @@ -1287,9 +1285,17 @@ func (og *operationGenerator) GenerateExpandVolumeFunc( detailedErr := fmt.Errorf("Error marking pvc %s as resized : %v", pvcWithResizeRequest.QualifiedName(), err) return detailedErr, detailedErr } + successMsg := fmt.Sprintf("ExpandVolume succeeded for volume %s", pvcWithResizeRequest.QualifiedName()) + og.recorder.Eventf(pvcWithResizeRequest.PVC, v1.EventTypeNormal, kevents.VolumeResizeSuccess, successMsg) + } else { + err := resizeMap.MarkForFSResize(pvcWithResizeRequest) + if err != nil { + detailedErr := fmt.Errorf("Error updating pvc %s condition for fs resize : %v", pvcWithResizeRequest.QualifiedName(), err) + glog.Warning(detailedErr) + return nil, nil + } } return nil, nil - } eventRecorderFunc := func(err *error) { @@ -1354,31 +1360,3 @@ func isDeviceOpened(deviceToDetach AttachedVolume, mounter mount.Interface) (boo } return deviceOpened, nil } - -func updatePVCStatusCapacity(pvcName string, pvc *v1.PersistentVolumeClaim, capacity v1.ResourceList, client clientset.Interface) error { - pvcCopy := pvc.DeepCopy() - - oldData, err := json.Marshal(pvcCopy) - if err != nil { - return fmt.Errorf("Failed to marshal oldData for pvc %q with %v", pvcName, err) - } - - pvcCopy.Status.Capacity = capacity - pvcCopy.Status.Conditions = []v1.PersistentVolumeClaimCondition{} - newData, err := json.Marshal(pvcCopy) - - if err != nil { - return fmt.Errorf("Failed to marshal newData for pvc %q with %v", pvcName, err) - } - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, pvcCopy) - - if err != nil { - return fmt.Errorf("Failed to CreateTwoWayMergePatch for pvc %q with %v ", pvcName, err) - } - _, err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace). - Patch(pvcName, types.StrategicMergePatchType, patchBytes, "status") - if err != nil { - return fmt.Errorf("Failed to patch PVC %q with %v", pvcName, err) - } - return nil -} diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/resize_util.go b/vendor/k8s.io/kubernetes/pkg/volume/util/resize_util.go new file mode 100644 index 000000000000..c1d2a1c82acd --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/volume/util/resize_util.go @@ -0,0 +1,125 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 util + +import ( + "encoding/json" + "fmt" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + clientset "k8s.io/client-go/kubernetes" +) + +var ( + knownResizeConditions map[v1.PersistentVolumeClaimConditionType]bool = map[v1.PersistentVolumeClaimConditionType]bool{ + v1.PersistentVolumeClaimFileSystemResizePending: true, + v1.PersistentVolumeClaimResizing: true, + } +) + +type resizeProcessStatus struct { + condition v1.PersistentVolumeClaimCondition + processed bool +} + +// ClaimToClaimKey return namespace/name string for pvc +func ClaimToClaimKey(claim *v1.PersistentVolumeClaim) string { + return fmt.Sprintf("%s/%s", claim.Namespace, claim.Name) +} + +// MarkFSResizeFinished marks file system resizing as done +func MarkFSResizeFinished( + pvc *v1.PersistentVolumeClaim, + capacity v1.ResourceList, + kubeClient clientset.Interface) error { + newPVC := pvc.DeepCopy() + newPVC.Status.Capacity = capacity + newPVC = MergeResizeConditionOnPVC(newPVC, []v1.PersistentVolumeClaimCondition{}) + _, err := PatchPVCStatus(pvc /*oldPVC*/, newPVC, kubeClient) + return err +} + +// PatchPVCStatus updates PVC status using PATCH verb +func PatchPVCStatus( + oldPVC *v1.PersistentVolumeClaim, + newPVC *v1.PersistentVolumeClaim, + kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) { + pvcName := oldPVC.Name + + oldData, err := json.Marshal(oldPVC) + if err != nil { + return nil, fmt.Errorf("PatchPVCStatus.Failed to marshal oldData for pvc %q with %v", pvcName, err) + } + + newData, err := json.Marshal(newPVC) + if err != nil { + return nil, fmt.Errorf("PatchPVCStatus.Failed to marshal newData for pvc %q with %v", pvcName, err) + } + + patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, oldPVC) + if err != nil { + return nil, fmt.Errorf("PatchPVCStatus.Failed to CreateTwoWayMergePatch for pvc %q with %v ", pvcName, err) + } + updatedClaim, updateErr := kubeClient.CoreV1().PersistentVolumeClaims(oldPVC.Namespace). + Patch(pvcName, types.StrategicMergePatchType, patchBytes, "status") + if updateErr != nil { + return nil, fmt.Errorf("PatchPVCStatus.Failed to patch PVC %q with %v", pvcName, updateErr) + } + return updatedClaim, nil +} + +// MergeResizeConditionOnPVC updates pvc with requested resize conditions +// leaving other conditions untouched. +func MergeResizeConditionOnPVC( + pvc *v1.PersistentVolumeClaim, + resizeConditions []v1.PersistentVolumeClaimCondition) *v1.PersistentVolumeClaim { + resizeConditionMap := map[v1.PersistentVolumeClaimConditionType]*resizeProcessStatus{} + + for _, condition := range resizeConditions { + resizeConditionMap[condition.Type] = &resizeProcessStatus{condition, false} + } + + oldConditions := pvc.Status.Conditions + newConditions := []v1.PersistentVolumeClaimCondition{} + for _, condition := range oldConditions { + // If Condition is of not resize type, we keep it. + if _, ok := knownResizeConditions[condition.Type]; !ok { + newConditions = append(newConditions, condition) + continue + } + + if newCondition, ok := resizeConditionMap[condition.Type]; ok { + if newCondition.condition.Status != condition.Status { + newConditions = append(newConditions, newCondition.condition) + } else { + newConditions = append(newConditions, condition) + } + newCondition.processed = true + } + } + + // append all unprocessed conditions + for _, newCondition := range resizeConditionMap { + if !newCondition.processed { + newConditions = append(newConditions, newCondition.condition) + } + } + pvc.Status.Conditions = newConditions + return pvc +} diff --git a/vendor/k8s.io/kubernetes/pkg/volume/util/resize_util_test.go b/vendor/k8s.io/kubernetes/pkg/volume/util/resize_util_test.go new file mode 100644 index 000000000000..28fb859eca88 --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/volume/util/resize_util_test.go @@ -0,0 +1,167 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 util + +import ( + "reflect" + "testing" + "time" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type conditionMergeTestCase struct { + description string + pvc *v1.PersistentVolumeClaim + newConditions []v1.PersistentVolumeClaimCondition + finalCondtions []v1.PersistentVolumeClaimCondition +} + +func TestMergeResizeCondition(t *testing.T) { + currentTime := metav1.Now() + + pvc := getPVC([]v1.PersistentVolumeClaimCondition{ + { + Type: v1.PersistentVolumeClaimResizing, + Status: v1.ConditionTrue, + LastTransitionTime: currentTime, + }, + }) + + noConditionPVC := getPVC([]v1.PersistentVolumeClaimCondition{}) + + conditionFalseTime := metav1.Now() + newTime := metav1.NewTime(time.Now().Add(1 * time.Hour)) + + testCases := []conditionMergeTestCase{ + { + description: "when removing all conditions", + pvc: pvc.DeepCopy(), + newConditions: []v1.PersistentVolumeClaimCondition{}, + finalCondtions: []v1.PersistentVolumeClaimCondition{}, + }, + { + description: "adding new condition", + pvc: pvc.DeepCopy(), + newConditions: []v1.PersistentVolumeClaimCondition{ + { + Type: v1.PersistentVolumeClaimFileSystemResizePending, + Status: v1.ConditionTrue, + }, + }, + finalCondtions: []v1.PersistentVolumeClaimCondition{ + { + Type: v1.PersistentVolumeClaimFileSystemResizePending, + Status: v1.ConditionTrue, + }, + }, + }, + { + description: "adding same condition with new timestamp", + pvc: pvc.DeepCopy(), + newConditions: []v1.PersistentVolumeClaimCondition{ + { + Type: v1.PersistentVolumeClaimResizing, + Status: v1.ConditionTrue, + LastTransitionTime: newTime, + }, + }, + finalCondtions: []v1.PersistentVolumeClaimCondition{ + { + Type: v1.PersistentVolumeClaimResizing, + Status: v1.ConditionTrue, + LastTransitionTime: currentTime, + }, + }, + }, + { + description: "adding same condition but with different status", + pvc: pvc.DeepCopy(), + newConditions: []v1.PersistentVolumeClaimCondition{ + { + Type: v1.PersistentVolumeClaimResizing, + Status: v1.ConditionFalse, + LastTransitionTime: conditionFalseTime, + }, + }, + finalCondtions: []v1.PersistentVolumeClaimCondition{ + { + Type: v1.PersistentVolumeClaimResizing, + Status: v1.ConditionFalse, + LastTransitionTime: conditionFalseTime, + }, + }, + }, + { + description: "when no condition exists on pvc", + pvc: noConditionPVC.DeepCopy(), + newConditions: []v1.PersistentVolumeClaimCondition{ + { + Type: v1.PersistentVolumeClaimResizing, + Status: v1.ConditionTrue, + LastTransitionTime: currentTime, + }, + }, + finalCondtions: []v1.PersistentVolumeClaimCondition{ + { + Type: v1.PersistentVolumeClaimResizing, + Status: v1.ConditionTrue, + LastTransitionTime: currentTime, + }, + }, + }, + } + + for _, testcase := range testCases { + updatePVC := MergeResizeConditionOnPVC(testcase.pvc, testcase.newConditions) + + updateConditions := updatePVC.Status.Conditions + if !reflect.DeepEqual(updateConditions, testcase.finalCondtions) { + t.Errorf("Expected updated conditions for test %s to be %v but got %v", + testcase.description, + testcase.finalCondtions, updateConditions) + } + } + +} + +func getPVC(conditions []v1.PersistentVolumeClaimCondition) *v1.PersistentVolumeClaim { + pvc := &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "resize"}, + Spec: v1.PersistentVolumeClaimSpec{ + AccessModes: []v1.PersistentVolumeAccessMode{ + v1.ReadWriteOnce, + v1.ReadOnlyMany, + }, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse("2Gi"), + }, + }, + }, + Status: v1.PersistentVolumeClaimStatus{ + Phase: v1.ClaimBound, + Conditions: conditions, + Capacity: v1.ResourceList{ + v1.ResourceStorage: resource.MustParse("2Gi"), + }, + }, + } + return pvc +} diff --git a/vendor/k8s.io/kubernetes/staging/src/k8s.io/api/core/v1/types.go b/vendor/k8s.io/kubernetes/staging/src/k8s.io/api/core/v1/types.go index 728cbd5a62b0..3ed94956c80f 100644 --- a/vendor/k8s.io/kubernetes/staging/src/k8s.io/api/core/v1/types.go +++ b/vendor/k8s.io/kubernetes/staging/src/k8s.io/api/core/v1/types.go @@ -657,6 +657,8 @@ type PersistentVolumeClaimConditionType string const ( // PersistentVolumeClaimResizing - a user trigger resize of pvc has been started PersistentVolumeClaimResizing PersistentVolumeClaimConditionType = "Resizing" + // PersistentVolumeClaimFileSystemResizePending - controller resize is finished and a file system resize is pending on node + PersistentVolumeClaimFileSystemResizePending PersistentVolumeClaimConditionType = "FileSystemResizePending" ) // PersistentVolumeClaimCondition contails details about state of pvc