mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 14:07:14 +00:00
Use workqueue model in expand controller
Add a test for patch creation
This commit is contained in:
parent
851afa0bea
commit
c16a555654
@ -7,14 +7,12 @@ go_library(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"expand_controller.go",
|
"expand_controller.go",
|
||||||
"pvc_populator.go",
|
"pvc_populator.go",
|
||||||
"sync_volume_resize.go",
|
|
||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/pkg/controller/volume/expand",
|
importpath = "k8s.io/kubernetes/pkg/controller/volume/expand",
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/controller:go_default_library",
|
"//pkg/controller:go_default_library",
|
||||||
"//pkg/controller/volume/events:go_default_library",
|
"//pkg/controller/volume/events:go_default_library",
|
||||||
"//pkg/controller/volume/expand/cache:go_default_library",
|
"//pkg/controller/volume/expand/cache:go_default_library",
|
||||||
"//pkg/util/goroutinemap/exponentialbackoff:go_default_library",
|
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
@ -23,7 +21,7 @@ go_library(
|
|||||||
"//pkg/volume/util/volumepathhandler:go_default_library",
|
"//pkg/volume/util/volumepathhandler:go_default_library",
|
||||||
"//staging/src/k8s.io/api/authentication/v1:go_default_library",
|
"//staging/src/k8s.io/api/authentication/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
@ -35,6 +33,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
|
||||||
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
|
24
pkg/controller/volume/expand/cache/BUILD
vendored
24
pkg/controller/volume/expand/cache/BUILD
vendored
@ -1,15 +1,10 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
load(
|
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
|
||||||
"go_library",
|
|
||||||
"go_test",
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_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",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/volume/util:go_default_library",
|
"//pkg/volume/util:go_default_library",
|
||||||
"//pkg/volume/util/types:go_default_library",
|
"//pkg/volume/util/types:go_default_library",
|
||||||
@ -34,18 +29,5 @@ filegroup(
|
|||||||
name = "all-srcs",
|
name = "all-srcs",
|
||||||
srcs = [":package-srcs"],
|
srcs = [":package-srcs"],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
visibility = ["//visibility:public"],
|
||||||
|
|
||||||
go_test(
|
|
||||||
name = "go_default_test",
|
|
||||||
srcs = ["volume_resize_map_test.go"],
|
|
||||||
embed = [":go_default_library"],
|
|
||||||
deps = [
|
|
||||||
"//pkg/volume/util/types:go_default_library",
|
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
|
||||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
@ -1,147 +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 cache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
|
||||||
"k8s.io/kubernetes/pkg/volume/util/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_AddValidPVCUpdate(t *testing.T) {
|
|
||||||
claim := testVolumeClaim("foo", "ns", v1.PersistentVolumeClaimSpec{
|
|
||||||
AccessModes: []v1.PersistentVolumeAccessMode{
|
|
||||||
v1.ReadWriteOnce,
|
|
||||||
v1.ReadOnlyMany,
|
|
||||||
},
|
|
||||||
Resources: v1.ResourceRequirements{
|
|
||||||
Requests: v1.ResourceList{
|
|
||||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse("12G"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
VolumeName: "foo",
|
|
||||||
})
|
|
||||||
|
|
||||||
unboundClaim := claim.DeepCopy()
|
|
||||||
unboundClaim.Status.Phase = v1.ClaimPending
|
|
||||||
|
|
||||||
noResizeClaim := claim.DeepCopy()
|
|
||||||
noResizeClaim.Status.Capacity = v1.ResourceList{
|
|
||||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse("12G"),
|
|
||||||
}
|
|
||||||
|
|
||||||
boundPV := getPersistentVolume("foo", resource.MustParse("10G"), claim)
|
|
||||||
unboundPV := getPersistentVolume("foo", resource.MustParse("10G"), nil)
|
|
||||||
misboundPV := getPersistentVolume("foo", resource.MustParse("10G"), nil)
|
|
||||||
misboundPV.Spec.ClaimRef = &v1.ObjectReference{
|
|
||||||
Namespace: "someOtherNamespace",
|
|
||||||
Name: "someOtherName",
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pvc *v1.PersistentVolumeClaim
|
|
||||||
pv *v1.PersistentVolume
|
|
||||||
expectedPVCs int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"validPVCUpdate",
|
|
||||||
claim,
|
|
||||||
boundPV,
|
|
||||||
1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"noResizeRequired",
|
|
||||||
noResizeClaim,
|
|
||||||
boundPV,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"unboundPVC",
|
|
||||||
unboundClaim,
|
|
||||||
boundPV,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"unboundPV",
|
|
||||||
claim,
|
|
||||||
unboundPV,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"misboundPV",
|
|
||||||
claim,
|
|
||||||
misboundPV,
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
resizeMap := createTestVolumeResizeMap()
|
|
||||||
pvc := test.pvc.DeepCopy()
|
|
||||||
pv := test.pv.DeepCopy()
|
|
||||||
resizeMap.AddPVCUpdate(pvc, pv)
|
|
||||||
pvcr := resizeMap.GetPVCsWithResizeRequest()
|
|
||||||
if len(pvcr) != test.expectedPVCs {
|
|
||||||
t.Errorf("Test %q expected %d pvc resize request got %d", test.name, test.expectedPVCs, len(pvcr))
|
|
||||||
}
|
|
||||||
if test.expectedPVCs > 0 {
|
|
||||||
assert.Equal(t, resource.MustParse("12G"), pvcr[0].ExpectedSize, test.name)
|
|
||||||
}
|
|
||||||
assert.Equal(t, 0, len(resizeMap.pvcrs), test.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestVolumeResizeMap() *volumeResizeMap {
|
|
||||||
fakeClient := &fake.Clientset{}
|
|
||||||
resizeMap := &volumeResizeMap{}
|
|
||||||
resizeMap.pvcrs = make(map[types.UniquePVCName]*PVCWithResizeRequest)
|
|
||||||
resizeMap.kubeClient = fakeClient
|
|
||||||
return resizeMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func testVolumeClaim(name string, namespace string, spec v1.PersistentVolumeClaimSpec) *v1.PersistentVolumeClaim {
|
|
||||||
return &v1.PersistentVolumeClaim{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
|
|
||||||
Spec: spec,
|
|
||||||
Status: v1.PersistentVolumeClaimStatus{
|
|
||||||
Phase: v1.ClaimBound,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPersistentVolume(volumeName string, capacity resource.Quantity, pvc *v1.PersistentVolumeClaim) *v1.PersistentVolume {
|
|
||||||
volume := &v1.PersistentVolume{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: volumeName},
|
|
||||||
Spec: v1.PersistentVolumeSpec{
|
|
||||||
Capacity: v1.ResourceList{
|
|
||||||
v1.ResourceName(v1.ResourceStorage): capacity,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if pvc != nil {
|
|
||||||
volume.Spec.ClaimRef = &v1.ObjectReference{
|
|
||||||
Namespace: pvc.Namespace,
|
|
||||||
Name: pvc.Name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return volume
|
|
||||||
}
|
|
@ -14,9 +14,6 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Package expand implements interfaces that attempt to resize a pvc
|
|
||||||
// by adding pvc to a volume resize map from which PVCs are picked and
|
|
||||||
// resized
|
|
||||||
package expand
|
package expand
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -28,8 +25,10 @@ import (
|
|||||||
|
|
||||||
authenticationv1 "k8s.io/api/authentication/v1"
|
authenticationv1 "k8s.io/api/authentication/v1"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
@ -37,10 +36,10 @@ import (
|
|||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
corelisters "k8s.io/client-go/listers/core/v1"
|
||||||
kcache "k8s.io/client-go/tools/cache"
|
kcache "k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
|
"k8s.io/client-go/util/workqueue"
|
||||||
cloudprovider "k8s.io/cloud-provider"
|
cloudprovider "k8s.io/cloud-provider"
|
||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/events"
|
"k8s.io/kubernetes/pkg/controller/volume/events"
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
@ -50,10 +49,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// How often resizing loop runs
|
// number of default volume expansion workers
|
||||||
syncLoopPeriod time.Duration = 400 * time.Millisecond
|
defaultWorkerCount = 10
|
||||||
// How often pvc populator runs
|
|
||||||
populatorLoopPeriod time.Duration = 2 * time.Minute
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExpandController expands the pvs
|
// ExpandController expands the pvs
|
||||||
@ -84,17 +81,9 @@ type expandController struct {
|
|||||||
// recorder is used to record events in the API server
|
// recorder is used to record events in the API server
|
||||||
recorder record.EventRecorder
|
recorder record.EventRecorder
|
||||||
|
|
||||||
// Volume resize map of volumes that needs resizing
|
operationGenerator operationexecutor.OperationGenerator
|
||||||
resizeMap cache.VolumeResizeMap
|
|
||||||
|
|
||||||
// Worker goroutine to process resize requests from resizeMap
|
queue workqueue.RateLimitingInterface
|
||||||
syncResize SyncVolumeResize
|
|
||||||
|
|
||||||
// Operation executor
|
|
||||||
opExecutor operationexecutor.OperationExecutor
|
|
||||||
|
|
||||||
// populator for periodically polling all PVCs
|
|
||||||
pvcPopulator PVCPopulator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExpandController(
|
func NewExpandController(
|
||||||
@ -111,6 +100,7 @@ func NewExpandController(
|
|||||||
pvcsSynced: pvcInformer.Informer().HasSynced,
|
pvcsSynced: pvcInformer.Informer().HasSynced,
|
||||||
pvLister: pvInformer.Lister(),
|
pvLister: pvInformer.Lister(),
|
||||||
pvSynced: pvInformer.Informer().HasSynced,
|
pvSynced: pvInformer.Informer().HasSynced,
|
||||||
|
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "expand"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := expc.volumePluginMgr.InitPlugins(plugins, nil, expc); err != nil {
|
if err := expc.volumePluginMgr.InitPlugins(plugins, nil, expc); err != nil {
|
||||||
@ -123,33 +113,130 @@ func NewExpandController(
|
|||||||
expc.recorder = eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "volume_expand"})
|
expc.recorder = eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "volume_expand"})
|
||||||
blkutil := volumepathhandler.NewBlockVolumePathHandler()
|
blkutil := volumepathhandler.NewBlockVolumePathHandler()
|
||||||
|
|
||||||
expc.opExecutor = operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator(
|
expc.operationGenerator = operationexecutor.NewOperationGenerator(
|
||||||
kubeClient,
|
kubeClient,
|
||||||
&expc.volumePluginMgr,
|
&expc.volumePluginMgr,
|
||||||
expc.recorder,
|
expc.recorder,
|
||||||
false,
|
false,
|
||||||
blkutil))
|
blkutil)
|
||||||
|
|
||||||
expc.resizeMap = cache.NewVolumeResizeMap(expc.kubeClient)
|
|
||||||
|
|
||||||
pvcInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{
|
pvcInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{
|
||||||
UpdateFunc: expc.pvcUpdate,
|
AddFunc: expc.enqueuePVC,
|
||||||
DeleteFunc: expc.deletePVC,
|
UpdateFunc: func(old, new interface{}) {
|
||||||
|
oldPVC := old.(*v1.PersistentVolumeClaim)
|
||||||
|
oldSize := oldPVC.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
|
||||||
|
newPVC := new.(*v1.PersistentVolumeClaim)
|
||||||
|
newSize := newPVC.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
if newSize.Cmp(oldSize) > 0 {
|
||||||
|
expc.enqueuePVC(new)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DeleteFunc: expc.enqueuePVC,
|
||||||
})
|
})
|
||||||
|
|
||||||
expc.syncResize = NewSyncVolumeResize(syncLoopPeriod, expc.opExecutor, expc.resizeMap, kubeClient)
|
|
||||||
expc.pvcPopulator = NewPVCPopulator(
|
|
||||||
populatorLoopPeriod,
|
|
||||||
expc.resizeMap,
|
|
||||||
expc.pvcLister,
|
|
||||||
expc.pvLister,
|
|
||||||
&expc.volumePluginMgr,
|
|
||||||
kubeClient)
|
|
||||||
return expc, nil
|
return expc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (expc *expandController) enqueuePVC(obj interface{}) {
|
||||||
|
pvc := obj.(*v1.PersistentVolumeClaim)
|
||||||
|
size := pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
|
statusSize := pvc.Status.Capacity[v1.ResourceStorage]
|
||||||
|
|
||||||
|
if pvc.Status.Phase == v1.ClaimBound && size.Cmp(statusSize) > 0 {
|
||||||
|
key, err := kcache.DeletionHandlingMetaNamespaceKeyFunc(pvc)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HandleError(fmt.Errorf("Couldn't get key for object %#v: %v", pvc, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expc.queue.Add(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (expc *expandController) processNextWorkItem() bool {
|
||||||
|
key, shutdown := expc.queue.Get()
|
||||||
|
if shutdown {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer expc.queue.Done(key)
|
||||||
|
|
||||||
|
err := expc.syncHandler(key.(string))
|
||||||
|
if err == nil {
|
||||||
|
expc.queue.Forget(key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.HandleError(fmt.Errorf("%v failed with : %v", key, err))
|
||||||
|
expc.queue.AddRateLimited(key)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (expc *expandController) syncHandler(key string) error {
|
||||||
|
namespace, name, err := kcache.SplitMetaNamespaceKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pvc, err := expc.pvcLister.PersistentVolumeClaims(namespace).Get(name)
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
klog.V(5).Infof("Error getting PVC %q (uid: %q) from informer : %v", util.GetPersistentVolumeClaimQualifiedName(pvc), pvc.UID, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pv, err := getPersistentVolume(pvc, expc.pvLister)
|
||||||
|
if err != nil {
|
||||||
|
klog.V(5).Infof("Error getting Persistent Volume for PVC %q (uid: %q) from informer : %v", util.GetPersistentVolumeClaimQualifiedName(pvc), pvc.UID, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if pv.Spec.ClaimRef == nil || pvc.Namespace != pv.Spec.ClaimRef.Namespace || pvc.Name != pv.Spec.ClaimRef.Name {
|
||||||
|
err := fmt.Errorf("Persistent Volume is not bound to PVC being updated : %s", util.ClaimToClaimKey(pvc))
|
||||||
|
klog.V(4).Infof("%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeSpec := volume.NewSpecFromPersistentVolume(pv, false)
|
||||||
|
volumePlugin, err := expc.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec)
|
||||||
|
if err != nil || volumePlugin == nil {
|
||||||
|
msg := fmt.Errorf("didn't find a plugin capable of expanding the volume; " +
|
||||||
|
"waiting for an external controller to process this PVC")
|
||||||
|
eventType := v1.EventTypeNormal
|
||||||
|
if err != nil {
|
||||||
|
eventType = v1.EventTypeWarning
|
||||||
|
}
|
||||||
|
expc.recorder.Event(pvc, eventType, events.ExternalExpanding, fmt.Sprintf("Ignoring the PVC: %v.", msg))
|
||||||
|
klog.V(3).Infof("Ignoring the PVC %q (uid: %q) : %v.", util.GetPersistentVolumeClaimQualifiedName(pvc), pvc.UID, msg)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return expc.expand(pvc, pv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (expc *expandController) expand(pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume) error {
|
||||||
|
pvc, err := util.MarkResizeInProgress(pvc, expc.kubeClient)
|
||||||
|
if err != nil {
|
||||||
|
klog.V(5).Infof("Error setting PVC %s in progress with error : %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedOperations, err := expc.operationGenerator.GenerateExpandVolumeFunc(pvc, pv)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Error starting ExpandVolume for pvc %s with %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
klog.V(5).Infof("Starting ExpandVolume for volume %s", util.GetPersistentVolumeClaimQualifiedName(pvc))
|
||||||
|
_, detailedErr := generatedOperations.Run()
|
||||||
|
|
||||||
|
return detailedErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO make concurrency configurable (workers/threadiness argument). previously, nestedpendingoperations spawned unlimited goroutines
|
||||||
func (expc *expandController) Run(stopCh <-chan struct{}) {
|
func (expc *expandController) Run(stopCh <-chan struct{}) {
|
||||||
defer runtime.HandleCrash()
|
defer runtime.HandleCrash()
|
||||||
|
defer expc.queue.ShutDown()
|
||||||
|
|
||||||
klog.Infof("Starting expand controller")
|
klog.Infof("Starting expand controller")
|
||||||
defer klog.Infof("Shutting down expand controller")
|
defer klog.Infof("Shutting down expand controller")
|
||||||
|
|
||||||
@ -157,73 +244,15 @@ func (expc *expandController) Run(stopCh <-chan struct{}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run volume sync work goroutine
|
for i := 0; i < defaultWorkerCount; i++ {
|
||||||
go expc.syncResize.Run(stopCh)
|
go wait.Until(expc.runWorker, time.Second, stopCh)
|
||||||
// Start the pvc populator loop
|
}
|
||||||
go expc.pvcPopulator.Run(stopCh)
|
|
||||||
<-stopCh
|
<-stopCh
|
||||||
}
|
}
|
||||||
|
|
||||||
func (expc *expandController) deletePVC(obj interface{}) {
|
func (expc *expandController) runWorker() {
|
||||||
pvc, ok := obj.(*v1.PersistentVolumeClaim)
|
for expc.processNextWorkItem() {
|
||||||
if !ok {
|
|
||||||
tombstone, ok := obj.(kcache.DeletedFinalStateUnknown)
|
|
||||||
if !ok {
|
|
||||||
runtime.HandleError(fmt.Errorf("couldn't get object from tombstone %+v", obj))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pvc, ok = tombstone.Obj.(*v1.PersistentVolumeClaim)
|
|
||||||
if !ok {
|
|
||||||
runtime.HandleError(fmt.Errorf("tombstone contained object that is not a pvc %#v", obj))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expc.resizeMap.DeletePVC(pvc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (expc *expandController) pvcUpdate(oldObj, newObj interface{}) {
|
|
||||||
oldPVC, ok := oldObj.(*v1.PersistentVolumeClaim)
|
|
||||||
|
|
||||||
if oldPVC == nil || !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
newPVC, ok := newObj.(*v1.PersistentVolumeClaim)
|
|
||||||
|
|
||||||
if newPVC == nil || !ok {
|
|
||||||
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 {
|
|
||||||
klog.V(5).Infof("Error getting Persistent Volume for PVC %q : %v", newPVC.UID, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeSpec := volume.NewSpecFromPersistentVolume(pv, false)
|
|
||||||
volumePlugin, err := expc.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec)
|
|
||||||
if err != nil || volumePlugin == nil {
|
|
||||||
retErr := fmt.Errorf("didn't find a plugin capable of expanding the volume; " +
|
|
||||||
"waiting for an external controller to process this PVC")
|
|
||||||
eventType := v1.EventTypeNormal
|
|
||||||
if err != nil {
|
|
||||||
eventType = v1.EventTypeWarning
|
|
||||||
}
|
|
||||||
expc.recorder.Event(newPVC, eventType, events.ExternalExpanding,
|
|
||||||
fmt.Sprintf("Ignoring the PVC: %v.", retErr))
|
|
||||||
klog.V(3).Infof("Ignoring the PVC %q (uid: %q) : %v.",
|
|
||||||
util.GetPersistentVolumeClaimQualifiedName(newPVC), newPVC.UID, retErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
expc.resizeMap.AddPVCUpdate(newPVC, pv)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,103 +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 expand
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/klog"
|
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
|
||||||
"k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff"
|
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
|
||||||
"k8s.io/kubernetes/pkg/volume/util/operationexecutor"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SyncVolumeResize interface {
|
|
||||||
Run(stopCh <-chan struct{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type syncResize struct {
|
|
||||||
loopPeriod time.Duration
|
|
||||||
resizeMap cache.VolumeResizeMap
|
|
||||||
opsExecutor operationexecutor.OperationExecutor
|
|
||||||
kubeClient clientset.Interface
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSyncVolumeResize returns actual volume resize handler
|
|
||||||
func NewSyncVolumeResize(
|
|
||||||
loopPeriod time.Duration,
|
|
||||||
opsExecutor operationexecutor.OperationExecutor,
|
|
||||||
resizeMap cache.VolumeResizeMap,
|
|
||||||
kubeClient clientset.Interface) SyncVolumeResize {
|
|
||||||
rc := &syncResize{
|
|
||||||
loopPeriod: loopPeriod,
|
|
||||||
opsExecutor: opsExecutor,
|
|
||||||
resizeMap: resizeMap,
|
|
||||||
kubeClient: kubeClient,
|
|
||||||
}
|
|
||||||
return rc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rc *syncResize) Run(stopCh <-chan struct{}) {
|
|
||||||
wait.Until(rc.Sync, rc.loopPeriod, stopCh)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rc *syncResize) Sync() {
|
|
||||||
// Resize PVCs that require resize
|
|
||||||
for _, pvcWithResizeRequest := range rc.resizeMap.GetPVCsWithResizeRequest() {
|
|
||||||
uniqueVolumeKey := v1.UniqueVolumeName(pvcWithResizeRequest.UniquePVCKey())
|
|
||||||
if rc.opsExecutor.IsOperationPending(uniqueVolumeKey, "") {
|
|
||||||
klog.V(10).Infof("Operation for PVC %s is already pending", pvcWithResizeRequest.QualifiedName())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedClaim, err := markPVCResizeInProgress(pvcWithResizeRequest, rc.kubeClient)
|
|
||||||
if err != nil {
|
|
||||||
klog.V(5).Infof("Error setting PVC %s in progress with error : %v", pvcWithResizeRequest.QualifiedName(), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if updatedClaim != nil {
|
|
||||||
pvcWithResizeRequest.PVC = updatedClaim
|
|
||||||
}
|
|
||||||
|
|
||||||
growFuncError := rc.opsExecutor.ExpandVolume(pvcWithResizeRequest, rc.resizeMap)
|
|
||||||
if growFuncError != nil && !exponentialbackoff.IsExponentialBackoff(growFuncError) {
|
|
||||||
klog.Errorf("Error growing pvc %s with %v", pvcWithResizeRequest.QualifiedName(), growFuncError)
|
|
||||||
}
|
|
||||||
if growFuncError == nil {
|
|
||||||
klog.V(5).Infof("Started opsExecutor.ExpandVolume for volume %s", pvcWithResizeRequest.QualifiedName())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func markPVCResizeInProgress(pvcWithResizeRequest *cache.PVCWithResizeRequest, kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
|
||||||
// Mark PVC as Resize Started
|
|
||||||
progressCondition := v1.PersistentVolumeClaimCondition{
|
|
||||||
Type: v1.PersistentVolumeClaimResizing,
|
|
||||||
Status: v1.ConditionTrue,
|
|
||||||
LastTransitionTime: metav1.Now(),
|
|
||||||
}
|
|
||||||
conditions := []v1.PersistentVolumeClaimCondition{progressCondition}
|
|
||||||
newPVC := pvcWithResizeRequest.PVC.DeepCopy()
|
|
||||||
newPVC = util.MergeResizeConditionOnPVC(newPVC, conditions)
|
|
||||||
|
|
||||||
return util.PatchPVCStatus(pvcWithResizeRequest.PVC /*oldPVC*/, newPVC, kubeClient)
|
|
||||||
}
|
|
@ -29,8 +29,10 @@ go_library(
|
|||||||
"//pkg/volume/util/volumepathhandler:go_default_library",
|
"//pkg/volume/util/volumepathhandler:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/storage/v1:go_default_library",
|
"//staging/src/k8s.io/api/storage/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
@ -143,15 +143,7 @@ func (grm *nestedPendingOperations) Run(
|
|||||||
defer k8sRuntime.HandleCrash()
|
defer k8sRuntime.HandleCrash()
|
||||||
// Handle completion of and error, if any, from operationFunc()
|
// Handle completion of and error, if any, from operationFunc()
|
||||||
defer grm.operationComplete(volumeName, podName, &detailedErr)
|
defer grm.operationComplete(volumeName, podName, &detailedErr)
|
||||||
if generatedOperations.CompleteFunc != nil {
|
return generatedOperations.Run()
|
||||||
defer generatedOperations.CompleteFunc(&detailedErr)
|
|
||||||
}
|
|
||||||
if generatedOperations.EventRecorderFunc != nil {
|
|
||||||
defer generatedOperations.EventRecorderFunc(&eventErr)
|
|
||||||
}
|
|
||||||
// Handle panic, if any, from operationFunc()
|
|
||||||
defer k8sRuntime.RecoverFromPanic(&detailedErr)
|
|
||||||
return generatedOperations.OperationFunc()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -14,7 +14,6 @@ go_library(
|
|||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/pkg/volume/util/operationexecutor",
|
importpath = "k8s.io/kubernetes/pkg/volume/util/operationexecutor",
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/controller/volume/expand/cache:go_default_library",
|
|
||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
"//pkg/kubelet/events:go_default_library",
|
"//pkg/kubelet/events:go_default_library",
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
@ -43,7 +42,6 @@ go_test(
|
|||||||
srcs = ["operation_executor_test.go"],
|
srcs = ["operation_executor_test.go"],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/controller/volume/expand/cache:go_default_library",
|
|
||||||
"//pkg/util/mount:go_default_library",
|
"//pkg/util/mount:go_default_library",
|
||||||
"//pkg/volume:go_default_library",
|
"//pkg/volume:go_default_library",
|
||||||
"//pkg/volume/util/types:go_default_library",
|
"//pkg/volume/util/types:go_default_library",
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
expandcache "k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
"k8s.io/kubernetes/pkg/volume/util"
|
"k8s.io/kubernetes/pkg/volume/util"
|
||||||
@ -141,7 +140,7 @@ type OperationExecutor interface {
|
|||||||
// otherwise it returns false
|
// otherwise it returns false
|
||||||
IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName) bool
|
IsOperationPending(volumeName v1.UniqueVolumeName, podName volumetypes.UniquePodName) bool
|
||||||
// Expand Volume will grow size available to PVC
|
// Expand Volume will grow size available to PVC
|
||||||
ExpandVolume(*expandcache.PVCWithResizeRequest, expandcache.VolumeResizeMap) error
|
ExpandVolume(*v1.PersistentVolumeClaim, *v1.PersistentVolume) error
|
||||||
// ExpandVolumeFSWithoutUnmounting will resize volume's file system to expected size without unmounting the volume.
|
// ExpandVolumeFSWithoutUnmounting will resize volume's file system to expected size without unmounting the volume.
|
||||||
ExpandVolumeFSWithoutUnmounting(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error
|
ExpandVolumeFSWithoutUnmounting(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) error
|
||||||
// ReconstructVolumeOperation construct a new volumeSpec and returns it created by plugin
|
// ReconstructVolumeOperation construct a new volumeSpec and returns it created by plugin
|
||||||
@ -818,13 +817,12 @@ func (oe *operationExecutor) UnmountDevice(
|
|||||||
deviceToDetach.VolumeName, podName, generatedOperations)
|
deviceToDetach.VolumeName, podName, generatedOperations)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oe *operationExecutor) ExpandVolume(pvcWithResizeRequest *expandcache.PVCWithResizeRequest, resizeMap expandcache.VolumeResizeMap) error {
|
func (oe *operationExecutor) ExpandVolume(pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume) error {
|
||||||
generatedOperations, err := oe.operationGenerator.GenerateExpandVolumeFunc(pvcWithResizeRequest, resizeMap)
|
generatedOperations, err := oe.operationGenerator.GenerateExpandVolumeFunc(pvc, pv)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
uniqueVolumeKey := v1.UniqueVolumeName(pvcWithResizeRequest.UniquePVCKey())
|
uniqueVolumeKey := v1.UniqueVolumeName(pvc.UID)
|
||||||
|
|
||||||
return oe.pendingOperations.Run(uniqueVolumeKey, "", generatedOperations)
|
return oe.pendingOperations.Run(uniqueVolumeKey, "", generatedOperations)
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
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/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
expandcache "k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
volumetypes "k8s.io/kubernetes/pkg/volume/util/types"
|
||||||
@ -453,8 +452,7 @@ func (fopg *fakeOperationGenerator) GenerateVerifyControllerAttachedVolumeFunc(v
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fopg *fakeOperationGenerator) GenerateExpandVolumeFunc(pvcWithResizeRequest *expandcache.PVCWithResizeRequest,
|
func (fopg *fakeOperationGenerator) GenerateExpandVolumeFunc(pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume) (volumetypes.GeneratedOperations, error) {
|
||||||
resizeMap expandcache.VolumeResizeMap) (volumetypes.GeneratedOperations, error) {
|
|
||||||
opFunc := func() (error, error) {
|
opFunc := func() (error, error) {
|
||||||
startOperationAndBlock(fopg.ch, fopg.quit)
|
startOperationAndBlock(fopg.ch, fopg.quit)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -34,7 +34,6 @@ import (
|
|||||||
volerr "k8s.io/cloud-provider/volume/errors"
|
volerr "k8s.io/cloud-provider/volume/errors"
|
||||||
csilib "k8s.io/csi-translation-lib"
|
csilib "k8s.io/csi-translation-lib"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
expandcache "k8s.io/kubernetes/pkg/controller/volume/expand/cache"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
kevents "k8s.io/kubernetes/pkg/kubelet/events"
|
kevents "k8s.io/kubernetes/pkg/kubelet/events"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
@ -129,7 +128,7 @@ type OperationGenerator interface {
|
|||||||
string,
|
string,
|
||||||
map[*volume.Spec]v1.UniqueVolumeName, ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error)
|
map[*volume.Spec]v1.UniqueVolumeName, ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error)
|
||||||
|
|
||||||
GenerateExpandVolumeFunc(*expandcache.PVCWithResizeRequest, expandcache.VolumeResizeMap) (volumetypes.GeneratedOperations, error)
|
GenerateExpandVolumeFunc(*v1.PersistentVolumeClaim, *v1.PersistentVolume) (volumetypes.GeneratedOperations, error)
|
||||||
|
|
||||||
// Generates the volume file system resize function, which can resize volume's file system to expected size without unmounting the volume.
|
// Generates the volume file system resize function, which can resize volume's file system to expected size without unmounting the volume.
|
||||||
GenerateExpandVolumeFSWithoutUnmountingFunc(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error)
|
GenerateExpandVolumeFSWithoutUnmountingFunc(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error)
|
||||||
@ -810,7 +809,7 @@ func (og *operationGenerator) resizeFileSystem(volumeToMount VolumeToMount, rsOp
|
|||||||
og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.FileSystemResizeSuccess, simpleMsg)
|
og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.FileSystemResizeSuccess, simpleMsg)
|
||||||
klog.Infof(detailedMsg)
|
klog.Infof(detailedMsg)
|
||||||
// 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 = util.MarkFSResizeFinished(pvc, pv.Spec.Capacity, og.kubeClient)
|
err = util.MarkFSResizeFinished(pvc, pvSpecCap, 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 false, fmt.Errorf("MountVolume.resizeFileSystem update PVC status failed : %v", err)
|
return false, fmt.Errorf("MountVolume.resizeFileSystem update PVC status failed : %v", err)
|
||||||
@ -1494,47 +1493,47 @@ func (og *operationGenerator) verifyVolumeIsSafeToDetach(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (og *operationGenerator) GenerateExpandVolumeFunc(
|
func (og *operationGenerator) GenerateExpandVolumeFunc(
|
||||||
pvcWithResizeRequest *expandcache.PVCWithResizeRequest,
|
pvc *v1.PersistentVolumeClaim,
|
||||||
resizeMap expandcache.VolumeResizeMap) (volumetypes.GeneratedOperations, error) {
|
pv *v1.PersistentVolume) (volumetypes.GeneratedOperations, error) {
|
||||||
|
|
||||||
volumeSpec := volume.NewSpecFromPersistentVolume(pvcWithResizeRequest.PersistentVolume, false)
|
volumeSpec := volume.NewSpecFromPersistentVolume(pv, false)
|
||||||
|
|
||||||
volumePlugin, err := og.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec)
|
volumePlugin, err := og.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return volumetypes.GeneratedOperations{}, fmt.Errorf("Error finding plugin for expanding volume: %q with error %v", pvcWithResizeRequest.QualifiedName(), err)
|
return volumetypes.GeneratedOperations{}, fmt.Errorf("Error finding plugin for expanding volume: %q with error %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if volumePlugin == nil {
|
if volumePlugin == nil {
|
||||||
return volumetypes.GeneratedOperations{}, fmt.Errorf("Can not find plugin for expanding volume: %q", pvcWithResizeRequest.QualifiedName())
|
return volumetypes.GeneratedOperations{}, fmt.Errorf("Can not find plugin for expanding volume: %q", util.GetPersistentVolumeClaimQualifiedName(pvc))
|
||||||
}
|
}
|
||||||
|
|
||||||
expandVolumeFunc := func() (error, error) {
|
expandVolumeFunc := func() (error, error) {
|
||||||
newSize := pvcWithResizeRequest.ExpectedSize
|
newSize := pvc.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
pvSize := pvcWithResizeRequest.PersistentVolume.Spec.Capacity[v1.ResourceStorage]
|
statusSize := pvc.Status.Capacity[v1.ResourceStorage]
|
||||||
|
pvSize := pv.Spec.Capacity[v1.ResourceStorage]
|
||||||
if pvSize.Cmp(newSize) < 0 {
|
if pvSize.Cmp(newSize) < 0 {
|
||||||
updatedSize, expandErr := volumePlugin.ExpandVolumeDevice(
|
updatedSize, expandErr := volumePlugin.ExpandVolumeDevice(
|
||||||
volumeSpec,
|
volumeSpec,
|
||||||
pvcWithResizeRequest.ExpectedSize,
|
newSize,
|
||||||
pvcWithResizeRequest.CurrentSize)
|
statusSize)
|
||||||
|
|
||||||
if expandErr != nil {
|
if expandErr != nil {
|
||||||
detailedErr := fmt.Errorf("error expanding volume %q of plugin %q: %v", pvcWithResizeRequest.QualifiedName(), volumePlugin.GetPluginName(), expandErr)
|
detailedErr := fmt.Errorf("error expanding volume %q of plugin %q: %v", util.GetPersistentVolumeClaimQualifiedName(pvc), volumePlugin.GetPluginName(), expandErr)
|
||||||
return detailedErr, detailedErr
|
return detailedErr, detailedErr
|
||||||
}
|
}
|
||||||
|
|
||||||
klog.Infof("ExpandVolume succeeded for volume %s", pvcWithResizeRequest.QualifiedName())
|
klog.Infof("ExpandVolume succeeded for volume %s", util.GetPersistentVolumeClaimQualifiedName(pvc))
|
||||||
|
|
||||||
newSize = updatedSize
|
newSize = updatedSize
|
||||||
// k8s doesn't have transactions, we can't guarantee that after updating PV - updating PVC will be
|
// k8s doesn't have transactions, we can't guarantee that after updating PV - updating PVC will be
|
||||||
// successful, that is why all PVCs for which pvc.Spec.Size > pvc.Status.Size must be reprocessed
|
// successful, that is why all PVCs for which pvc.Spec.Size > pvc.Status.Size must be reprocessed
|
||||||
// until they reflect user requested size in pvc.Status.Size
|
// until they reflect user requested size in pvc.Status.Size
|
||||||
updateErr := resizeMap.UpdatePVSize(pvcWithResizeRequest, newSize)
|
updateErr := util.UpdatePVSize(pv, newSize, og.kubeClient)
|
||||||
|
|
||||||
if updateErr != nil {
|
if updateErr != nil {
|
||||||
detailedErr := fmt.Errorf("Error updating PV spec capacity for volume %q with : %v", pvcWithResizeRequest.QualifiedName(), updateErr)
|
detailedErr := fmt.Errorf("Error updating PV spec capacity for volume %q with : %v", util.GetPersistentVolumeClaimQualifiedName(pvc), updateErr)
|
||||||
return detailedErr, detailedErr
|
return detailedErr, detailedErr
|
||||||
}
|
}
|
||||||
klog.Infof("ExpandVolume.UpdatePV succeeded for volume %s", pvcWithResizeRequest.QualifiedName())
|
|
||||||
|
klog.Infof("ExpandVolume.UpdatePV succeeded for volume %s", util.GetPersistentVolumeClaimQualifiedName(pvc))
|
||||||
}
|
}
|
||||||
|
|
||||||
fsVolume, _ := util.CheckVolumeModeFilesystem(volumeSpec)
|
fsVolume, _ := util.CheckVolumeModeFilesystem(volumeSpec)
|
||||||
@ -1542,19 +1541,18 @@ func (og *operationGenerator) GenerateExpandVolumeFunc(
|
|||||||
// Rest of the volume expand controller code will assume PVC as *not* resized until pvc.Status.Size
|
// Rest of the volume expand controller code will assume PVC as *not* resized until pvc.Status.Size
|
||||||
// reflects user requested size.
|
// reflects user requested size.
|
||||||
if !volumePlugin.RequiresFSResize() || !fsVolume {
|
if !volumePlugin.RequiresFSResize() || !fsVolume {
|
||||||
klog.V(4).Infof("Controller resizing done for PVC %s", pvcWithResizeRequest.QualifiedName())
|
klog.V(4).Infof("Controller resizing done for PVC %s", util.GetPersistentVolumeClaimQualifiedName(pvc))
|
||||||
err := resizeMap.MarkAsResized(pvcWithResizeRequest, newSize)
|
err := util.MarkResizeFinished(pvc, newSize, og.kubeClient)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
detailedErr := fmt.Errorf("Error marking pvc %s as resized : %v", pvcWithResizeRequest.QualifiedName(), err)
|
detailedErr := fmt.Errorf("Error marking pvc %s as resized : %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err)
|
||||||
return detailedErr, detailedErr
|
return detailedErr, detailedErr
|
||||||
}
|
}
|
||||||
successMsg := fmt.Sprintf("ExpandVolume succeeded for volume %s", pvcWithResizeRequest.QualifiedName())
|
successMsg := fmt.Sprintf("ExpandVolume succeeded for volume %s", util.GetPersistentVolumeClaimQualifiedName(pvc))
|
||||||
og.recorder.Eventf(pvcWithResizeRequest.PVC, v1.EventTypeNormal, kevents.VolumeResizeSuccess, successMsg)
|
og.recorder.Eventf(pvc, v1.EventTypeNormal, kevents.VolumeResizeSuccess, successMsg)
|
||||||
} else {
|
} else {
|
||||||
err := resizeMap.MarkForFSResize(pvcWithResizeRequest)
|
err := util.MarkForFSResize(pvc, og.kubeClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
detailedErr := fmt.Errorf("Error updating pvc %s condition for fs resize : %v", pvcWithResizeRequest.QualifiedName(), err)
|
detailedErr := fmt.Errorf("Error updating pvc %s condition for fs resize : %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err)
|
||||||
klog.Warning(detailedErr)
|
klog.Warning(detailedErr)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -1564,7 +1562,7 @@ func (og *operationGenerator) GenerateExpandVolumeFunc(
|
|||||||
|
|
||||||
eventRecorderFunc := func(err *error) {
|
eventRecorderFunc := func(err *error) {
|
||||||
if *err != nil {
|
if *err != nil {
|
||||||
og.recorder.Eventf(pvcWithResizeRequest.PVC, v1.EventTypeWarning, kevents.VolumeResizeFailed, (*err).Error())
|
og.recorder.Eventf(pvc, v1.EventTypeWarning, kevents.VolumeResizeFailed, (*err).Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"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"
|
||||||
@ -46,45 +50,154 @@ func ClaimToClaimKey(claim *v1.PersistentVolumeClaim) string {
|
|||||||
return fmt.Sprintf("%s/%s", claim.Namespace, claim.Name)
|
return fmt.Sprintf("%s/%s", claim.Namespace, claim.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdatePVSize updates just pv size after cloudprovider resizing is successful
|
||||||
|
func UpdatePVSize(
|
||||||
|
pv *v1.PersistentVolume,
|
||||||
|
newSize resource.Quantity,
|
||||||
|
kubeClient clientset.Interface) error {
|
||||||
|
pvClone := pv.DeepCopy()
|
||||||
|
|
||||||
|
oldData, err := json.Marshal(pvClone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unexpected error marshaling old PV %q with error : %v", pvClone.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pvClone.Spec.Capacity[v1.ResourceStorage] = newSize
|
||||||
|
|
||||||
|
newData, err := json.Marshal(pvClone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unexpected error marshaling new PV %q with error : %v", pvClone.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, pvClone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error Creating two way merge patch for PV %q with error : %v", pvClone.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = kubeClient.CoreV1().PersistentVolumes().Patch(pvClone.Name, types.StrategicMergePatchType, patchBytes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error Patching PV %q with error : %v", pvClone.Name, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkResizeInProgress marks cloudprovider resizing as in progress
|
||||||
|
func MarkResizeInProgress(
|
||||||
|
pvc *v1.PersistentVolumeClaim,
|
||||||
|
kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
||||||
|
// Mark PVC as Resize Started
|
||||||
|
progressCondition := v1.PersistentVolumeClaimCondition{
|
||||||
|
Type: v1.PersistentVolumeClaimResizing,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
}
|
||||||
|
conditions := []v1.PersistentVolumeClaimCondition{progressCondition}
|
||||||
|
newPVC := pvc.DeepCopy()
|
||||||
|
newPVC = MergeResizeConditionOnPVC(newPVC, conditions)
|
||||||
|
return PatchPVCStatus(pvc /*oldPVC*/, newPVC, kubeClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkForFSResize marks file system resizing as pending
|
||||||
|
func MarkForFSResize(
|
||||||
|
pvc *v1.PersistentVolumeClaim,
|
||||||
|
kubeClient clientset.Interface) 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 := pvc.DeepCopy()
|
||||||
|
newPVC = MergeResizeConditionOnPVC(newPVC, conditions)
|
||||||
|
_, err := PatchPVCStatus(pvc /*oldPVC*/, newPVC, kubeClient)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkResizeFinished marks all resizing as done
|
||||||
|
func MarkResizeFinished(
|
||||||
|
pvc *v1.PersistentVolumeClaim,
|
||||||
|
newSize resource.Quantity,
|
||||||
|
kubeClient clientset.Interface) error {
|
||||||
|
return MarkFSResizeFinished(pvc, newSize, kubeClient)
|
||||||
|
}
|
||||||
|
|
||||||
// MarkFSResizeFinished marks file system resizing as done
|
// MarkFSResizeFinished marks file system resizing as done
|
||||||
func MarkFSResizeFinished(
|
func MarkFSResizeFinished(
|
||||||
pvc *v1.PersistentVolumeClaim,
|
pvc *v1.PersistentVolumeClaim,
|
||||||
capacity v1.ResourceList,
|
newSize resource.Quantity,
|
||||||
kubeClient clientset.Interface) error {
|
kubeClient clientset.Interface) error {
|
||||||
newPVC := pvc.DeepCopy()
|
newPVC := pvc.DeepCopy()
|
||||||
newPVC.Status.Capacity = capacity
|
newPVC.Status.Capacity[v1.ResourceStorage] = newSize
|
||||||
newPVC = MergeResizeConditionOnPVC(newPVC, []v1.PersistentVolumeClaimCondition{})
|
newPVC = MergeResizeConditionOnPVC(newPVC, []v1.PersistentVolumeClaimCondition{})
|
||||||
_, err := PatchPVCStatus(pvc /*oldPVC*/, newPVC, kubeClient)
|
_, err := PatchPVCStatus(pvc /*oldPVC*/, newPVC, kubeClient)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PatchPVCStatus updates PVC status using PATCH verb
|
// PatchPVCStatus updates PVC status using PATCH verb
|
||||||
|
// Don't use Update because this can be called from kubelet and if kubelet has an older client its
|
||||||
|
// Updates will overwrite new fields. And to avoid writing to a stale object, add ResourceVersion
|
||||||
|
// to the patch so that Patch will fail if the patch's RV != actual up-to-date RV like Update would
|
||||||
func PatchPVCStatus(
|
func PatchPVCStatus(
|
||||||
oldPVC *v1.PersistentVolumeClaim,
|
oldPVC *v1.PersistentVolumeClaim,
|
||||||
newPVC *v1.PersistentVolumeClaim,
|
newPVC *v1.PersistentVolumeClaim,
|
||||||
kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
kubeClient clientset.Interface) (*v1.PersistentVolumeClaim, error) {
|
||||||
pvcName := oldPVC.Name
|
patchBytes, err := createPVCPatch(oldPVC, newPVC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("PatchPVCStatus failed to patch PVC %q: %v", oldPVC.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedClaim, updateErr := kubeClient.CoreV1().PersistentVolumeClaims(oldPVC.Namespace).
|
||||||
|
Patch(oldPVC.Name, types.StrategicMergePatchType, patchBytes, "status")
|
||||||
|
if updateErr != nil {
|
||||||
|
return nil, fmt.Errorf("PatchPVCStatus failed to patch PVC %q: %v", oldPVC.Name, updateErr)
|
||||||
|
}
|
||||||
|
return updatedClaim, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPVCPatch(
|
||||||
|
oldPVC *v1.PersistentVolumeClaim,
|
||||||
|
newPVC *v1.PersistentVolumeClaim) ([]byte, error) {
|
||||||
oldData, err := json.Marshal(oldPVC)
|
oldData, err := json.Marshal(oldPVC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("PatchPVCStatus.Failed to marshal oldData for pvc %q with %v", pvcName, err)
|
return nil, fmt.Errorf("failed to marshal old data: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newData, err := json.Marshal(newPVC)
|
newData, err := json.Marshal(newPVC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("PatchPVCStatus.Failed to marshal newData for pvc %q with %v", pvcName, err)
|
return nil, fmt.Errorf("failed to marshal new data: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, oldPVC)
|
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, oldPVC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("PatchPVCStatus.Failed to CreateTwoWayMergePatch for pvc %q with %v ", pvcName, err)
|
return nil, fmt.Errorf("failed to create 2 way merge patch: %v", err)
|
||||||
}
|
}
|
||||||
updatedClaim, updateErr := kubeClient.CoreV1().PersistentVolumeClaims(oldPVC.Namespace).
|
|
||||||
Patch(pvcName, types.StrategicMergePatchType, patchBytes, "status")
|
patchBytes, err = addResourceVersion(patchBytes, oldPVC.ResourceVersion)
|
||||||
if updateErr != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("PatchPVCStatus.Failed to patch PVC %q with %v", pvcName, updateErr)
|
return nil, fmt.Errorf("failed to add resource version: %v", err)
|
||||||
}
|
}
|
||||||
return updatedClaim, nil
|
|
||||||
|
return patchBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addResourceVersion(patchBytes []byte, resourceVersion string) ([]byte, error) {
|
||||||
|
var patchMap map[string]interface{}
|
||||||
|
err := json.Unmarshal(patchBytes, &patchMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error unmarshalling patch: %v", err)
|
||||||
|
}
|
||||||
|
u := unstructured.Unstructured{Object: patchMap}
|
||||||
|
a, err := meta.Accessor(&u)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating accessor: %v", err)
|
||||||
|
}
|
||||||
|
a.SetResourceVersion(resourceVersion)
|
||||||
|
versionBytes, err := json.Marshal(patchMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error marshalling json patch: %v", err)
|
||||||
|
}
|
||||||
|
return versionBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeResizeConditionOnPVC updates pvc with requested resize conditions
|
// MergeResizeConditionOnPVC updates pvc with requested resize conditions
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -141,6 +142,38 @@ func TestMergeResizeCondition(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreatePVCPatch(t *testing.T) {
|
||||||
|
pvc1 := getPVC([]v1.PersistentVolumeClaimCondition{
|
||||||
|
{
|
||||||
|
Type: v1.PersistentVolumeClaimFileSystemResizePending,
|
||||||
|
Status: v1.ConditionTrue,
|
||||||
|
LastTransitionTime: metav1.Now(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
pvc1.SetResourceVersion("10")
|
||||||
|
pvc2 := pvc1.DeepCopy()
|
||||||
|
pvc2.Status.Capacity = v1.ResourceList{
|
||||||
|
v1.ResourceName("size"): resource.MustParse("10G"),
|
||||||
|
}
|
||||||
|
patchBytes, err := createPVCPatch(pvc1, pvc2)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error creating patch bytes %v", err)
|
||||||
|
}
|
||||||
|
var patchMap map[string]interface{}
|
||||||
|
err = json.Unmarshal(patchBytes, &patchMap)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error unmarshalling json patch : %v", err)
|
||||||
|
}
|
||||||
|
metadata, ok := patchMap["metadata"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("error converting metadata to version map")
|
||||||
|
}
|
||||||
|
resourceVersion, _ := metadata["resourceVersion"].(string)
|
||||||
|
if resourceVersion != "10" {
|
||||||
|
t.Errorf("expected resource version to 10 got %s", resourceVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getPVC(conditions []v1.PersistentVolumeClaimCondition) *v1.PersistentVolumeClaim {
|
func getPVC(conditions []v1.PersistentVolumeClaimCondition) *v1.PersistentVolumeClaim {
|
||||||
pvc := &v1.PersistentVolumeClaim{
|
pvc := &v1.PersistentVolumeClaim{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "resize"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "resize"},
|
||||||
|
@ -9,7 +9,10 @@ go_library(
|
|||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = ["types.go"],
|
srcs = ["types.go"],
|
||||||
importpath = "k8s.io/kubernetes/pkg/volume/util/types",
|
importpath = "k8s.io/kubernetes/pkg/volume/util/types",
|
||||||
deps = ["//staging/src/k8s.io/apimachinery/pkg/types:go_default_library"],
|
deps = [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
|
@ -17,7 +17,10 @@ limitations under the License.
|
|||||||
// Package types defines types used only by volume components
|
// Package types defines types used only by volume components
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import "k8s.io/apimachinery/pkg/types"
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
// UniquePodName defines the type to key pods off of
|
// UniquePodName defines the type to key pods off of
|
||||||
type UniquePodName types.UID
|
type UniquePodName types.UID
|
||||||
@ -34,3 +37,16 @@ type GeneratedOperations struct {
|
|||||||
EventRecorderFunc func(*error)
|
EventRecorderFunc func(*error)
|
||||||
CompleteFunc func(*error)
|
CompleteFunc func(*error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run executes the operations and its supporting functions
|
||||||
|
func (o *GeneratedOperations) Run() (eventErr, detailedErr error) {
|
||||||
|
if o.CompleteFunc != nil {
|
||||||
|
defer o.CompleteFunc(&detailedErr)
|
||||||
|
}
|
||||||
|
if o.EventRecorderFunc != nil {
|
||||||
|
defer o.EventRecorderFunc(&eventErr)
|
||||||
|
}
|
||||||
|
// Handle panic, if any, from operationFunc()
|
||||||
|
defer runtime.RecoverFromPanic(&detailedErr)
|
||||||
|
return o.OperationFunc()
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user