mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
Merge pull request #58415 from gnufied/fix-volume-resize-messages
Automatic merge from submit-queue (batch tested with PRs 52942, 58415). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Improve messaging on volume expansion - we now provide clear message to user what to do when cloudprovider resizing is finished and file system resizing is needed. - add a event when resizing is successful - Use PATCH both in controller-manager and kubelet for updating PVC status - Remove code duplication between controller-manager and kubelet for updating PVC status - Only remove conditions that are managed by resize controller ```release-note Improve messages user gets during and after volume resizing is done. ```
This commit is contained in:
commit
4bd22b5467
@ -577,6 +577,8 @@ type PersistentVolumeClaimConditionType string
|
|||||||
const (
|
const (
|
||||||
// An user trigger resize of pvc has been started
|
// An user trigger resize of pvc has been started
|
||||||
PersistentVolumeClaimResizing PersistentVolumeClaimConditionType = "Resizing"
|
PersistentVolumeClaimResizing PersistentVolumeClaimConditionType = "Resizing"
|
||||||
|
// PersistentVolumeClaimFileSystemResizePending - controller resize is finished and a file system resize is pending on node
|
||||||
|
PersistentVolumeClaimFileSystemResizePending PersistentVolumeClaimConditionType = "FileSystemResizePending"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PersistentVolumeClaimCondition struct {
|
type PersistentVolumeClaimCondition struct {
|
||||||
|
@ -17,7 +17,6 @@ go_library(
|
|||||||
"//pkg/cloudprovider:go_default_library",
|
"//pkg/cloudprovider:go_default_library",
|
||||||
"//pkg/controller:go_default_library",
|
"//pkg/controller:go_default_library",
|
||||||
"//pkg/controller/volume/expand/cache: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/goroutinemap/exponentialbackoff:go_default_library",
|
||||||
"//pkg/util/io:go_default_library",
|
"//pkg/util/io:go_default_library",
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
@ -53,7 +52,6 @@ filegroup(
|
|||||||
srcs = [
|
srcs = [
|
||||||
":package-srcs",
|
":package-srcs",
|
||||||
"//pkg/controller/volume/expand/cache:all-srcs",
|
"//pkg/controller/volume/expand/cache:all-srcs",
|
||||||
"//pkg/controller/volume/expand/util:all-srcs",
|
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
3
pkg/controller/volume/expand/cache/BUILD
vendored
3
pkg/controller/volume/expand/cache/BUILD
vendored
@ -11,12 +11,13 @@ go_library(
|
|||||||
srcs = ["volume_resize_map.go"],
|
srcs = ["volume_resize_map.go"],
|
||||||
importpath = "k8s.io/kubernetes/pkg/controller/volume/expand/cache",
|
importpath = "k8s.io/kubernetes/pkg/controller/volume/expand/cache",
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/controller/volume/expand/util:go_default_library",
|
|
||||||
"//pkg/util/strings:go_default_library",
|
"//pkg/util/strings:go_default_library",
|
||||||
|
"//pkg/volume/util:go_default_library",
|
||||||
"//pkg/volume/util/types:go_default_library",
|
"//pkg/volume/util/types:go_default_library",
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1: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/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/types:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
|
@ -24,11 +24,12 @@ import (
|
|||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
commontypes "k8s.io/apimachinery/pkg/types"
|
commontypes "k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/expand/util"
|
|
||||||
"k8s.io/kubernetes/pkg/util/strings"
|
"k8s.io/kubernetes/pkg/util/strings"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/types"
|
"k8s.io/kubernetes/pkg/volume/util/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,6 +45,8 @@ type VolumeResizeMap interface {
|
|||||||
MarkAsResized(*PVCWithResizeRequest, resource.Quantity) error
|
MarkAsResized(*PVCWithResizeRequest, resource.Quantity) error
|
||||||
// UpdatePVSize updates just pv size after cloudprovider resizing is successful
|
// UpdatePVSize updates just pv size after cloudprovider resizing is successful
|
||||||
UpdatePVSize(*PVCWithResizeRequest, resource.Quantity) error
|
UpdatePVSize(*PVCWithResizeRequest, resource.Quantity) error
|
||||||
|
// MarkForFSResize updates pvc condition to indicate that a file system resize is pending
|
||||||
|
MarkForFSResize(*PVCWithResizeRequest) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type volumeResizeMap struct {
|
type volumeResizeMap struct {
|
||||||
@ -160,6 +163,21 @@ func (resizeMap *volumeResizeMap) MarkAsResized(pvcr *PVCWithResizeRequest, newS
|
|||||||
return nil
|
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
|
// UpdatePVSize updates just pv size after cloudprovider resizing is successful
|
||||||
func (resizeMap *volumeResizeMap) UpdatePVSize(pvcr *PVCWithResizeRequest, newSize resource.Quantity) error {
|
func (resizeMap *volumeResizeMap) UpdatePVSize(pvcr *PVCWithResizeRequest, newSize resource.Quantity) error {
|
||||||
oldPv := pvcr.PersistentVolume
|
oldPv := pvcr.PersistentVolume
|
||||||
@ -195,15 +213,9 @@ func (resizeMap *volumeResizeMap) UpdatePVSize(pvcr *PVCWithResizeRequest, newSi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (resizeMap *volumeResizeMap) updatePVCCapacityAndConditions(pvcr *PVCWithResizeRequest, newSize resource.Quantity, pvcConditions []v1.PersistentVolumeClaimCondition) error {
|
func (resizeMap *volumeResizeMap) updatePVCCapacityAndConditions(pvcr *PVCWithResizeRequest, newSize resource.Quantity, pvcConditions []v1.PersistentVolumeClaimCondition) error {
|
||||||
claimClone := pvcr.PVC.DeepCopy()
|
newPVC := pvcr.PVC.DeepCopy()
|
||||||
|
newPVC.Status.Capacity[v1.ResourceStorage] = newSize
|
||||||
claimClone.Status.Capacity[v1.ResourceStorage] = newSize
|
newPVC = util.MergeResizeConditionOnPVC(newPVC, pvcConditions)
|
||||||
claimClone.Status.Conditions = pvcConditions
|
_, err := util.PatchPVCStatus(pvcr.PVC /*oldPVC*/, newPVC, resizeMap.kubeClient)
|
||||||
|
return err
|
||||||
_, 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
|
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// How often resizing loop runs
|
// How often resizing loop runs
|
||||||
syncLoopPeriod time.Duration = 30 * time.Second
|
syncLoopPeriod time.Duration = 400 * time.Millisecond
|
||||||
// How often pvc populator runs
|
// How often pvc populator runs
|
||||||
populatorLoopPeriod time.Duration = 2 * time.Minute
|
populatorLoopPeriod time.Duration = 2 * time.Minute
|
||||||
)
|
)
|
||||||
@ -190,12 +190,21 @@ func (expc *expandController) pvcUpdate(oldObj, newObj interface{}) {
|
|||||||
if newPVC == nil || !ok {
|
if newPVC == nil || !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pv, err := getPersistentVolume(newPVC, expc.pvLister)
|
|
||||||
if err != nil {
|
newSize := newPVC.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
glog.V(5).Infof("Error getting Persistent Volume for pvc %q : %v", newPVC.UID, err)
|
oldSize := oldPvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
return
|
|
||||||
|
// 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) {
|
func getPersistentVolume(pvc *v1.PersistentVolumeClaim, pvLister corelisters.PersistentVolumeLister) (*v1.PersistentVolume, error) {
|
||||||
|
@ -25,8 +25,8 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
"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/util/goroutinemap/exponentialbackoff"
|
||||||
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -96,6 +96,8 @@ func markPVCResizeInProgress(pvcWithResizeRequest *cache.PVCWithResizeRequest, k
|
|||||||
LastTransitionTime: metav1.Now(),
|
LastTransitionTime: metav1.Now(),
|
||||||
}
|
}
|
||||||
conditions := []v1.PersistentVolumeClaimCondition{progressCondition}
|
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)
|
||||||
}
|
}
|
||||||
|
@ -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"],
|
|
||||||
)
|
|
@ -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
|
|
||||||
}
|
|
@ -52,6 +52,7 @@ const (
|
|||||||
FailedDetachVolume = "FailedDetachVolume"
|
FailedDetachVolume = "FailedDetachVolume"
|
||||||
FailedMountVolume = "FailedMount"
|
FailedMountVolume = "FailedMount"
|
||||||
VolumeResizeFailed = "VolumeResizeFailed"
|
VolumeResizeFailed = "VolumeResizeFailed"
|
||||||
|
VolumeResizeSuccess = "VolumeResizeSuccessful"
|
||||||
FileSystemResizeFailed = "FileSystemResizeFailed"
|
FileSystemResizeFailed = "FileSystemResizeFailed"
|
||||||
FileSystemResizeSuccess = "FileSystemResizeSuccessful"
|
FileSystemResizeSuccess = "FileSystemResizeSuccessful"
|
||||||
FailedUnMountVolume = "FailedUnMount"
|
FailedUnMountVolume = "FailedUnMount"
|
||||||
|
@ -16,6 +16,7 @@ go_library(
|
|||||||
"finalizer.go",
|
"finalizer.go",
|
||||||
"io_util.go",
|
"io_util.go",
|
||||||
"metrics.go",
|
"metrics.go",
|
||||||
|
"resize_util.go",
|
||||||
"util.go",
|
"util.go",
|
||||||
] + select({
|
] + select({
|
||||||
"@io_bazel_rules_go//go/platform:android": [
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
@ -90,6 +91,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/types: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/sets:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
] + select({
|
] + select({
|
||||||
"@io_bazel_rules_go//go/platform:android": [
|
"@io_bazel_rules_go//go/platform:android": [
|
||||||
@ -134,6 +136,7 @@ go_library(
|
|||||||
go_test(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"resize_util_test.go",
|
||||||
"util_test.go",
|
"util_test.go",
|
||||||
] + select({
|
] + select({
|
||||||
"@io_bazel_rules_go//go/platform:linux": [
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
@ -149,6 +152,7 @@ go_test(
|
|||||||
"//pkg/apis/core/v1/helper:go_default_library",
|
"//pkg/apis/core/v1/helper:go_default_library",
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1: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/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/util/testing:go_default_library",
|
"//vendor/k8s.io/client-go/util/testing:go_default_library",
|
||||||
|
@ -29,7 +29,6 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_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/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/types: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/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package operationexecutor
|
package operationexecutor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -27,7 +26,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
@ -640,7 +638,7 @@ func (og *operationGenerator) resizeFileSystem(volumeToMount VolumeToMount, devi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// File system resize succeeded, now update the PVC's Capacity to match the PV's
|
// 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 {
|
if err != nil {
|
||||||
// On retry, resizeFileSystem will be called again but do nothing
|
// On retry, resizeFileSystem will be called again but do nothing
|
||||||
return volumeToMount.GenerateError("MountVolume.resizeFileSystem update PVC status failed", err)
|
return volumeToMount.GenerateError("MountVolume.resizeFileSystem update PVC status failed", err)
|
||||||
@ -1293,9 +1291,17 @@ func (og *operationGenerator) GenerateExpandVolumeFunc(
|
|||||||
detailedErr := fmt.Errorf("Error marking pvc %s as resized : %v", pvcWithResizeRequest.QualifiedName(), err)
|
detailedErr := fmt.Errorf("Error marking pvc %s as resized : %v", pvcWithResizeRequest.QualifiedName(), err)
|
||||||
return detailedErr, detailedErr
|
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
|
return nil, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eventRecorderFunc := func(err *error) {
|
eventRecorderFunc := func(err *error) {
|
||||||
@ -1360,31 +1366,3 @@ func isDeviceOpened(deviceToDetach AttachedVolume, mounter mount.Interface) (boo
|
|||||||
}
|
}
|
||||||
return deviceOpened, nil
|
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
|
|
||||||
}
|
|
||||||
|
125
pkg/volume/util/resize_util.go
Normal file
125
pkg/volume/util/resize_util.go
Normal file
@ -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
|
||||||
|
}
|
167
pkg/volume/util/resize_util_test.go
Normal file
167
pkg/volume/util/resize_util_test.go
Normal file
@ -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
|
||||||
|
}
|
@ -657,6 +657,8 @@ type PersistentVolumeClaimConditionType string
|
|||||||
const (
|
const (
|
||||||
// PersistentVolumeClaimResizing - a user trigger resize of pvc has been started
|
// PersistentVolumeClaimResizing - a user trigger resize of pvc has been started
|
||||||
PersistentVolumeClaimResizing PersistentVolumeClaimConditionType = "Resizing"
|
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
|
// PersistentVolumeClaimCondition contails details about state of pvc
|
||||||
|
@ -60,6 +60,7 @@ var _ = utils.SIGDescribe("Volume expand [Feature:ExpandPersistentVolumes] [Slow
|
|||||||
}
|
}
|
||||||
resizableSc, err = createResizableStorageClass(test, ns, "resizing", c)
|
resizableSc, err = createResizableStorageClass(test, ns, "resizing", c)
|
||||||
Expect(err).NotTo(HaveOccurred(), "Error creating resizable storage class")
|
Expect(err).NotTo(HaveOccurred(), "Error creating resizable storage class")
|
||||||
|
Expect(resizableSc.AllowVolumeExpansion).NotTo(BeNil())
|
||||||
Expect(*resizableSc.AllowVolumeExpansion).To(BeTrue())
|
Expect(*resizableSc.AllowVolumeExpansion).To(BeTrue())
|
||||||
|
|
||||||
pvc = newClaim(test, ns, "default")
|
pvc = newClaim(test, ns, "default")
|
||||||
@ -108,8 +109,8 @@ var _ = utils.SIGDescribe("Volume expand [Feature:ExpandPersistentVolumes] [Slow
|
|||||||
Expect(err).NotTo(HaveOccurred(), "While fetching pvc after controller resize")
|
Expect(err).NotTo(HaveOccurred(), "While fetching pvc after controller resize")
|
||||||
|
|
||||||
inProgressConditions := pvc.Status.Conditions
|
inProgressConditions := pvc.Status.Conditions
|
||||||
Expect(len(inProgressConditions)).To(Equal(1), "pvc must have resize condition")
|
Expect(len(inProgressConditions)).To(Equal(1), "pvc must have file system resize pending condition")
|
||||||
Expect(inProgressConditions[0].Type).To(Equal(v1.PersistentVolumeClaimResizing), "pvc must have resizing condition")
|
Expect(inProgressConditions[0].Type).To(Equal(v1.PersistentVolumeClaimFileSystemResizePending), "pvc must have fs resizing condition")
|
||||||
|
|
||||||
By("Deleting the previously created pod")
|
By("Deleting the previously created pod")
|
||||||
err = framework.DeletePodWithWait(f, c, pod)
|
err = framework.DeletePodWithWait(f, c, pod)
|
||||||
|
Loading…
Reference in New Issue
Block a user