mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-05 15:37:24 +00:00
Support scale subresource for PDBs (#76294)
* Support scale subresource for PDBs * Check group in finder functions * Small fixes and more tests
This commit is contained in:
committed by
Kubernetes Prow Robot
parent
cdff17a96b
commit
f1883c9e8c
@@ -23,13 +23,20 @@ import (
|
||||
"time"
|
||||
|
||||
apps "k8s.io/api/apps/v1"
|
||||
autoscalingapi "k8s.io/api/autoscaling/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
policy "k8s.io/api/policy/v1beta1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/meta/testrestmapper"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/client-go/informers"
|
||||
scalefake "k8s.io/client-go/scale/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
@@ -90,6 +97,14 @@ type disruptionController struct {
|
||||
rsStore cache.Store
|
||||
dStore cache.Store
|
||||
ssStore cache.Store
|
||||
|
||||
scaleClient *scalefake.FakeScaleClient
|
||||
}
|
||||
|
||||
var customGVK = schema.GroupVersionKind{
|
||||
Group: "custom.k8s.io",
|
||||
Version: "v1",
|
||||
Kind: "customresource",
|
||||
}
|
||||
|
||||
func newFakeDisruptionController() (*disruptionController, *pdbStates) {
|
||||
@@ -97,6 +112,10 @@ func newFakeDisruptionController() (*disruptionController, *pdbStates) {
|
||||
|
||||
informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
scheme.AddKnownTypeWithName(customGVK, &v1.Service{})
|
||||
fakeScaleClient := &scalefake.FakeScaleClient{}
|
||||
|
||||
dc := NewDisruptionController(
|
||||
informerFactory.Core().V1().Pods(),
|
||||
informerFactory.Policy().V1beta1().PodDisruptionBudgets(),
|
||||
@@ -105,6 +124,8 @@ func newFakeDisruptionController() (*disruptionController, *pdbStates) {
|
||||
informerFactory.Apps().V1().Deployments(),
|
||||
informerFactory.Apps().V1().StatefulSets(),
|
||||
nil,
|
||||
testrestmapper.TestOnlyStaticRESTMapper(scheme),
|
||||
fakeScaleClient,
|
||||
)
|
||||
dc.getUpdater = func() updater { return ps.Set }
|
||||
dc.podListerSynced = alwaysReady
|
||||
@@ -122,6 +143,7 @@ func newFakeDisruptionController() (*disruptionController, *pdbStates) {
|
||||
informerFactory.Apps().V1().ReplicaSets().Informer().GetStore(),
|
||||
informerFactory.Apps().V1().Deployments().Informer().GetStore(),
|
||||
informerFactory.Apps().V1().StatefulSets().Informer().GetStore(),
|
||||
fakeScaleClient,
|
||||
}, ps
|
||||
}
|
||||
|
||||
@@ -490,6 +512,52 @@ func TestReplicaSet(t *testing.T) {
|
||||
ps.VerifyPdbStatus(t, pdbName, 0, 1, 2, 10, map[string]metav1.Time{})
|
||||
}
|
||||
|
||||
func TestScaleResource(t *testing.T) {
|
||||
customResourceUID := uuid.NewUUID()
|
||||
replicas := int32(10)
|
||||
pods := int32(4)
|
||||
maxUnavailable := int32(5)
|
||||
|
||||
dc, ps := newFakeDisruptionController()
|
||||
|
||||
dc.scaleClient.AddReactor("get", "customresources", func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
obj := &autoscalingapi.Scale{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
UID: customResourceUID,
|
||||
},
|
||||
Spec: autoscalingapi.ScaleSpec{
|
||||
Replicas: replicas,
|
||||
},
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
|
||||
pdb, pdbName := newMaxUnavailablePodDisruptionBudget(t, intstr.FromInt(int(maxUnavailable)))
|
||||
add(t, dc.pdbStore, pdb)
|
||||
|
||||
trueVal := true
|
||||
for i := 0; i < int(pods); i++ {
|
||||
pod, _ := newPod(t, fmt.Sprintf("pod-%d", i))
|
||||
pod.SetOwnerReferences([]metav1.OwnerReference{
|
||||
{
|
||||
Kind: customGVK.Kind,
|
||||
APIVersion: customGVK.GroupVersion().String(),
|
||||
Controller: &trueVal,
|
||||
UID: customResourceUID,
|
||||
},
|
||||
})
|
||||
add(t, dc.podStore, pod)
|
||||
}
|
||||
|
||||
dc.sync(pdbName)
|
||||
disruptionsAllowed := int32(0)
|
||||
if replicas-pods < maxUnavailable {
|
||||
disruptionsAllowed = maxUnavailable - (replicas - pods)
|
||||
}
|
||||
ps.VerifyPdbStatus(t, pdbName, disruptionsAllowed, pods, replicas-maxUnavailable, replicas, map[string]metav1.Time{})
|
||||
}
|
||||
|
||||
// Verify that multiple controllers doesn't allow the PDB to be set true.
|
||||
func TestMultipleControllers(t *testing.T) {
|
||||
const podCount = 2
|
||||
@@ -759,3 +827,202 @@ func TestUpdateDisruptedPods(t *testing.T) {
|
||||
|
||||
ps.VerifyPdbStatus(t, pdbName, 0, 1, 1, 3, map[string]metav1.Time{"p3": {Time: currentTime}})
|
||||
}
|
||||
|
||||
func TestBasicFinderFunctions(t *testing.T) {
|
||||
dc, _ := newFakeDisruptionController()
|
||||
|
||||
rs, _ := newReplicaSet(t, 10)
|
||||
add(t, dc.rsStore, rs)
|
||||
rc, _ := newReplicationController(t, 12)
|
||||
add(t, dc.rcStore, rc)
|
||||
ss, _ := newStatefulSet(t, 14)
|
||||
add(t, dc.ssStore, ss)
|
||||
|
||||
testCases := map[string]struct {
|
||||
finderFunc podControllerFinder
|
||||
apiVersion string
|
||||
kind string
|
||||
name string
|
||||
uid types.UID
|
||||
findsScale bool
|
||||
expectedScale int32
|
||||
}{
|
||||
"replicaset controller with apps group": {
|
||||
finderFunc: dc.getPodReplicaSet,
|
||||
apiVersion: "apps/v1",
|
||||
kind: controllerKindRS.Kind,
|
||||
name: rs.Name,
|
||||
uid: rs.UID,
|
||||
findsScale: true,
|
||||
expectedScale: 10,
|
||||
},
|
||||
"replicaset controller with invalid group": {
|
||||
finderFunc: dc.getPodReplicaSet,
|
||||
apiVersion: "invalid/v1",
|
||||
kind: controllerKindRS.Kind,
|
||||
name: rs.Name,
|
||||
uid: rs.UID,
|
||||
findsScale: false,
|
||||
},
|
||||
"replicationcontroller with empty group": {
|
||||
finderFunc: dc.getPodReplicationController,
|
||||
apiVersion: "/v1",
|
||||
kind: controllerKindRC.Kind,
|
||||
name: rc.Name,
|
||||
uid: rc.UID,
|
||||
findsScale: true,
|
||||
expectedScale: 12,
|
||||
},
|
||||
"replicationcontroller with invalid group": {
|
||||
finderFunc: dc.getPodReplicationController,
|
||||
apiVersion: "apps/v1",
|
||||
kind: controllerKindRC.Kind,
|
||||
name: rc.Name,
|
||||
uid: rc.UID,
|
||||
findsScale: false,
|
||||
},
|
||||
"statefulset controller with extensions group": {
|
||||
finderFunc: dc.getPodStatefulSet,
|
||||
apiVersion: "apps/v1",
|
||||
kind: controllerKindSS.Kind,
|
||||
name: ss.Name,
|
||||
uid: ss.UID,
|
||||
findsScale: true,
|
||||
expectedScale: 14,
|
||||
},
|
||||
"statefulset controller with invalid kind": {
|
||||
finderFunc: dc.getPodStatefulSet,
|
||||
apiVersion: "apps/v1",
|
||||
kind: controllerKindRS.Kind,
|
||||
name: ss.Name,
|
||||
uid: ss.UID,
|
||||
findsScale: false,
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
controllerRef := &metav1.OwnerReference{
|
||||
APIVersion: tc.apiVersion,
|
||||
Kind: tc.kind,
|
||||
Name: tc.name,
|
||||
UID: tc.uid,
|
||||
}
|
||||
|
||||
controllerAndScale, _ := tc.finderFunc(controllerRef, metav1.NamespaceDefault)
|
||||
|
||||
if controllerAndScale == nil {
|
||||
if tc.findsScale {
|
||||
t.Error("Expected scale, but got nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if got, want := controllerAndScale.scale, tc.expectedScale; got != want {
|
||||
t.Errorf("Expected scale %d, but got %d", want, got)
|
||||
}
|
||||
|
||||
if got, want := controllerAndScale.UID, tc.uid; got != want {
|
||||
t.Errorf("Expected uid %s, but got %s", want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeploymentFinderFunction(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
rsApiVersion string
|
||||
rsKind string
|
||||
depApiVersion string
|
||||
depKind string
|
||||
findsScale bool
|
||||
expectedScale int32
|
||||
}{
|
||||
"happy path": {
|
||||
rsApiVersion: "apps/v1",
|
||||
rsKind: controllerKindRS.Kind,
|
||||
depApiVersion: "extensions/v1",
|
||||
depKind: controllerKindDep.Kind,
|
||||
findsScale: true,
|
||||
expectedScale: 10,
|
||||
},
|
||||
"invalid rs apiVersion": {
|
||||
rsApiVersion: "invalid/v1",
|
||||
rsKind: controllerKindRS.Kind,
|
||||
depApiVersion: "apps/v1",
|
||||
depKind: controllerKindDep.Kind,
|
||||
findsScale: false,
|
||||
},
|
||||
"invalid rs kind": {
|
||||
rsApiVersion: "apps/v1",
|
||||
rsKind: "InvalidKind",
|
||||
depApiVersion: "apps/v1",
|
||||
depKind: controllerKindDep.Kind,
|
||||
findsScale: false,
|
||||
},
|
||||
"invalid deployment apiVersion": {
|
||||
rsApiVersion: "extensions/v1",
|
||||
rsKind: controllerKindRS.Kind,
|
||||
depApiVersion: "deployment/v1",
|
||||
depKind: controllerKindDep.Kind,
|
||||
findsScale: false,
|
||||
},
|
||||
"invalid deployment kind": {
|
||||
rsApiVersion: "apps/v1",
|
||||
rsKind: controllerKindRS.Kind,
|
||||
depApiVersion: "extensions/v1",
|
||||
depKind: "InvalidKind",
|
||||
findsScale: false,
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
dc, _ := newFakeDisruptionController()
|
||||
|
||||
dep, _ := newDeployment(t, 10)
|
||||
dep.Spec.Selector = newSel(labels)
|
||||
add(t, dc.dStore, dep)
|
||||
|
||||
rs, _ := newReplicaSet(t, 5)
|
||||
rs.Labels = labels
|
||||
trueVal := true
|
||||
rs.OwnerReferences = append(rs.OwnerReferences, metav1.OwnerReference{
|
||||
APIVersion: tc.depApiVersion,
|
||||
Kind: tc.depKind,
|
||||
Name: dep.Name,
|
||||
UID: dep.UID,
|
||||
Controller: &trueVal,
|
||||
})
|
||||
add(t, dc.rsStore, rs)
|
||||
|
||||
controllerRef := &metav1.OwnerReference{
|
||||
APIVersion: tc.rsApiVersion,
|
||||
Kind: tc.rsKind,
|
||||
Name: rs.Name,
|
||||
UID: rs.UID,
|
||||
}
|
||||
|
||||
controllerAndScale, _ := dc.getPodDeployment(controllerRef, metav1.NamespaceDefault)
|
||||
|
||||
if controllerAndScale == nil {
|
||||
if tc.findsScale {
|
||||
t.Error("Expected scale, but got nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if got, want := controllerAndScale.scale, tc.expectedScale; got != want {
|
||||
t.Errorf("Expected scale %d, but got %d", want, got)
|
||||
}
|
||||
|
||||
if got, want := controllerAndScale.UID, dep.UID; got != want {
|
||||
t.Errorf("Expected uid %s, but got %s", want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user