Promote PDBs to GA
This commit is contained in:
parent
8a8c267e58
commit
21fba79d45
@ -262,6 +262,7 @@ var apiVersionPriorities = map[schema.GroupVersion]priority{
|
|||||||
{Group: "networking.k8s.io", Version: "v1"}: {group: 17200, version: 15},
|
{Group: "networking.k8s.io", Version: "v1"}: {group: 17200, version: 15},
|
||||||
{Group: "networking.k8s.io", Version: "v1beta1"}: {group: 17200, version: 9},
|
{Group: "networking.k8s.io", Version: "v1beta1"}: {group: 17200, version: 9},
|
||||||
{Group: "extensions", Version: "v1beta1"}: {group: 17150, version: 1}, // prioritize below networking.k8s.io, which contains the GA version of Ingress, the only resource remaining in extensions/v1beta1
|
{Group: "extensions", Version: "v1beta1"}: {group: 17150, version: 1}, // prioritize below networking.k8s.io, which contains the GA version of Ingress, the only resource remaining in extensions/v1beta1
|
||||||
|
{Group: "policy", Version: "v1"}: {group: 17100, version: 15},
|
||||||
{Group: "policy", Version: "v1beta1"}: {group: 17100, version: 9},
|
{Group: "policy", Version: "v1beta1"}: {group: 17100, version: 9},
|
||||||
{Group: "rbac.authorization.k8s.io", Version: "v1"}: {group: 17000, version: 15},
|
{Group: "rbac.authorization.k8s.io", Version: "v1"}: {group: 17000, version: 15},
|
||||||
{Group: "rbac.authorization.k8s.io", Version: "v1beta1"}: {group: 17000, version: 12},
|
{Group: "rbac.authorization.k8s.io", Version: "v1beta1"}: {group: 17000, version: 12},
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/scale"
|
"k8s.io/client-go/scale"
|
||||||
@ -34,16 +33,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func startDisruptionController(ctx ControllerContext) (http.Handler, bool, error) {
|
func startDisruptionController(ctx ControllerContext) (http.Handler, bool, error) {
|
||||||
var group = "policy"
|
|
||||||
var version = "v1beta1"
|
|
||||||
var resource = "poddisruptionbudgets"
|
|
||||||
|
|
||||||
if !ctx.AvailableResources[schema.GroupVersionResource{Group: group, Version: version, Resource: resource}] {
|
|
||||||
klog.Infof(
|
|
||||||
"Refusing to start disruption because resource %q in group %q is not available.",
|
|
||||||
resource, group+"/"+version)
|
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PodDisruptionBudget) {
|
if !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PodDisruptionBudget) {
|
||||||
klog.Infof("Refusing to start disruption because the PodDisruptionBudget feature is disabled")
|
klog.Infof("Refusing to start disruption because the PodDisruptionBudget feature is disabled")
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
@ -59,7 +48,7 @@ func startDisruptionController(ctx ControllerContext) (http.Handler, bool, error
|
|||||||
|
|
||||||
go disruption.NewDisruptionController(
|
go disruption.NewDisruptionController(
|
||||||
ctx.InformerFactory.Core().V1().Pods(),
|
ctx.InformerFactory.Core().V1().Pods(),
|
||||||
ctx.InformerFactory.Policy().V1beta1().PodDisruptionBudgets(),
|
ctx.InformerFactory.Policy().V1().PodDisruptionBudgets(),
|
||||||
ctx.InformerFactory.Core().V1().ReplicationControllers(),
|
ctx.InformerFactory.Core().V1().ReplicationControllers(),
|
||||||
ctx.InformerFactory.Apps().V1().ReplicaSets(),
|
ctx.InformerFactory.Apps().V1().ReplicaSets(),
|
||||||
ctx.InformerFactory.Apps().V1().Deployments(),
|
ctx.InformerFactory.Apps().V1().Deployments(),
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"k8s.io/api/node/v1alpha1": "nodev1alpha1",
|
"k8s.io/api/node/v1alpha1": "nodev1alpha1",
|
||||||
"k8s.io/api/node/v1beta1": "nodev1beta1",
|
"k8s.io/api/node/v1beta1": "nodev1beta1",
|
||||||
"k8s.io/api/node/v1": "nodev1",
|
"k8s.io/api/node/v1": "nodev1",
|
||||||
|
"k8s.io/api/policy/v1": "policyv1",
|
||||||
"k8s.io/api/policy/v1beta1": "policyv1beta1",
|
"k8s.io/api/policy/v1beta1": "policyv1beta1",
|
||||||
"k8s.io/api/rbac/v1": "rbacv1",
|
"k8s.io/api/rbac/v1": "rbacv1",
|
||||||
"k8s.io/api/rbac/v1alpha1": "rbacv1alpha1",
|
"k8s.io/api/rbac/v1alpha1": "rbacv1alpha1",
|
||||||
|
@ -95,6 +95,7 @@ networking.k8s.io/v1beta1 \
|
|||||||
node.k8s.io/v1 \
|
node.k8s.io/v1 \
|
||||||
node.k8s.io/v1alpha1 \
|
node.k8s.io/v1alpha1 \
|
||||||
node.k8s.io/v1beta1 \
|
node.k8s.io/v1beta1 \
|
||||||
|
policy/v1 \
|
||||||
policy/v1beta1 \
|
policy/v1beta1 \
|
||||||
rbac.authorization.k8s.io/v1 \
|
rbac.authorization.k8s.io/v1 \
|
||||||
rbac.authorization.k8s.io/v1beta1 \
|
rbac.authorization.k8s.io/v1beta1 \
|
||||||
|
51
pkg/apis/policy/helper.go
Normal file
51
pkg/apis/policy/helper.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 policy
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PDBV1beta1Label = "pdb.kubernetes.io/deprecated-v1beta1-empty-selector-match"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
NonV1beta1MatchAllSelector = &metav1.LabelSelector{}
|
||||||
|
NonV1beta1MatchNoneSelector = &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{{Key: PDBV1beta1Label, Operator: metav1.LabelSelectorOpExists}},
|
||||||
|
}
|
||||||
|
|
||||||
|
V1beta1MatchNoneSelector = &metav1.LabelSelector{}
|
||||||
|
V1beta1MatchAllSelector = &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{{Key: PDBV1beta1Label, Operator: metav1.LabelSelectorOpDoesNotExist}},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func StripPDBV1beta1Label(selector *metav1.LabelSelector) {
|
||||||
|
if selector == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
trimmedMatchExpressions := selector.MatchExpressions[:0]
|
||||||
|
for _, exp := range selector.MatchExpressions {
|
||||||
|
if exp.Key != PDBV1beta1Label {
|
||||||
|
trimmedMatchExpressions = append(trimmedMatchExpressions, exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selector.MatchExpressions = trimmedMatchExpressions
|
||||||
|
}
|
@ -23,6 +23,7 @@ import (
|
|||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
"k8s.io/kubernetes/pkg/apis/policy"
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/policy/v1"
|
||||||
"k8s.io/kubernetes/pkg/apis/policy/v1beta1"
|
"k8s.io/kubernetes/pkg/apis/policy/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,5 +35,7 @@ func init() {
|
|||||||
func Install(scheme *runtime.Scheme) {
|
func Install(scheme *runtime.Scheme) {
|
||||||
utilruntime.Must(policy.AddToScheme(scheme))
|
utilruntime.Must(policy.AddToScheme(scheme))
|
||||||
utilruntime.Must(v1beta1.AddToScheme(scheme))
|
utilruntime.Must(v1beta1.AddToScheme(scheme))
|
||||||
utilruntime.Must(scheme.SetVersionPriority(v1beta1.SchemeGroupVersion))
|
utilruntime.Must(v1.AddToScheme(scheme))
|
||||||
|
// TODO (mortent): priority should change after 1.21. See https://github.com/kubernetes/kubernetes/pull/95718#discussion_r520969477
|
||||||
|
utilruntime.Must(scheme.SetVersionPriority(v1beta1.SchemeGroupVersion, v1.SchemeGroupVersion))
|
||||||
}
|
}
|
||||||
|
42
pkg/apis/policy/v1/conversion.go
Normal file
42
pkg/apis/policy/v1/conversion.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/api/policy/v1"
|
||||||
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
|
"k8s.io/apimachinery/pkg/conversion"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Convert_v1_PodDisruptionBudget_To_policy_PodDisruptionBudget(in *v1.PodDisruptionBudget, out *policy.PodDisruptionBudget, s conversion.Scope) error {
|
||||||
|
if err := autoConvert_v1_PodDisruptionBudget_To_policy_PodDisruptionBudget(in, out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case apiequality.Semantic.DeepEqual(in.Spec.Selector, policy.NonV1beta1MatchNoneSelector):
|
||||||
|
// no-op, preserve
|
||||||
|
case apiequality.Semantic.DeepEqual(in.Spec.Selector, policy.NonV1beta1MatchAllSelector):
|
||||||
|
// no-op, preserve
|
||||||
|
default:
|
||||||
|
// otherwise, make sure the label intended to be used in a match-all or match-none selector
|
||||||
|
// never gets combined with user-specified fields
|
||||||
|
policy.StripPDBV1beta1Label(out.Spec.Selector)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
127
pkg/apis/policy/v1/conversion_test.go
Normal file
127
pkg/apis/policy/v1/conversion_test.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"k8s.io/api/policy/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConversion(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
Name string
|
||||||
|
In runtime.Object
|
||||||
|
Out runtime.Object
|
||||||
|
ExpectOut runtime.Object
|
||||||
|
ExpectErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "v1 to internal with match none selector",
|
||||||
|
In: &v1.PodDisruptionBudget{
|
||||||
|
Spec: v1.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "pdb.kubernetes.io/deprecated-v1beta1-empty-selector-match",
|
||||||
|
Operator: metav1.LabelSelectorOpExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Out: &policy.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &policy.PodDisruptionBudget{
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "pdb.kubernetes.io/deprecated-v1beta1-empty-selector-match",
|
||||||
|
Operator: metav1.LabelSelectorOpExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v1 to internal with nil selector",
|
||||||
|
In: &v1.PodDisruptionBudget{},
|
||||||
|
Out: &policy.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &policy.PodDisruptionBudget{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v1 to internal with match all selector",
|
||||||
|
In: &v1.PodDisruptionBudget{
|
||||||
|
Spec: v1.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "pdb.kubernetes.io/deprecated-v1beta1-empty-selector-match",
|
||||||
|
Operator: metav1.LabelSelectorOpDoesNotExist,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Out: &policy.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &policy.PodDisruptionBudget{
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
scheme := runtime.NewScheme()
|
||||||
|
if err := policy.AddToScheme(scheme); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := AddToScheme(scheme); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
err := scheme.Convert(tc.In, tc.Out, nil)
|
||||||
|
if err != nil {
|
||||||
|
if len(tc.ExpectErr) == 0 {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), tc.ExpectErr) {
|
||||||
|
t.Fatalf("expected error %s, got %v", tc.ExpectErr, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(tc.ExpectErr) > 0 {
|
||||||
|
t.Fatalf("expected error %s, got none", tc.ExpectErr)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tc.Out, tc.ExpectOut) {
|
||||||
|
t.Fatalf("unexpected result:\n %s", cmp.Diff(tc.ExpectOut, tc.Out))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
24
pkg/apis/policy/v1/doc.go
Normal file
24
pkg/apis/policy/v1/doc.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/policy
|
||||||
|
// +k8s:conversion-gen-external-types=k8s.io/api/policy/v1
|
||||||
|
// +k8s:defaulter-gen=TypeMeta
|
||||||
|
// +k8s:defaulter-gen-input=../../../../vendor/k8s.io/api/policy/v1
|
||||||
|
|
||||||
|
// Package policy is for any kind of policy object. Currently, this only
|
||||||
|
// includes policyv1.PodDisruptionBudget
|
||||||
|
package v1 // import "k8s.io/kubernetes/pkg/apis/policy/v1"
|
45
pkg/apis/policy/v1/register.go
Normal file
45
pkg/apis/policy/v1/register.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GroupName is the group name use in this package
|
||||||
|
const GroupName = "policy"
|
||||||
|
|
||||||
|
// SchemeGroupVersion is group version used to register these objects
|
||||||
|
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
|
||||||
|
|
||||||
|
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||||
|
func Resource(resource string) schema.GroupResource {
|
||||||
|
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
localSchemeBuilder = &policyv1.SchemeBuilder
|
||||||
|
AddToScheme = localSchemeBuilder.AddToScheme
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// We only register manually written functions here. The registration of the
|
||||||
|
// generated functions takes place in the generated files. The separation
|
||||||
|
// makes the code compile even when the generated files are missing.
|
||||||
|
localSchemeBuilder.Register(RegisterDefaults)
|
||||||
|
}
|
66
pkg/apis/policy/v1beta1/conversion.go
Normal file
66
pkg/apis/policy/v1beta1/conversion.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1beta1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/api/policy/v1beta1"
|
||||||
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
|
"k8s.io/apimachinery/pkg/conversion"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Convert_v1beta1_PodDisruptionBudget_To_policy_PodDisruptionBudget(in *v1beta1.PodDisruptionBudget, out *policy.PodDisruptionBudget, s conversion.Scope) error {
|
||||||
|
if err := autoConvert_v1beta1_PodDisruptionBudget_To_policy_PodDisruptionBudget(in, out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case apiequality.Semantic.DeepEqual(in.Spec.Selector, policy.V1beta1MatchNoneSelector):
|
||||||
|
// If the v1beta1 version has a non-nil but empty selector, it should be
|
||||||
|
// selecting no pods, even when used with the internal or v1 api. We
|
||||||
|
// add a selector that is non-empty but will never match any pods.
|
||||||
|
out.Spec.Selector = policy.NonV1beta1MatchNoneSelector.DeepCopy()
|
||||||
|
case apiequality.Semantic.DeepEqual(in.Spec.Selector, policy.V1beta1MatchAllSelector):
|
||||||
|
// If the v1beta1 version has our v1beta1-specific "match-all" selector,
|
||||||
|
// swap that out for a simpler empty "match-all" selector for v1
|
||||||
|
out.Spec.Selector = policy.NonV1beta1MatchAllSelector.DeepCopy()
|
||||||
|
default:
|
||||||
|
// otherwise, make sure the label intended to be used in a match-all or match-none selector
|
||||||
|
// never gets combined with user-specified fields
|
||||||
|
policy.StripPDBV1beta1Label(out.Spec.Selector)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Convert_policy_PodDisruptionBudget_To_v1beta1_PodDisruptionBudget(in *policy.PodDisruptionBudget, out *v1beta1.PodDisruptionBudget, s conversion.Scope) error {
|
||||||
|
if err := autoConvert_policy_PodDisruptionBudget_To_v1beta1_PodDisruptionBudget(in, out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case apiequality.Semantic.DeepEqual(in.Spec.Selector, policy.NonV1beta1MatchNoneSelector):
|
||||||
|
// If the internal version has our v1beta1-specific "match-none" selector,
|
||||||
|
// swap that out for a simpler empty "match-none" selector for v1beta1
|
||||||
|
out.Spec.Selector = policy.V1beta1MatchNoneSelector.DeepCopy()
|
||||||
|
case apiequality.Semantic.DeepEqual(in.Spec.Selector, policy.NonV1beta1MatchAllSelector):
|
||||||
|
// If the internal version has a non-nil but empty selector, we want it to
|
||||||
|
// select all pods. We make sure this happens even with the v1beta1 api by
|
||||||
|
// adding a non-empty selector that selects all pods.
|
||||||
|
out.Spec.Selector = policy.V1beta1MatchAllSelector.DeepCopy()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
197
pkg/apis/policy/v1beta1/conversion_test.go
Normal file
197
pkg/apis/policy/v1beta1/conversion_test.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1beta1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"k8s.io/api/policy/v1beta1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConversion(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
Name string
|
||||||
|
In runtime.Object
|
||||||
|
Out runtime.Object
|
||||||
|
ExpectOut runtime.Object
|
||||||
|
ExpectErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "v1beta1 to internal with empty selector",
|
||||||
|
In: &v1beta1.PodDisruptionBudget{
|
||||||
|
Spec: v1beta1.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Out: &policy.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &policy.PodDisruptionBudget{
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "pdb.kubernetes.io/deprecated-v1beta1-empty-selector-match",
|
||||||
|
Operator: metav1.LabelSelectorOpExists,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v1beta1 to internal with nil selector",
|
||||||
|
In: &v1beta1.PodDisruptionBudget{},
|
||||||
|
Out: &policy.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &policy.PodDisruptionBudget{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v1 to internal with existing selector",
|
||||||
|
In: &v1beta1.PodDisruptionBudget{
|
||||||
|
Spec: v1beta1.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Out: &policy.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &policy.PodDisruptionBudget{
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "v1beta1 to internal with existing pdb selector",
|
||||||
|
In: &v1beta1.PodDisruptionBudget{
|
||||||
|
Spec: v1beta1.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "pdb.kubernetes.io/deprecated-v1beta1-empty-selector-match",
|
||||||
|
Operator: metav1.LabelSelectorOpDoesNotExist,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Out: &policy.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &policy.PodDisruptionBudget{
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "internal to v1beta1 with empty selector",
|
||||||
|
In: &policy.PodDisruptionBudget{
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Out: &v1beta1.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &v1beta1.PodDisruptionBudget{
|
||||||
|
Spec: v1beta1.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "pdb.kubernetes.io/deprecated-v1beta1-empty-selector-match",
|
||||||
|
Operator: metav1.LabelSelectorOpDoesNotExist,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "internal to v1beta1 with nil selector",
|
||||||
|
In: &policy.PodDisruptionBudget{},
|
||||||
|
Out: &v1beta1.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &v1beta1.PodDisruptionBudget{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "internal to v1beta1 with existing selector",
|
||||||
|
In: &policy.PodDisruptionBudget{
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Out: &v1beta1.PodDisruptionBudget{},
|
||||||
|
ExpectOut: &v1beta1.PodDisruptionBudget{
|
||||||
|
Spec: v1beta1.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
scheme := runtime.NewScheme()
|
||||||
|
if err := policy.AddToScheme(scheme); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := AddToScheme(scheme); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
err := scheme.Convert(tc.In, tc.Out, nil)
|
||||||
|
if err != nil {
|
||||||
|
if len(tc.ExpectErr) == 0 {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), tc.ExpectErr) {
|
||||||
|
t.Fatalf("expected error %s, got %v", tc.ExpectErr, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(tc.ExpectErr) > 0 {
|
||||||
|
t.Fatalf("expected error %s, got none", tc.ExpectErr)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tc.Out, tc.ExpectOut) {
|
||||||
|
t.Fatalf("unexpected result:\n %s", cmp.Diff(tc.ExpectOut, tc.Out))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,7 @@ import (
|
|||||||
apps "k8s.io/api/apps/v1beta1"
|
apps "k8s.io/api/apps/v1beta1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/api/extensions/v1beta1"
|
"k8s.io/api/extensions/v1beta1"
|
||||||
policy "k8s.io/api/policy/v1beta1"
|
policy "k8s.io/api/policy/v1"
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
@ -38,13 +38,13 @@ import (
|
|||||||
"k8s.io/client-go/discovery"
|
"k8s.io/client-go/discovery"
|
||||||
appsv1informers "k8s.io/client-go/informers/apps/v1"
|
appsv1informers "k8s.io/client-go/informers/apps/v1"
|
||||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||||
policyinformers "k8s.io/client-go/informers/policy/v1beta1"
|
policyinformers "k8s.io/client-go/informers/policy/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"
|
||||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
appsv1listers "k8s.io/client-go/listers/apps/v1"
|
appsv1listers "k8s.io/client-go/listers/apps/v1"
|
||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
corelisters "k8s.io/client-go/listers/core/v1"
|
||||||
policylisters "k8s.io/client-go/listers/policy/v1beta1"
|
policylisters "k8s.io/client-go/listers/policy/v1"
|
||||||
scaleclient "k8s.io/client-go/scale"
|
scaleclient "k8s.io/client-go/scale"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
@ -506,9 +506,6 @@ func (dc *DisruptionController) getPodsForPdb(pdb *policy.PodDisruptionBudget) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return []*v1.Pod{}, err
|
return []*v1.Pod{}, err
|
||||||
}
|
}
|
||||||
if sel.Empty() {
|
|
||||||
return []*v1.Pod{}, nil
|
|
||||||
}
|
|
||||||
pods, err := dc.podLister.Pods(pdb.Namespace).List(sel)
|
pods, err := dc.podLister.Pods(pdb.Namespace).List(sel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*v1.Pod{}, err
|
return []*v1.Pod{}, err
|
||||||
@ -835,6 +832,6 @@ func (dc *DisruptionController) updatePdbStatus(pdb *policy.PodDisruptionBudget,
|
|||||||
func (dc *DisruptionController) writePdbStatus(pdb *policy.PodDisruptionBudget) error {
|
func (dc *DisruptionController) writePdbStatus(pdb *policy.PodDisruptionBudget) error {
|
||||||
// If this update fails, don't retry it. Allow the failure to get handled &
|
// If this update fails, don't retry it. Allow the failure to get handled &
|
||||||
// retried in `processNextWorkItem()`.
|
// retried in `processNextWorkItem()`.
|
||||||
_, err := dc.kubeClient.PolicyV1beta1().PodDisruptionBudgets(pdb.Namespace).UpdateStatus(context.TODO(), pdb, metav1.UpdateOptions{})
|
_, err := dc.kubeClient.PolicyV1().PodDisruptionBudgets(pdb.Namespace).UpdateStatus(context.TODO(), pdb, metav1.UpdateOptions{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ import (
|
|||||||
apps "k8s.io/api/apps/v1"
|
apps "k8s.io/api/apps/v1"
|
||||||
autoscalingapi "k8s.io/api/autoscaling/v1"
|
autoscalingapi "k8s.io/api/autoscaling/v1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
policy "k8s.io/api/policy/v1beta1"
|
policy "k8s.io/api/policy/v1"
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
@ -151,7 +151,7 @@ func newFakeDisruptionController() (*disruptionController, *pdbStates) {
|
|||||||
|
|
||||||
dc := NewDisruptionController(
|
dc := NewDisruptionController(
|
||||||
informerFactory.Core().V1().Pods(),
|
informerFactory.Core().V1().Pods(),
|
||||||
informerFactory.Policy().V1beta1().PodDisruptionBudgets(),
|
informerFactory.Policy().V1().PodDisruptionBudgets(),
|
||||||
informerFactory.Core().V1().ReplicationControllers(),
|
informerFactory.Core().V1().ReplicationControllers(),
|
||||||
informerFactory.Apps().V1().ReplicaSets(),
|
informerFactory.Apps().V1().ReplicaSets(),
|
||||||
informerFactory.Apps().V1().Deployments(),
|
informerFactory.Apps().V1().Deployments(),
|
||||||
@ -175,7 +175,7 @@ func newFakeDisruptionController() (*disruptionController, *pdbStates) {
|
|||||||
return &disruptionController{
|
return &disruptionController{
|
||||||
dc,
|
dc,
|
||||||
informerFactory.Core().V1().Pods().Informer().GetStore(),
|
informerFactory.Core().V1().Pods().Informer().GetStore(),
|
||||||
informerFactory.Policy().V1beta1().PodDisruptionBudgets().Informer().GetStore(),
|
informerFactory.Policy().V1().PodDisruptionBudgets().Informer().GetStore(),
|
||||||
informerFactory.Core().V1().ReplicationControllers().Informer().GetStore(),
|
informerFactory.Core().V1().ReplicationControllers().Informer().GetStore(),
|
||||||
informerFactory.Apps().V1().ReplicaSets().Informer().GetStore(),
|
informerFactory.Apps().V1().ReplicaSets().Informer().GetStore(),
|
||||||
informerFactory.Apps().V1().Deployments().Informer().GetStore(),
|
informerFactory.Apps().V1().Deployments().Informer().GetStore(),
|
||||||
@ -402,7 +402,7 @@ func add(t *testing.T, store cache.Store, obj interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create one with no selector. Verify it matches 0 pods.
|
// Create one with no selector. Verify it matches all pods
|
||||||
func TestNoSelector(t *testing.T) {
|
func TestNoSelector(t *testing.T) {
|
||||||
dc, ps := newFakeDisruptionController()
|
dc, ps := newFakeDisruptionController()
|
||||||
|
|
||||||
@ -416,7 +416,7 @@ func TestNoSelector(t *testing.T) {
|
|||||||
|
|
||||||
add(t, dc.podStore, pod)
|
add(t, dc.podStore, pod)
|
||||||
dc.sync(pdbName)
|
dc.sync(pdbName)
|
||||||
ps.VerifyPdbStatus(t, pdbName, 0, 0, 3, 0, map[string]metav1.Time{})
|
ps.VerifyPdbStatus(t, pdbName, 0, 1, 3, 1, map[string]metav1.Time{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that available/expected counts go up as we add pods, then verify that
|
// Verify that available/expected counts go up as we add pods, then verify that
|
||||||
@ -1151,7 +1151,7 @@ func TestUpdatePDBStatusRetries(t *testing.T) {
|
|||||||
|
|
||||||
// Create a PDB and 3 pods that match it.
|
// Create a PDB and 3 pods that match it.
|
||||||
pdb, pdbKey := newMinAvailablePodDisruptionBudget(t, intstr.FromInt(1))
|
pdb, pdbKey := newMinAvailablePodDisruptionBudget(t, intstr.FromInt(1))
|
||||||
pdb, err := dc.coreClient.PolicyV1beta1().PodDisruptionBudgets(pdb.Namespace).Create(context.TODO(), pdb, metav1.CreateOptions{})
|
pdb, err := dc.coreClient.PolicyV1().PodDisruptionBudgets(pdb.Namespace).Create(context.TODO(), pdb, metav1.CreateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create PDB: %v", err)
|
t.Fatalf("Failed to create PDB: %v", err)
|
||||||
}
|
}
|
||||||
@ -1186,7 +1186,7 @@ func TestUpdatePDBStatusRetries(t *testing.T) {
|
|||||||
// These GVRs are copied from the generated fake code because they are not exported.
|
// These GVRs are copied from the generated fake code because they are not exported.
|
||||||
var (
|
var (
|
||||||
podsResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
podsResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
||||||
poddisruptionbudgetsResource = schema.GroupVersionResource{Group: "policy", Version: "v1beta1", Resource: "poddisruptionbudgets"}
|
poddisruptionbudgetsResource = schema.GroupVersionResource{Group: "policy", Version: "v1", Resource: "poddisruptionbudgets"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bypass the coreClient.Fake and write directly to the ObjectTracker, because
|
// Bypass the coreClient.Fake and write directly to the ObjectTracker, because
|
||||||
@ -1248,7 +1248,7 @@ func TestUpdatePDBStatusRetries(t *testing.T) {
|
|||||||
|
|
||||||
// (C) Whether or not sync() returned an error, the PDB status should reflect
|
// (C) Whether or not sync() returned an error, the PDB status should reflect
|
||||||
// the evictions that took place.
|
// the evictions that took place.
|
||||||
finalPDB, err := dc.coreClient.PolicyV1beta1().PodDisruptionBudgets("default").Get(context.TODO(), pdb.Name, metav1.GetOptions{})
|
finalPDB, err := dc.coreClient.PolicyV1().PodDisruptionBudgets("default").Get(context.TODO(), pdb.Name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to get PDB: %v", err)
|
t.Fatalf("Failed to get PDB: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ import (
|
|||||||
nodev1 "k8s.io/api/node/v1"
|
nodev1 "k8s.io/api/node/v1"
|
||||||
nodev1alpha1 "k8s.io/api/node/v1alpha1"
|
nodev1alpha1 "k8s.io/api/node/v1alpha1"
|
||||||
nodev1beta1 "k8s.io/api/node/v1beta1"
|
nodev1beta1 "k8s.io/api/node/v1beta1"
|
||||||
|
policyapiv1 "k8s.io/api/policy/v1"
|
||||||
policyapiv1beta1 "k8s.io/api/policy/v1beta1"
|
policyapiv1beta1 "k8s.io/api/policy/v1beta1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
|
rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
|
||||||
@ -688,6 +689,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
|
|||||||
networkingapiv1beta1.SchemeGroupVersion,
|
networkingapiv1beta1.SchemeGroupVersion,
|
||||||
nodev1.SchemeGroupVersion,
|
nodev1.SchemeGroupVersion,
|
||||||
nodev1beta1.SchemeGroupVersion,
|
nodev1beta1.SchemeGroupVersion,
|
||||||
|
policyapiv1.SchemeGroupVersion,
|
||||||
policyapiv1beta1.SchemeGroupVersion,
|
policyapiv1beta1.SchemeGroupVersion,
|
||||||
rbacv1.SchemeGroupVersion,
|
rbacv1.SchemeGroupVersion,
|
||||||
rbacv1beta1.SchemeGroupVersion,
|
rbacv1beta1.SchemeGroupVersion,
|
||||||
|
@ -77,6 +77,7 @@ var GVRToStorageVersionHash = map[string]string{
|
|||||||
"networking.k8s.io/v1/ingressclasses": "l/iqIbDgFyQ=",
|
"networking.k8s.io/v1/ingressclasses": "l/iqIbDgFyQ=",
|
||||||
"node.k8s.io/v1/runtimeclasses": "WQTu1GL3T2Q=",
|
"node.k8s.io/v1/runtimeclasses": "WQTu1GL3T2Q=",
|
||||||
"node.k8s.io/v1beta1/runtimeclasses": "WQTu1GL3T2Q=",
|
"node.k8s.io/v1beta1/runtimeclasses": "WQTu1GL3T2Q=",
|
||||||
|
"policy/v1/poddisruptionbudgets": "6BGBu0kpHtk=",
|
||||||
"policy/v1beta1/poddisruptionbudgets": "6BGBu0kpHtk=",
|
"policy/v1beta1/poddisruptionbudgets": "6BGBu0kpHtk=",
|
||||||
"policy/v1beta1/podsecuritypolicies": "khBLobUXkqA=",
|
"policy/v1beta1/podsecuritypolicies": "khBLobUXkqA=",
|
||||||
"rbac.authorization.k8s.io/v1/clusterrolebindings": "48tpQ8gZHFc=",
|
"rbac.authorization.k8s.io/v1/clusterrolebindings": "48tpQ8gZHFc=",
|
||||||
|
@ -805,7 +805,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
WindowsEndpointSliceProxying: {Default: true, PreRelease: featuregate.Beta},
|
WindowsEndpointSliceProxying: {Default: true, PreRelease: featuregate.Beta},
|
||||||
StartupProbe: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23
|
StartupProbe: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23
|
||||||
AllowInsecureBackendProxy: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23
|
AllowInsecureBackendProxy: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23
|
||||||
PodDisruptionBudget: {Default: true, PreRelease: featuregate.Beta},
|
PodDisruptionBudget: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.25
|
||||||
CronJobControllerV2: {Default: true, PreRelease: featuregate.Beta},
|
CronJobControllerV2: {Default: true, PreRelease: featuregate.Beta},
|
||||||
DaemonSetUpdateSurge: {Default: false, PreRelease: featuregate.Alpha},
|
DaemonSetUpdateSurge: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
ServiceTopology: {Default: false, PreRelease: featuregate.Alpha},
|
ServiceTopology: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
"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/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
@ -31,7 +31,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
"k8s.io/apiserver/pkg/util/dryrun"
|
"k8s.io/apiserver/pkg/util/dryrun"
|
||||||
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
|
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
|
||||||
"k8s.io/client-go/util/retry"
|
"k8s.io/client-go/util/retry"
|
||||||
pdbhelper "k8s.io/component-helpers/apps/poddisruptionbudget"
|
pdbhelper "k8s.io/component-helpers/apps/poddisruptionbudget"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||||
@ -314,7 +314,7 @@ func createTooManyRequestsError(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkAndDecrement checks if the provided PodDisruptionBudget allows any disruption.
|
// checkAndDecrement checks if the provided PodDisruptionBudget allows any disruption.
|
||||||
func (r *EvictionREST) checkAndDecrement(namespace string, podName string, pdb policyv1beta1.PodDisruptionBudget, dryRun bool) error {
|
func (r *EvictionREST) checkAndDecrement(namespace string, podName string, pdb policyv1.PodDisruptionBudget, dryRun bool) error {
|
||||||
if pdb.Status.ObservedGeneration < pdb.Generation {
|
if pdb.Status.ObservedGeneration < pdb.Generation {
|
||||||
|
|
||||||
return createTooManyRequestsError(pdb.Name)
|
return createTooManyRequestsError(pdb.Name)
|
||||||
@ -358,17 +358,13 @@ func (r *EvictionREST) checkAndDecrement(namespace string, podName string, pdb p
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getPodDisruptionBudgets returns any PDBs that match the pod or err if there's an error.
|
// getPodDisruptionBudgets returns any PDBs that match the pod or err if there's an error.
|
||||||
func (r *EvictionREST) getPodDisruptionBudgets(ctx context.Context, pod *api.Pod) ([]policyv1beta1.PodDisruptionBudget, error) {
|
func (r *EvictionREST) getPodDisruptionBudgets(ctx context.Context, pod *api.Pod) ([]policyv1.PodDisruptionBudget, error) {
|
||||||
if len(pod.Labels) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
pdbList, err := r.podDisruptionBudgetClient.PodDisruptionBudgets(pod.Namespace).List(context.TODO(), metav1.ListOptions{})
|
pdbList, err := r.podDisruptionBudgetClient.PodDisruptionBudgets(pod.Namespace).List(context.TODO(), metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pdbs []policyv1beta1.PodDisruptionBudget
|
var pdbs []policyv1.PodDisruptionBudget
|
||||||
for _, pdb := range pdbList.Items {
|
for _, pdb := range pdbList.Items {
|
||||||
if pdb.Namespace != pod.Namespace {
|
if pdb.Namespace != pod.Namespace {
|
||||||
continue
|
continue
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
policyv1"k8s.io/api/policy/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||||
@ -52,10 +52,10 @@ func TestEviction(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "matching pdbs with no disruptions allowed, pod running",
|
name: "matching pdbs with no disruptions allowed, pod running",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t1", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t1", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectError: true,
|
expectError: true,
|
||||||
@ -64,10 +64,10 @@ func TestEviction(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "matching pdbs with no disruptions allowed, pod pending",
|
name: "matching pdbs with no disruptions allowed, pod pending",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t2", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t2", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectError: false,
|
expectError: false,
|
||||||
@ -77,10 +77,10 @@ func TestEviction(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "matching pdbs with no disruptions allowed, pod succeeded",
|
name: "matching pdbs with no disruptions allowed, pod succeeded",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t3", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t3", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectError: false,
|
expectError: false,
|
||||||
@ -90,10 +90,10 @@ func TestEviction(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "matching pdbs with no disruptions allowed, pod failed",
|
name: "matching pdbs with no disruptions allowed, pod failed",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t4", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t4", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectError: false,
|
expectError: false,
|
||||||
@ -103,10 +103,10 @@ func TestEviction(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "matching pdbs with disruptions allowed",
|
name: "matching pdbs with disruptions allowed",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t5", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t5", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectDeleted: true,
|
expectDeleted: true,
|
||||||
@ -114,10 +114,10 @@ func TestEviction(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "non-matching pdbs",
|
name: "non-matching pdbs",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"b": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"b": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t6", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t6", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectDeleted: true,
|
expectDeleted: true,
|
||||||
@ -125,10 +125,10 @@ func TestEviction(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "matching pdbs with disruptions allowed but bad name in Url",
|
name: "matching pdbs with disruptions allowed but bad name in Url",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
||||||
}},
|
}},
|
||||||
badNameInURL: true,
|
badNameInURL: true,
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t7", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t7", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
@ -161,7 +161,7 @@ func TestEviction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client := fake.NewSimpleClientset(tc.pdbs...)
|
client := fake.NewSimpleClientset(tc.pdbs...)
|
||||||
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1beta1())
|
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1())
|
||||||
|
|
||||||
name := pod.Name
|
name := pod.Name
|
||||||
if tc.badNameInURL {
|
if tc.badNameInURL {
|
||||||
@ -225,10 +225,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "pdbs No disruptions allowed, pod pending, first delete conflict, pod still pending, pod deleted successfully",
|
name: "pdbs No disruptions allowed, pod pending, first delete conflict, pod still pending, pod deleted successfully",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t1", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t1", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectError: false,
|
expectError: false,
|
||||||
@ -241,10 +241,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
// pod should not be deleted.
|
// pod should not be deleted.
|
||||||
{
|
{
|
||||||
name: "pdbs No disruptions allowed, pod pending, first delete conflict, pod becomes running, continueToPDBs",
|
name: "pdbs No disruptions allowed, pod pending, first delete conflict, pod becomes running, continueToPDBs",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t2", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t2", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectError: true,
|
expectError: true,
|
||||||
@ -254,10 +254,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pdbs disruptions allowed, pod pending, first delete conflict, pod becomes running, continueToPDBs",
|
name: "pdbs disruptions allowed, pod pending, first delete conflict, pod becomes running, continueToPDBs",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t3", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t3", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectError: false,
|
expectError: false,
|
||||||
@ -267,10 +267,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pod pending, always conflict on delete",
|
name: "pod pending, always conflict on delete",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t4", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t4", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(0)},
|
||||||
expectError: true,
|
expectError: true,
|
||||||
@ -280,10 +280,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "pod pending, always conflict on delete, user provided ResourceVersion constraint",
|
name: "pod pending, always conflict on delete, user provided ResourceVersion constraint",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t5", Namespace: "default"}, DeleteOptions: metav1.NewRVDeletionPrecondition("userProvided")},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t5", Namespace: "default"}, DeleteOptions: metav1.NewRVDeletionPrecondition("userProvided")},
|
||||||
expectError: true,
|
expectError: true,
|
||||||
@ -293,10 +293,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "matching pdbs with no disruptions allowed, pod terminating",
|
name: "matching pdbs with no disruptions allowed, pod terminating",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 0},
|
||||||
}},
|
}},
|
||||||
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t6", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(300)},
|
eviction: &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "t6", Namespace: "default"}, DeleteOptions: metav1.NewDeleteOptions(300)},
|
||||||
expectError: false,
|
expectError: false,
|
||||||
@ -306,10 +306,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "matching pdbs with no disruptions allowed, pod running, pod healthy, unhealthy pod not ours",
|
name: "matching pdbs with no disruptions allowed, pod running, pod healthy, unhealthy pod not ours",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{
|
Status: policyv1.PodDisruptionBudgetStatus{
|
||||||
// This simulates 3 pods desired, our pod healthy, unhealthy pod is not ours.
|
// This simulates 3 pods desired, our pod healthy, unhealthy pod is not ours.
|
||||||
DisruptionsAllowed: 0,
|
DisruptionsAllowed: 0,
|
||||||
CurrentHealthy: 2,
|
CurrentHealthy: 2,
|
||||||
@ -329,10 +329,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "matching pdbs with no disruptions allowed, pod running, pod unhealthy, unhealthy pod ours",
|
name: "matching pdbs with no disruptions allowed, pod running, pod unhealthy, unhealthy pod ours",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{
|
Status: policyv1.PodDisruptionBudgetStatus{
|
||||||
// This simulates 3 pods desired, our pod unhealthy
|
// This simulates 3 pods desired, our pod unhealthy
|
||||||
DisruptionsAllowed: 0,
|
DisruptionsAllowed: 0,
|
||||||
CurrentHealthy: 2,
|
CurrentHealthy: 2,
|
||||||
@ -353,10 +353,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
{
|
{
|
||||||
// This case should return the 529 retry error.
|
// This case should return the 529 retry error.
|
||||||
name: "matching pdbs with no disruptions allowed, pod running, pod unhealthy, unhealthy pod ours, resource version conflict",
|
name: "matching pdbs with no disruptions allowed, pod running, pod unhealthy, unhealthy pod ours, resource version conflict",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{
|
Status: policyv1.PodDisruptionBudgetStatus{
|
||||||
// This simulates 3 pods desired, our pod unhealthy
|
// This simulates 3 pods desired, our pod unhealthy
|
||||||
DisruptionsAllowed: 0,
|
DisruptionsAllowed: 0,
|
||||||
CurrentHealthy: 2,
|
CurrentHealthy: 2,
|
||||||
@ -377,10 +377,10 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
{
|
{
|
||||||
// This case should return the 529 retry error.
|
// This case should return the 529 retry error.
|
||||||
name: "matching pdbs with no disruptions allowed, pod running, pod unhealthy, unhealthy pod ours, other error on delete",
|
name: "matching pdbs with no disruptions allowed, pod running, pod unhealthy, unhealthy pod ours, other error on delete",
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{
|
Status: policyv1.PodDisruptionBudgetStatus{
|
||||||
// This simulates 3 pods desired, our pod unhealthy
|
// This simulates 3 pods desired, our pod unhealthy
|
||||||
DisruptionsAllowed: 0,
|
DisruptionsAllowed: 0,
|
||||||
CurrentHealthy: 2,
|
CurrentHealthy: 2,
|
||||||
@ -428,7 +428,7 @@ func TestEvictionIngorePDB(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client := fake.NewSimpleClientset(tc.pdbs...)
|
client := fake.NewSimpleClientset(tc.pdbs...)
|
||||||
evictionRest := newEvictionStorage(ms, client.PolicyV1beta1())
|
evictionRest := newEvictionStorage(ms, client.PolicyV1())
|
||||||
|
|
||||||
name := pod.Name
|
name := pod.Name
|
||||||
ms.pod = pod
|
ms.pod = pod
|
||||||
@ -473,10 +473,10 @@ func TestEvictionDryRun(t *testing.T) {
|
|||||||
name: "with pdbs",
|
name: "with pdbs",
|
||||||
evictionOptions: &metav1.DeleteOptions{DryRun: []string{"All"}},
|
evictionOptions: &metav1.DeleteOptions{DryRun: []string{"All"}},
|
||||||
requestOptions: &metav1.CreateOptions{DryRun: []string{"All"}},
|
requestOptions: &metav1.CreateOptions{DryRun: []string{"All"}},
|
||||||
pdbs: []runtime.Object{&policyv1beta1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -496,7 +496,7 @@ func TestEvictionDryRun(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client := fake.NewSimpleClientset(tc.pdbs...)
|
client := fake.NewSimpleClientset(tc.pdbs...)
|
||||||
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1beta1())
|
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1())
|
||||||
eviction := &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}, DeleteOptions: tc.evictionOptions}
|
eviction := &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}, DeleteOptions: tc.evictionOptions}
|
||||||
_, err := evictionRest.Create(testContext, pod.Name, eviction, nil, tc.requestOptions)
|
_, err := evictionRest.Create(testContext, pod.Name, eviction, nil, tc.requestOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -509,47 +509,47 @@ func TestEvictionDryRun(t *testing.T) {
|
|||||||
func TestEvictionPDBStatus(t *testing.T) {
|
func TestEvictionPDBStatus(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
pdb *policyv1beta1.PodDisruptionBudget
|
pdb *policyv1.PodDisruptionBudget
|
||||||
expectedDisruptionsAllowed int32
|
expectedDisruptionsAllowed int32
|
||||||
expectedReason string
|
expectedReason string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "pdb status is updated after eviction",
|
name: "pdb status is updated after eviction",
|
||||||
pdb: &policyv1beta1.PodDisruptionBudget{
|
pdb: &policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{
|
Status: policyv1.PodDisruptionBudgetStatus{
|
||||||
DisruptionsAllowed: 1,
|
DisruptionsAllowed: 1,
|
||||||
Conditions: []metav1.Condition{
|
Conditions: []metav1.Condition{
|
||||||
{
|
{
|
||||||
Type: policyv1beta1.DisruptionAllowedCondition,
|
Type: policyv1.DisruptionAllowedCondition,
|
||||||
Reason: policyv1beta1.SufficientPodsReason,
|
Reason: policyv1.SufficientPodsReason,
|
||||||
Status: metav1.ConditionTrue,
|
Status: metav1.ConditionTrue,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedDisruptionsAllowed: 0,
|
expectedDisruptionsAllowed: 0,
|
||||||
expectedReason: policyv1beta1.InsufficientPodsReason,
|
expectedReason: policyv1.InsufficientPodsReason,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "condition reason is only updated if AllowedDisruptions becomes 0",
|
name: "condition reason is only updated if AllowedDisruptions becomes 0",
|
||||||
pdb: &policyv1beta1.PodDisruptionBudget{
|
pdb: &policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||||
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
Spec: policyv1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
|
||||||
Status: policyv1beta1.PodDisruptionBudgetStatus{
|
Status: policyv1.PodDisruptionBudgetStatus{
|
||||||
DisruptionsAllowed: 3,
|
DisruptionsAllowed: 3,
|
||||||
Conditions: []metav1.Condition{
|
Conditions: []metav1.Condition{
|
||||||
{
|
{
|
||||||
Type: policyv1beta1.DisruptionAllowedCondition,
|
Type: policyv1.DisruptionAllowedCondition,
|
||||||
Reason: policyv1beta1.SufficientPodsReason,
|
Reason: policyv1.SufficientPodsReason,
|
||||||
Status: metav1.ConditionTrue,
|
Status: metav1.ConditionTrue,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedDisruptionsAllowed: 2,
|
expectedDisruptionsAllowed: 2,
|
||||||
expectedReason: policyv1beta1.SufficientPodsReason,
|
expectedReason: policyv1.SufficientPodsReason,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,14 +578,14 @@ func TestEvictionPDBStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1beta1())
|
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1())
|
||||||
eviction := &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "foo-1", Namespace: "default"}, DeleteOptions: &metav1.DeleteOptions{}}
|
eviction := &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "foo-1", Namespace: "default"}, DeleteOptions: &metav1.DeleteOptions{}}
|
||||||
_, err := evictionRest.Create(testContext, "foo-1", eviction, nil, &metav1.CreateOptions{})
|
_, err := evictionRest.Create(testContext, "foo-1", eviction, nil, &metav1.CreateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to run eviction: %v", err)
|
t.Fatalf("Failed to run eviction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
existingPDB, err := client.PolicyV1beta1().PodDisruptionBudgets(metav1.NamespaceDefault).Get(context.TODO(), tc.pdb.Name, metav1.GetOptions{})
|
existingPDB, err := client.PolicyV1().PodDisruptionBudgets(metav1.NamespaceDefault).Get(context.TODO(), tc.pdb.Name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%#v", err)
|
t.Errorf("%#v", err)
|
||||||
return
|
return
|
||||||
@ -595,7 +595,7 @@ func TestEvictionPDBStatus(t *testing.T) {
|
|||||||
t.Errorf("expected DisruptionsAllowed to be %d, but got %d", want, got)
|
t.Errorf("expected DisruptionsAllowed to be %d, but got %d", want, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
cond := apimeta.FindStatusCondition(existingPDB.Status.Conditions, policyv1beta1.DisruptionAllowedCondition)
|
cond := apimeta.FindStatusCondition(existingPDB.Status.Conditions, policyv1.DisruptionAllowedCondition)
|
||||||
if want, got := tc.expectedReason, cond.Reason; want != got {
|
if want, got := tc.expectedReason, cond.Reason; want != got {
|
||||||
t.Errorf("expected Reason to be %q, but got %q", want, got)
|
t.Errorf("expected Reason to be %q, but got %q", want, got)
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ import (
|
|||||||
storeerr "k8s.io/apiserver/pkg/storage/errors"
|
storeerr "k8s.io/apiserver/pkg/storage/errors"
|
||||||
"k8s.io/apiserver/pkg/util/dryrun"
|
"k8s.io/apiserver/pkg/util/dryrun"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
|
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
|
@ -35,7 +35,7 @@ import (
|
|||||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd3"
|
"k8s.io/apiserver/pkg/storage/etcd3"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
|
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
@ -110,13 +110,9 @@ func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter generi
|
|||||||
NegotiatedSerializer: legacyscheme.Codecs,
|
NegotiatedSerializer: legacyscheme.Codecs,
|
||||||
}
|
}
|
||||||
|
|
||||||
var podDisruptionClient policyclient.PodDisruptionBudgetsGetter
|
podDisruptionClient, err := policyclient.NewForConfig(c.LoopbackClientConfig)
|
||||||
if policyGroupVersion := (schema.GroupVersion{Group: "policy", Version: "v1beta1"}); legacyscheme.Scheme.IsVersionRegistered(policyGroupVersion) {
|
if err != nil {
|
||||||
var err error
|
return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, err
|
||||||
podDisruptionClient, err = policyclient.NewForConfig(c.LoopbackClientConfig)
|
|
||||||
if err != nil {
|
|
||||||
return LegacyRESTStorage{}, genericapiserver.APIGroupInfo{}, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
restStorage := LegacyRESTStorage{}
|
restStorage := LegacyRESTStorage{}
|
||||||
|
|
||||||
|
@ -114,8 +114,8 @@ func (podDisruptionBudgetStatusStrategy) ValidateUpdate(ctx context.Context, obj
|
|||||||
var apiVersion schema.GroupVersion
|
var apiVersion schema.GroupVersion
|
||||||
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
|
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
|
||||||
apiVersion = schema.GroupVersion{
|
apiVersion = schema.GroupVersion{
|
||||||
Group: requestInfo.APIVersion,
|
Group: requestInfo.APIGroup,
|
||||||
Version: requestInfo.APIGroup,
|
Version: requestInfo.APIVersion,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return validation.ValidatePodDisruptionBudgetStatusUpdate(obj.(*policy.PodDisruptionBudget).Status,
|
return validation.ValidatePodDisruptionBudgetStatusUpdate(obj.(*policy.PodDisruptionBudget).Status,
|
||||||
|
@ -148,3 +148,65 @@ func TestPodDisruptionBudgetStatusStrategy(t *testing.T) {
|
|||||||
t.Errorf("Unexpected error %v", errs)
|
t.Errorf("Unexpected error %v", errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPodDisruptionBudgetStatusValidationByApiVersion(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
apiVersion string
|
||||||
|
validation bool
|
||||||
|
}{
|
||||||
|
"policy/v1beta1 should not do update validation": {
|
||||||
|
apiVersion: "v1beta1",
|
||||||
|
validation: false,
|
||||||
|
},
|
||||||
|
"policy/v1 should do update validation": {
|
||||||
|
apiVersion: "v1",
|
||||||
|
validation: true,
|
||||||
|
},
|
||||||
|
"policy/some-version should do update validation": {
|
||||||
|
apiVersion: "some-version",
|
||||||
|
validation: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tn, tc := range testCases {
|
||||||
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(),
|
||||||
|
&genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "policy",
|
||||||
|
APIVersion: tc.apiVersion,
|
||||||
|
})
|
||||||
|
|
||||||
|
oldMaxUnavailable := intstr.FromInt(2)
|
||||||
|
newMaxUnavailable := intstr.FromInt(3)
|
||||||
|
oldPdb := &policy.PodDisruptionBudget{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "10"},
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
|
||||||
|
MaxUnavailable: &oldMaxUnavailable,
|
||||||
|
},
|
||||||
|
Status: policy.PodDisruptionBudgetStatus{
|
||||||
|
DisruptionsAllowed: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
newPdb := &policy.PodDisruptionBudget{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "9"},
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
|
||||||
|
MinAvailable: &newMaxUnavailable,
|
||||||
|
},
|
||||||
|
Status: policy.PodDisruptionBudgetStatus{
|
||||||
|
DisruptionsAllowed: -1, // This is not allowed, so should trigger validation error.
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := StatusStrategy.ValidateUpdate(ctx, newPdb, oldPdb)
|
||||||
|
hasErrors := len(errs) > 0
|
||||||
|
if !tc.validation && hasErrors {
|
||||||
|
t.Errorf("Validation failed when no validation should happen")
|
||||||
|
}
|
||||||
|
if tc.validation && !hasErrors {
|
||||||
|
t.Errorf("Expected validation errors but didn't get any")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package rest
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
policyapiv1 "k8s.io/api/policy/v1"
|
||||||
policyapiv1beta1 "k8s.io/api/policy/v1beta1"
|
policyapiv1beta1 "k8s.io/api/policy/v1beta1"
|
||||||
"k8s.io/apiserver/pkg/registry/generic"
|
"k8s.io/apiserver/pkg/registry/generic"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
@ -42,6 +43,14 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
|
|||||||
apiGroupInfo.VersionedResourcesStorageMap[policyapiv1beta1.SchemeGroupVersion.Version] = storageMap
|
apiGroupInfo.VersionedResourcesStorageMap[policyapiv1beta1.SchemeGroupVersion.Version] = storageMap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if apiResourceConfigSource.VersionEnabled(policyapiv1.SchemeGroupVersion) {
|
||||||
|
if storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
|
||||||
|
return genericapiserver.APIGroupInfo{}, false, err
|
||||||
|
} else {
|
||||||
|
apiGroupInfo.VersionedResourcesStorageMap[policyapiv1.SchemeGroupVersion.Version] = storageMap
|
||||||
|
}
|
||||||
|
}
|
||||||
return apiGroupInfo, true, nil
|
return apiGroupInfo, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +73,19 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag
|
|||||||
return storage, err
|
return storage, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
|
||||||
|
storage := map[string]rest.Storage{}
|
||||||
|
|
||||||
|
poddisruptionbudgetStorage, poddisruptionbudgetStatusStorage, err := poddisruptionbudgetstore.NewREST(restOptionsGetter)
|
||||||
|
if err != nil {
|
||||||
|
return storage, err
|
||||||
|
}
|
||||||
|
storage["poddisruptionbudgets"] = poddisruptionbudgetStorage
|
||||||
|
storage["poddisruptionbudgets/status"] = poddisruptionbudgetStatusStorage
|
||||||
|
|
||||||
|
return storage, err
|
||||||
|
}
|
||||||
|
|
||||||
func (p RESTStorageProvider) GroupName() string {
|
func (p RESTStorageProvider) GroupName() string {
|
||||||
return policy.GroupName
|
return policy.GroupName
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ import (
|
|||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
policy "k8s.io/api/policy/v1beta1"
|
policy "k8s.io/api/policy/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -37,7 +37,7 @@ import (
|
|||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
corelisters "k8s.io/client-go/listers/core/v1"
|
||||||
policylisters "k8s.io/client-go/listers/policy/v1beta1"
|
policylisters "k8s.io/client-go/listers/policy/v1"
|
||||||
corev1helpers "k8s.io/component-helpers/scheduling/corev1"
|
corev1helpers "k8s.io/component-helpers/scheduling/corev1"
|
||||||
extenderv1 "k8s.io/kube-scheduler/extender/v1"
|
extenderv1 "k8s.io/kube-scheduler/extender/v1"
|
||||||
kubefeatures "k8s.io/kubernetes/pkg/features"
|
kubefeatures "k8s.io/kubernetes/pkg/features"
|
||||||
@ -799,7 +799,7 @@ func filterPodsWithPDBViolation(podInfos []*framework.PodInfo, pdbs []*policy.Po
|
|||||||
|
|
||||||
func getPDBLister(informerFactory informers.SharedInformerFactory) policylisters.PodDisruptionBudgetLister {
|
func getPDBLister(informerFactory informers.SharedInformerFactory) policylisters.PodDisruptionBudgetLister {
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PodDisruptionBudget) {
|
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PodDisruptionBudget) {
|
||||||
return informerFactory.Policy().V1beta1().PodDisruptionBudgets().Lister()
|
return informerFactory.Policy().V1().PodDisruptionBudgets().Lister()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
policy "k8s.io/api/policy/v1beta1"
|
policy "k8s.io/api/policy/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
24
staging/src/k8s.io/api/policy/v1/doc.go
Normal file
24
staging/src/k8s.io/api/policy/v1/doc.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=package
|
||||||
|
// +k8s:protobuf-gen=package
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
|
||||||
|
// Package policy is for any kind of policy object. Suitable examples, even if
|
||||||
|
// they aren't all here, are PodDisruptionBudget, PodSecurityPolicy,
|
||||||
|
// NetworkPolicy, etc.
|
||||||
|
package v1 // import "k8s.io/api/policy/v1"
|
51
staging/src/k8s.io/api/policy/v1/register.go
Normal file
51
staging/src/k8s.io/api/policy/v1/register.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GroupName is the group name use in this package
|
||||||
|
const GroupName = "policy"
|
||||||
|
|
||||||
|
// SchemeGroupVersion is group version used to register these objects
|
||||||
|
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
|
||||||
|
|
||||||
|
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||||
|
func Resource(resource string) schema.GroupResource {
|
||||||
|
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||||
|
localSchemeBuilder = &SchemeBuilder
|
||||||
|
AddToScheme = localSchemeBuilder.AddToScheme
|
||||||
|
)
|
||||||
|
|
||||||
|
// Adds the list of known types to the given scheme.
|
||||||
|
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||||
|
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||||
|
&PodDisruptionBudget{},
|
||||||
|
&PodDisruptionBudgetList{},
|
||||||
|
)
|
||||||
|
// Add the watch version that applies
|
||||||
|
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||||
|
return nil
|
||||||
|
}
|
150
staging/src/k8s.io/api/policy/v1/types.go
Normal file
150
staging/src/k8s.io/api/policy/v1/types.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PodDisruptionBudgetSpec is a description of a PodDisruptionBudget.
|
||||||
|
type PodDisruptionBudgetSpec struct {
|
||||||
|
// An eviction is allowed if at least "minAvailable" pods selected by
|
||||||
|
// "selector" will still be available after the eviction, i.e. even in the
|
||||||
|
// absence of the evicted pod. So for example you can prevent all voluntary
|
||||||
|
// evictions by specifying "100%".
|
||||||
|
// +optional
|
||||||
|
MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty" protobuf:"bytes,1,opt,name=minAvailable"`
|
||||||
|
|
||||||
|
// Label query over pods whose evictions are managed by the disruption
|
||||||
|
// budget.
|
||||||
|
// A null selector will match no pods, while an empty ({}) selector will select
|
||||||
|
// all pods within the namespace.
|
||||||
|
// +patchStrategy=replace
|
||||||
|
// +optional
|
||||||
|
Selector *metav1.LabelSelector `json:"selector,omitempty" patchStrategy:"replace" protobuf:"bytes,2,opt,name=selector"`
|
||||||
|
|
||||||
|
// An eviction is allowed if at most "maxUnavailable" pods selected by
|
||||||
|
// "selector" are unavailable after the eviction, i.e. even in absence of
|
||||||
|
// the evicted pod. For example, one can prevent all voluntary evictions
|
||||||
|
// by specifying 0. This is a mutually exclusive setting with "minAvailable".
|
||||||
|
// +optional
|
||||||
|
MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty" protobuf:"bytes,3,opt,name=maxUnavailable"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PodDisruptionBudgetStatus represents information about the status of a
|
||||||
|
// PodDisruptionBudget. Status may trail the actual state of a system.
|
||||||
|
type PodDisruptionBudgetStatus struct {
|
||||||
|
// Most recent generation observed when updating this PDB status. DisruptionsAllowed and other
|
||||||
|
// status information is valid only if observedGeneration equals to PDB's object generation.
|
||||||
|
// +optional
|
||||||
|
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,1,opt,name=observedGeneration"`
|
||||||
|
|
||||||
|
// DisruptedPods contains information about pods whose eviction was
|
||||||
|
// processed by the API server eviction subresource handler but has not
|
||||||
|
// yet been observed by the PodDisruptionBudget controller.
|
||||||
|
// A pod will be in this map from the time when the API server processed the
|
||||||
|
// eviction request to the time when the pod is seen by PDB controller
|
||||||
|
// as having been marked for deletion (or after a timeout). The key in the map is the name of the pod
|
||||||
|
// and the value is the time when the API server processed the eviction request. If
|
||||||
|
// the deletion didn't occur and a pod is still there it will be removed from
|
||||||
|
// the list automatically by PodDisruptionBudget controller after some time.
|
||||||
|
// If everything goes smooth this map should be empty for the most of the time.
|
||||||
|
// Large number of entries in the map may indicate problems with pod deletions.
|
||||||
|
// +optional
|
||||||
|
DisruptedPods map[string]metav1.Time `json:"disruptedPods,omitempty" protobuf:"bytes,2,rep,name=disruptedPods"`
|
||||||
|
|
||||||
|
// Number of pod disruptions that are currently allowed.
|
||||||
|
DisruptionsAllowed int32 `json:"disruptionsAllowed" protobuf:"varint,3,opt,name=disruptionsAllowed"`
|
||||||
|
|
||||||
|
// current number of healthy pods
|
||||||
|
CurrentHealthy int32 `json:"currentHealthy" protobuf:"varint,4,opt,name=currentHealthy"`
|
||||||
|
|
||||||
|
// minimum desired number of healthy pods
|
||||||
|
DesiredHealthy int32 `json:"desiredHealthy" protobuf:"varint,5,opt,name=desiredHealthy"`
|
||||||
|
|
||||||
|
// total number of pods counted by this disruption budget
|
||||||
|
ExpectedPods int32 `json:"expectedPods" protobuf:"varint,6,opt,name=expectedPods"`
|
||||||
|
|
||||||
|
// Conditions contain conditions for PDB. The disruption controller sets the
|
||||||
|
// DisruptionAllowed condition. The following are known values for the reason field
|
||||||
|
// (additional reasons could be added in the future):
|
||||||
|
// - SyncFailed: The controller encountered an error and wasn't able to compute
|
||||||
|
// the number of allowed disruptions. Therefore no disruptions are
|
||||||
|
// allowed and the status of the condition will be False.
|
||||||
|
// - InsufficientPods: The number of pods are either at or below the number
|
||||||
|
// required by the PodDisruptionBudget. No disruptions are
|
||||||
|
// allowed and the status of the condition will be False.
|
||||||
|
// - SufficientPods: There are more pods than required by the PodDisruptionBudget.
|
||||||
|
// The condition will be True, and the number of allowed
|
||||||
|
// disruptions are provided by the disruptionsAllowed property.
|
||||||
|
//
|
||||||
|
// +optional
|
||||||
|
// +patchMergeKey=type
|
||||||
|
// +patchStrategy=merge
|
||||||
|
// +listType=map
|
||||||
|
// +listMapKey=type
|
||||||
|
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,7,rep,name=conditions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DisruptionAllowedCondition is a condition set by the disruption controller
|
||||||
|
// that signal whether any of the pods covered by the PDB can be disrupted.
|
||||||
|
DisruptionAllowedCondition = "DisruptionAllowed"
|
||||||
|
|
||||||
|
// SyncFailedReason is set on the DisruptionAllowed condition if reconcile
|
||||||
|
// of the PDB failed and therefore disruption of pods are not allowed.
|
||||||
|
SyncFailedReason = "SyncFailed"
|
||||||
|
// SufficientPodsReason is set on the DisruptionAllowed condition if there are
|
||||||
|
// more pods covered by the PDB than required and at least one can be disrupted.
|
||||||
|
SufficientPodsReason = "SufficientPods"
|
||||||
|
// InsufficientPodsReason is set on the DisruptionAllowed condition if the number
|
||||||
|
// of pods are equal to or fewer than required by the PDB.
|
||||||
|
InsufficientPodsReason = "InsufficientPods"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +genclient
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods
|
||||||
|
type PodDisruptionBudget struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
// Standard object's metadata.
|
||||||
|
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||||
|
// +optional
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
|
|
||||||
|
// Specification of the desired behavior of the PodDisruptionBudget.
|
||||||
|
// +optional
|
||||||
|
Spec PodDisruptionBudgetSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
|
||||||
|
// Most recently observed status of the PodDisruptionBudget.
|
||||||
|
// +optional
|
||||||
|
Status PodDisruptionBudgetStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// PodDisruptionBudgetList is a collection of PodDisruptionBudgets.
|
||||||
|
type PodDisruptionBudgetList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
// Standard object's metadata.
|
||||||
|
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||||
|
// +optional
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
|
// Items is a list of PodDisruptionBudgets
|
||||||
|
Items []PodDisruptionBudget `json:"items" protobuf:"bytes,2,rep,name=items"`
|
||||||
|
}
|
@ -33,8 +33,12 @@ type PodDisruptionBudgetSpec struct {
|
|||||||
|
|
||||||
// Label query over pods whose evictions are managed by the disruption
|
// Label query over pods whose evictions are managed by the disruption
|
||||||
// budget.
|
// budget.
|
||||||
|
// A null selector selects no pods.
|
||||||
|
// An empty selector ({}) also selects no pods, which differs from standard behavior of selecting all pods.
|
||||||
|
// In policy/v1, an empty selector will select all pods in the namespace.
|
||||||
|
// +patchStrategy=replace
|
||||||
// +optional
|
// +optional
|
||||||
Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"`
|
Selector *metav1.LabelSelector `json:"selector,omitempty" patchStrategy:"replace" protobuf:"bytes,2,opt,name=selector"`
|
||||||
|
|
||||||
// An eviction is allowed if at most "maxUnavailable" pods selected by
|
// An eviction is allowed if at most "maxUnavailable" pods selected by
|
||||||
// "selector" are unavailable after the eviction, i.e. even in absence of
|
// "selector" are unavailable after the eviction, i.e. even in absence of
|
||||||
@ -118,7 +122,9 @@ const (
|
|||||||
// +genclient
|
// +genclient
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
// +k8s:prerelease-lifecycle-gen:introduced=1.5
|
// +k8s:prerelease-lifecycle-gen:introduced=1.5
|
||||||
// +k8s:prerelease-lifecycle-gen:deprecated=1.22
|
// +k8s:prerelease-lifecycle-gen:deprecated=1.21
|
||||||
|
// +k8s:prerelease-lifecycle-gen:removed=1.25
|
||||||
|
// +k8s:prerelease-lifecycle-gen:replacement=policy,v1,PodDisruptionBudget
|
||||||
|
|
||||||
// PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods
|
// PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods
|
||||||
type PodDisruptionBudget struct {
|
type PodDisruptionBudget struct {
|
||||||
@ -136,7 +142,9 @@ type PodDisruptionBudget struct {
|
|||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
// +k8s:prerelease-lifecycle-gen:introduced=1.5
|
// +k8s:prerelease-lifecycle-gen:introduced=1.5
|
||||||
// +k8s:prerelease-lifecycle-gen:deprecated=1.22
|
// +k8s:prerelease-lifecycle-gen:deprecated=1.21
|
||||||
|
// +k8s:prerelease-lifecycle-gen:removed=1.25
|
||||||
|
// +k8s:prerelease-lifecycle-gen:replacement=policy,v1,PodDisruptionBudgetList
|
||||||
|
|
||||||
// PodDisruptionBudgetList is a collection of PodDisruptionBudgets.
|
// PodDisruptionBudgetList is a collection of PodDisruptionBudgets.
|
||||||
type PodDisruptionBudgetList struct {
|
type PodDisruptionBudgetList struct {
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
admissionv1 "k8s.io/api/admission/v1"
|
admissionv1 "k8s.io/api/admission/v1"
|
||||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||||
admissionregv1 "k8s.io/api/admissionregistration/v1"
|
admissionregv1 "k8s.io/api/admissionregistration/v1"
|
||||||
@ -110,6 +111,7 @@ var groups = []runtime.SchemeBuilder{
|
|||||||
nodev1.SchemeBuilder,
|
nodev1.SchemeBuilder,
|
||||||
nodev1alpha1.SchemeBuilder,
|
nodev1alpha1.SchemeBuilder,
|
||||||
nodev1beta1.SchemeBuilder,
|
nodev1beta1.SchemeBuilder,
|
||||||
|
policyv1.SchemeBuilder,
|
||||||
policyv1beta1.SchemeBuilder,
|
policyv1beta1.SchemeBuilder,
|
||||||
rbacv1alpha1.SchemeBuilder,
|
rbacv1alpha1.SchemeBuilder,
|
||||||
rbacv1beta1.SchemeBuilder,
|
rbacv1beta1.SchemeBuilder,
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 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 v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
policy "k8s.io/api/policy/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PodDisruptionBudgetListerExpansion allows custom methods to be added to
|
||||||
|
// PodDisruptionBudgetLister.
|
||||||
|
type PodDisruptionBudgetListerExpansion interface {
|
||||||
|
GetPodPodDisruptionBudgets(pod *v1.Pod) ([]*policy.PodDisruptionBudget, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PodDisruptionBudgetNamespaceListerExpansion allows custom methods to be added to
|
||||||
|
// PodDisruptionBudgetNamespaceLister.
|
||||||
|
type PodDisruptionBudgetNamespaceListerExpansion interface{}
|
||||||
|
|
||||||
|
// GetPodPodDisruptionBudgets returns a list of PodDisruptionBudgets matching a pod.
|
||||||
|
func (s *podDisruptionBudgetLister) GetPodPodDisruptionBudgets(pod *v1.Pod) ([]*policy.PodDisruptionBudget, error) {
|
||||||
|
var selector labels.Selector
|
||||||
|
|
||||||
|
list, err := s.PodDisruptionBudgets(pod.Namespace).List(labels.Everything())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pdbList []*policy.PodDisruptionBudget
|
||||||
|
for i := range list {
|
||||||
|
pdb := list[i]
|
||||||
|
selector, err = metav1.LabelSelectorAsSelector(pdb.Spec.Selector)
|
||||||
|
if err != nil {
|
||||||
|
klog.Warningf("invalid selector: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlike the v1beta version, here we let an empty selector match everything.
|
||||||
|
if !selector.Matches(labels.Set(pod.Labels)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pdbList = append(pdbList, pdb)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pdbList) == 0 {
|
||||||
|
return nil, fmt.Errorf("could not find PodDisruptionBudget for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pdbList, nil
|
||||||
|
}
|
@ -40,10 +40,6 @@ type PodDisruptionBudgetNamespaceListerExpansion interface{}
|
|||||||
func (s *podDisruptionBudgetLister) GetPodPodDisruptionBudgets(pod *v1.Pod) ([]*policy.PodDisruptionBudget, error) {
|
func (s *podDisruptionBudgetLister) GetPodPodDisruptionBudgets(pod *v1.Pod) ([]*policy.PodDisruptionBudget, error) {
|
||||||
var selector labels.Selector
|
var selector labels.Selector
|
||||||
|
|
||||||
if len(pod.Labels) == 0 {
|
|
||||||
return nil, fmt.Errorf("no PodDisruptionBudgets found for pod %v because it has no labels", pod.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
list, err := s.PodDisruptionBudgets(pod.Namespace).List(labels.Everything())
|
list, err := s.PodDisruptionBudgets(pod.Namespace).List(labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
package poddisruptionbudget
|
package poddisruptionbudget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
policy "k8s.io/api/policy/v1beta1"
|
policy "k8s.io/api/policy/v1"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
@ -38,6 +38,7 @@ import (
|
|||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
imagepolicyv1alpha1 "k8s.io/api/imagepolicy/v1alpha1"
|
imagepolicyv1alpha1 "k8s.io/api/imagepolicy/v1alpha1"
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
|
rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
|
||||||
@ -74,7 +75,7 @@ func init() {
|
|||||||
utilruntime.Must(Scheme.SetVersionPriority(extensionsv1beta1.SchemeGroupVersion))
|
utilruntime.Must(Scheme.SetVersionPriority(extensionsv1beta1.SchemeGroupVersion))
|
||||||
utilruntime.Must(Scheme.SetVersionPriority(imagepolicyv1alpha1.SchemeGroupVersion))
|
utilruntime.Must(Scheme.SetVersionPriority(imagepolicyv1alpha1.SchemeGroupVersion))
|
||||||
utilruntime.Must(Scheme.SetVersionPriority(networkingv1.SchemeGroupVersion))
|
utilruntime.Must(Scheme.SetVersionPriority(networkingv1.SchemeGroupVersion))
|
||||||
utilruntime.Must(Scheme.SetVersionPriority(policyv1beta1.SchemeGroupVersion))
|
utilruntime.Must(Scheme.SetVersionPriority(policyv1beta1.SchemeGroupVersion, policyv1.SchemeGroupVersion))
|
||||||
utilruntime.Must(Scheme.SetVersionPriority(rbacv1.SchemeGroupVersion, rbacv1beta1.SchemeGroupVersion, rbacv1alpha1.SchemeGroupVersion))
|
utilruntime.Must(Scheme.SetVersionPriority(rbacv1.SchemeGroupVersion, rbacv1beta1.SchemeGroupVersion, rbacv1alpha1.SchemeGroupVersion))
|
||||||
utilruntime.Must(Scheme.SetVersionPriority(schedulingv1alpha1.SchemeGroupVersion))
|
utilruntime.Must(Scheme.SetVersionPriority(schedulingv1alpha1.SchemeGroupVersion))
|
||||||
utilruntime.Must(Scheme.SetVersionPriority(storagev1.SchemeGroupVersion, storagev1beta1.SchemeGroupVersion))
|
utilruntime.Must(Scheme.SetVersionPriority(storagev1.SchemeGroupVersion, storagev1beta1.SchemeGroupVersion))
|
||||||
|
@ -119,9 +119,9 @@ var (
|
|||||||
gvr("", "v1", "pods/exec"): {"create": testPodConnectSubresource},
|
gvr("", "v1", "pods/exec"): {"create": testPodConnectSubresource},
|
||||||
gvr("", "v1", "pods/portforward"): {"create": testPodConnectSubresource},
|
gvr("", "v1", "pods/portforward"): {"create": testPodConnectSubresource},
|
||||||
|
|
||||||
gvr("", "v1", "bindings"): {"create": testPodBindingEviction},
|
gvr("", "v1", "bindings"): {"create": testPodBindingEviction},
|
||||||
gvr("", "v1", "pods/binding"): {"create": testPodBindingEviction},
|
gvr("", "v1", "pods/binding"): {"create": testPodBindingEviction},
|
||||||
gvr("", "v1", "pods/eviction"): {"create": testPodBindingEviction},
|
gvr("", "v1", "pods/eviction"): {"create": testPodBindingEviction},
|
||||||
|
|
||||||
gvr("", "v1", "nodes/proxy"): {"*": testSubresourceProxy},
|
gvr("", "v1", "nodes/proxy"): {"*": testSubresourceProxy},
|
||||||
gvr("", "v1", "pods/proxy"): {"*": testSubresourceProxy},
|
gvr("", "v1", "pods/proxy"): {"*": testSubresourceProxy},
|
||||||
|
@ -54,6 +54,7 @@ var statusData = map[schema.GroupVersionResource]string{
|
|||||||
gvr("batch", "v1", "cronjobs"): `{"status": {"lastScheduleTime": null}}`,
|
gvr("batch", "v1", "cronjobs"): `{"status": {"lastScheduleTime": null}}`,
|
||||||
gvr("batch", "v1beta1", "cronjobs"): `{"status": {"lastScheduleTime": null}}`,
|
gvr("batch", "v1beta1", "cronjobs"): `{"status": {"lastScheduleTime": null}}`,
|
||||||
gvr("storage.k8s.io", "v1", "volumeattachments"): `{"status": {"attached": true}}`,
|
gvr("storage.k8s.io", "v1", "volumeattachments"): `{"status": {"attached": true}}`,
|
||||||
|
gvr("policy", "v1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`,
|
||||||
gvr("policy", "v1beta1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`,
|
gvr("policy", "v1beta1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`,
|
||||||
gvr("certificates.k8s.io", "v1beta1", "certificatesigningrequests"): `{"status": {"conditions": [{"type": "MyStatus"}]}}`,
|
gvr("certificates.k8s.io", "v1beta1", "certificatesigningrequests"): `{"status": {"conditions": [{"type": "MyStatus"}]}}`,
|
||||||
gvr("certificates.k8s.io", "v1", "certificatesigningrequests"): `{"status": {"conditions": [{"type": "MyStatus", "status": "True"}]}}`,
|
gvr("certificates.k8s.io", "v1", "certificatesigningrequests"): `{"status": {"conditions": [{"type": "MyStatus", "status": "True"}]}}`,
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
"k8s.io/api/policy/v1beta1"
|
"k8s.io/api/policy/v1beta1"
|
||||||
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -81,7 +82,7 @@ func setup(t *testing.T) (*kubeapiservertesting.TestServer, *disruption.Disrupti
|
|||||||
|
|
||||||
pdbc := disruption.NewDisruptionController(
|
pdbc := disruption.NewDisruptionController(
|
||||||
informers.Core().V1().Pods(),
|
informers.Core().V1().Pods(),
|
||||||
informers.Policy().V1beta1().PodDisruptionBudgets(),
|
informers.Policy().V1().PodDisruptionBudgets(),
|
||||||
informers.Core().V1().ReplicationControllers(),
|
informers.Core().V1().ReplicationControllers(),
|
||||||
informers.Apps().V1().ReplicaSets(),
|
informers.Apps().V1().ReplicaSets(),
|
||||||
informers.Apps().V1().Deployments(),
|
informers.Apps().V1().Deployments(),
|
||||||
@ -113,7 +114,6 @@ func TestPDBWithScaleSubresource(t *testing.T) {
|
|||||||
|
|
||||||
replicas := 4
|
replicas := 4
|
||||||
maxUnavailable := int32(2)
|
maxUnavailable := int32(2)
|
||||||
podLabelValue := "test-crd"
|
|
||||||
|
|
||||||
resource := &unstructured.Unstructured{
|
resource := &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]interface{}{
|
||||||
@ -134,40 +134,42 @@ func TestPDBWithScaleSubresource(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trueValue := true
|
trueValue := true
|
||||||
ownerRef := metav1.OwnerReference{
|
ownerRefs := []metav1.OwnerReference{
|
||||||
Name: resource.GetName(),
|
{
|
||||||
Kind: crdDefinition.Spec.Names.Kind,
|
Name: resource.GetName(),
|
||||||
APIVersion: crdDefinition.Spec.Group + "/" + crdDefinition.Spec.Versions[0].Name,
|
Kind: crdDefinition.Spec.Names.Kind,
|
||||||
UID: createdResource.GetUID(),
|
APIVersion: crdDefinition.Spec.Group + "/" + crdDefinition.Spec.Versions[0].Name,
|
||||||
Controller: &trueValue,
|
UID: createdResource.GetUID(),
|
||||||
|
Controller: &trueValue,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for i := 0; i < replicas; i++ {
|
for i := 0; i < replicas; i++ {
|
||||||
createPod(t, fmt.Sprintf("pod-%d", i), nsName, podLabelValue, clientSet, ownerRef)
|
createPod(t, fmt.Sprintf("pod-%d", i), nsName, map[string]string{"app": "test-crd"}, clientSet, ownerRefs)
|
||||||
}
|
}
|
||||||
|
|
||||||
waitToObservePods(t, informers.Core().V1().Pods().Informer(), 4, v1.PodRunning)
|
waitToObservePods(t, informers.Core().V1().Pods().Informer(), 4, v1.PodRunning)
|
||||||
|
|
||||||
pdb := &v1beta1.PodDisruptionBudget{
|
pdb := &policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-pdb",
|
Name: "test-pdb",
|
||||||
},
|
},
|
||||||
Spec: v1beta1.PodDisruptionBudgetSpec{
|
Spec: policyv1.PodDisruptionBudgetSpec{
|
||||||
MaxUnavailable: &intstr.IntOrString{
|
MaxUnavailable: &intstr.IntOrString{
|
||||||
Type: intstr.Int,
|
Type: intstr.Int,
|
||||||
IntVal: maxUnavailable,
|
IntVal: maxUnavailable,
|
||||||
},
|
},
|
||||||
Selector: &metav1.LabelSelector{
|
Selector: &metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{"app": podLabelValue},
|
MatchLabels: map[string]string{"app": "test-crd"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if _, err := clientSet.PolicyV1beta1().PodDisruptionBudgets(nsName).Create(context.TODO(), pdb, metav1.CreateOptions{}); err != nil {
|
if _, err := clientSet.PolicyV1().PodDisruptionBudgets(nsName).Create(context.TODO(), pdb, metav1.CreateOptions{}); err != nil {
|
||||||
t.Errorf("Error creating PodDisruptionBudget: %v", err)
|
t.Errorf("Error creating PodDisruptionBudget: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
waitPDBStable(t, clientSet, 4, nsName, pdb.Name)
|
waitPDBStable(t, clientSet, 4, nsName, pdb.Name)
|
||||||
|
|
||||||
newPdb, err := clientSet.PolicyV1beta1().PodDisruptionBudgets(nsName).Get(context.TODO(), pdb.Name, metav1.GetOptions{})
|
newPdb, err := clientSet.PolicyV1().PodDisruptionBudgets(nsName).Get(context.TODO(), pdb.Name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error getting PodDisruptionBudget: %v", err)
|
t.Errorf("Error getting PodDisruptionBudget: %v", err)
|
||||||
}
|
}
|
||||||
@ -183,15 +185,211 @@ func TestPDBWithScaleSubresource(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPod(t *testing.T, name, namespace, labelValue string, clientSet clientset.Interface, ownerRef metav1.OwnerReference) {
|
func TestEmptySelector(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
createPDBFunc func(clientSet clientset.Interface, name, nsName string, minAvailable intstr.IntOrString) error
|
||||||
|
expectedCurrentHealthy int32
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "v1beta1 should not target any pods",
|
||||||
|
createPDBFunc: func(clientSet clientset.Interface, name, nsName string, minAvailable intstr.IntOrString) error {
|
||||||
|
pdb := &v1beta1.PodDisruptionBudget{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: v1beta1.PodDisruptionBudgetSpec{
|
||||||
|
MinAvailable: &minAvailable,
|
||||||
|
Selector: &metav1.LabelSelector{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := clientSet.PolicyV1beta1().PodDisruptionBudgets(nsName).Create(context.TODO(), pdb, metav1.CreateOptions{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
expectedCurrentHealthy: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1 should target all pods",
|
||||||
|
createPDBFunc: func(clientSet clientset.Interface, name, nsName string, minAvailable intstr.IntOrString) error {
|
||||||
|
pdb := &policyv1.PodDisruptionBudget{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: policyv1.PodDisruptionBudgetSpec{
|
||||||
|
MinAvailable: &minAvailable,
|
||||||
|
Selector: &metav1.LabelSelector{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := clientSet.PolicyV1().PodDisruptionBudgets(nsName).Create(context.TODO(), pdb, metav1.CreateOptions{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
expectedCurrentHealthy: 4,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
s, pdbc, informers, clientSet, _, _ := setup(t)
|
||||||
|
defer s.TearDownFn()
|
||||||
|
|
||||||
|
nsName := fmt.Sprintf("pdb-empty-selector-%d", i)
|
||||||
|
createNs(t, nsName, clientSet)
|
||||||
|
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
informers.Start(stopCh)
|
||||||
|
go pdbc.Run(stopCh)
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
replicas := 4
|
||||||
|
minAvailable := intstr.FromInt(2)
|
||||||
|
|
||||||
|
for j := 0; j < replicas; j++ {
|
||||||
|
createPod(t, fmt.Sprintf("pod-%d", j), nsName, map[string]string{"app": "test-crd"},
|
||||||
|
clientSet, []metav1.OwnerReference{})
|
||||||
|
}
|
||||||
|
|
||||||
|
waitToObservePods(t, informers.Core().V1().Pods().Informer(), 4, v1.PodRunning)
|
||||||
|
|
||||||
|
pdbName := "test-pdb"
|
||||||
|
if err := tc.createPDBFunc(clientSet, pdbName, nsName, minAvailable); err != nil {
|
||||||
|
t.Errorf("Error creating PodDisruptionBudget: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitPDBStable(t, clientSet, tc.expectedCurrentHealthy, nsName, pdbName)
|
||||||
|
|
||||||
|
newPdb, err := clientSet.PolicyV1().PodDisruptionBudgets(nsName).Get(context.TODO(), pdbName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error getting PodDisruptionBudget: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected, found := tc.expectedCurrentHealthy, newPdb.Status.CurrentHealthy; expected != found {
|
||||||
|
t.Errorf("Expected %d, but found %d", expected, found)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectorsForPodsWithoutLabels(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
createPDBFunc func(clientSet clientset.Interface, name, nsName string, minAvailable intstr.IntOrString) error
|
||||||
|
expectedCurrentHealthy int32
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "pods with no labels can be targeted by v1 PDBs with empty selector",
|
||||||
|
createPDBFunc: func(clientSet clientset.Interface, name, nsName string, minAvailable intstr.IntOrString) error {
|
||||||
|
pdb := &policyv1.PodDisruptionBudget{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: policyv1.PodDisruptionBudgetSpec{
|
||||||
|
MinAvailable: &minAvailable,
|
||||||
|
Selector: &metav1.LabelSelector{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := clientSet.PolicyV1().PodDisruptionBudgets(nsName).Create(context.TODO(), pdb, metav1.CreateOptions{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
expectedCurrentHealthy: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pods with no labels can be targeted by v1 PDBs with DoesNotExist selector",
|
||||||
|
createPDBFunc: func(clientSet clientset.Interface, name, nsName string, minAvailable intstr.IntOrString) error {
|
||||||
|
pdb := &policyv1.PodDisruptionBudget{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: policyv1.PodDisruptionBudgetSpec{
|
||||||
|
MinAvailable: &minAvailable,
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "DoesNotExist",
|
||||||
|
Operator: metav1.LabelSelectorOpDoesNotExist,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := clientSet.PolicyV1().PodDisruptionBudgets(nsName).Create(context.TODO(), pdb, metav1.CreateOptions{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
expectedCurrentHealthy: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pods with no labels can be targeted by v1beta1 PDBs with DoesNotExist selector",
|
||||||
|
createPDBFunc: func(clientSet clientset.Interface, name, nsName string, minAvailable intstr.IntOrString) error {
|
||||||
|
pdb := &v1beta1.PodDisruptionBudget{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: v1beta1.PodDisruptionBudgetSpec{
|
||||||
|
MinAvailable: &minAvailable,
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "DoesNotExist",
|
||||||
|
Operator: metav1.LabelSelectorOpDoesNotExist,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := clientSet.PolicyV1beta1().PodDisruptionBudgets(nsName).Create(context.TODO(), pdb, metav1.CreateOptions{})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
expectedCurrentHealthy: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
s, pdbc, informers, clientSet, _, _ := setup(t)
|
||||||
|
defer s.TearDownFn()
|
||||||
|
|
||||||
|
nsName := fmt.Sprintf("pdb-selectors-%d", i)
|
||||||
|
createNs(t, nsName, clientSet)
|
||||||
|
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
informers.Start(stopCh)
|
||||||
|
go pdbc.Run(stopCh)
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
minAvailable := intstr.FromInt(1)
|
||||||
|
|
||||||
|
// Create the PDB first and wait for it to settle.
|
||||||
|
pdbName := "test-pdb"
|
||||||
|
if err := tc.createPDBFunc(clientSet, pdbName, nsName, minAvailable); err != nil {
|
||||||
|
t.Errorf("Error creating PodDisruptionBudget: %v", err)
|
||||||
|
}
|
||||||
|
waitPDBStable(t, clientSet, 0, nsName, pdbName)
|
||||||
|
|
||||||
|
// Create a pod and wait for it be reach the running phase.
|
||||||
|
createPod(t,"pod", nsName, map[string]string{}, clientSet, []metav1.OwnerReference{})
|
||||||
|
waitToObservePods(t, informers.Core().V1().Pods().Informer(), 1, v1.PodRunning)
|
||||||
|
|
||||||
|
// Then verify that the added pod are picked up by the disruption controller.
|
||||||
|
waitPDBStable(t, clientSet, 1, nsName, pdbName)
|
||||||
|
|
||||||
|
newPdb, err := clientSet.PolicyV1().PodDisruptionBudgets(nsName).Get(context.TODO(), pdbName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error getting PodDisruptionBudget: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected, found := tc.expectedCurrentHealthy, newPdb.Status.CurrentHealthy; expected != found {
|
||||||
|
t.Errorf("Expected %d, but found %d", expected, found)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPod(t *testing.T, name, namespace string, labels map[string]string, clientSet clientset.Interface, ownerRefs []metav1.OwnerReference) {
|
||||||
pod := &v1.Pod{
|
pod := &v1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Labels: map[string]string{"app": labelValue},
|
Labels: labels,
|
||||||
OwnerReferences: []metav1.OwnerReference{
|
OwnerReferences: ownerRefs,
|
||||||
ownerRef,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Spec: v1.PodSpec{
|
Spec: v1.PodSpec{
|
||||||
Containers: []v1.Container{
|
Containers: []v1.Container{
|
||||||
@ -271,7 +469,7 @@ func waitPDBStable(t *testing.T, clientSet clientset.Interface, podNum int32, ns
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if pdb.Status.CurrentHealthy != podNum {
|
if pdb.Status.ObservedGeneration == 0 || pdb.Status.CurrentHealthy != podNum {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -265,6 +265,14 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
|
|||||||
},
|
},
|
||||||
// --
|
// --
|
||||||
|
|
||||||
|
// k8s.io/kubernetes/pkg/apis/policy/v1
|
||||||
|
gvr("policy", "v1", "poddisruptionbudgets"): {
|
||||||
|
Stub: `{"metadata": {"name": "pdbv1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`,
|
||||||
|
ExpectedEtcdPath: "/registry/poddisruptionbudgets/" + namespace + "/pdbv1",
|
||||||
|
ExpectedGVK: gvkP("policy", "v1beta1", "PodDisruptionBudget"),
|
||||||
|
},
|
||||||
|
// --
|
||||||
|
|
||||||
// k8s.io/kubernetes/pkg/apis/policy/v1beta1
|
// k8s.io/kubernetes/pkg/apis/policy/v1beta1
|
||||||
gvr("policy", "v1beta1", "poddisruptionbudgets"): {
|
gvr("policy", "v1beta1", "poddisruptionbudgets"): {
|
||||||
Stub: `{"metadata": {"name": "pdb1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`,
|
Stub: `{"metadata": {"name": "pdb1"}, "spec": {"selector": {"matchLabels": {"anokkey": "anokvalue"}}}}`,
|
||||||
|
@ -19,7 +19,6 @@ package evictions
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
@ -28,7 +27,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/api/policy/v1beta1"
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
|
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
@ -95,7 +95,7 @@ func TestConcurrentEvictionRequests(t *testing.T) {
|
|||||||
waitToObservePods(t, informers.Core().V1().Pods().Informer(), numOfEvictions, v1.PodRunning)
|
waitToObservePods(t, informers.Core().V1().Pods().Informer(), numOfEvictions, v1.PodRunning)
|
||||||
|
|
||||||
pdb := newPDB()
|
pdb := newPDB()
|
||||||
if _, err := clientSet.PolicyV1beta1().PodDisruptionBudgets(ns.Name).Create(context.TODO(), pdb, metav1.CreateOptions{}); err != nil {
|
if _, err := clientSet.PolicyV1().PodDisruptionBudgets(ns.Name).Create(context.TODO(), pdb, metav1.CreateOptions{}); err != nil {
|
||||||
t.Errorf("Failed to create PodDisruptionBudget: %v", err)
|
t.Errorf("Failed to create PodDisruptionBudget: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ func TestConcurrentEvictionRequests(t *testing.T) {
|
|||||||
go func(id int, errCh chan error) {
|
go func(id int, errCh chan error) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
podName := fmt.Sprintf(podNameFormat, id)
|
podName := fmt.Sprintf(podNameFormat, id)
|
||||||
eviction := newEviction(ns.Name, podName, deleteOption)
|
eviction := newV1beta1Eviction(ns.Name, podName, deleteOption)
|
||||||
|
|
||||||
err := wait.PollImmediate(5*time.Second, 60*time.Second, func() (bool, error) {
|
err := wait.PollImmediate(5*time.Second, 60*time.Second, func() (bool, error) {
|
||||||
e := clientSet.PolicyV1beta1().Evictions(ns.Name).Evict(context.TODO(), eviction)
|
e := clientSet.PolicyV1beta1().Evictions(ns.Name).Evict(context.TODO(), eviction)
|
||||||
@ -208,7 +208,7 @@ func TestTerminalPodEviction(t *testing.T) {
|
|||||||
waitToObservePods(t, informers.Core().V1().Pods().Informer(), 1, v1.PodSucceeded)
|
waitToObservePods(t, informers.Core().V1().Pods().Informer(), 1, v1.PodSucceeded)
|
||||||
|
|
||||||
pdb := newPDB()
|
pdb := newPDB()
|
||||||
if _, err := clientSet.PolicyV1beta1().PodDisruptionBudgets(ns.Name).Create(context.TODO(), pdb, metav1.CreateOptions{}); err != nil {
|
if _, err := clientSet.PolicyV1().PodDisruptionBudgets(ns.Name).Create(context.TODO(), pdb, metav1.CreateOptions{}); err != nil {
|
||||||
t.Errorf("Failed to create PodDisruptionBudget: %v", err)
|
t.Errorf("Failed to create PodDisruptionBudget: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ func TestTerminalPodEviction(t *testing.T) {
|
|||||||
t.Fatalf("Error while listing pod disruption budget")
|
t.Fatalf("Error while listing pod disruption budget")
|
||||||
}
|
}
|
||||||
oldPdb := pdbList.Items[0]
|
oldPdb := pdbList.Items[0]
|
||||||
eviction := newEviction(ns.Name, pod.Name, deleteOption)
|
eviction := newV1beta1Eviction(ns.Name, pod.Name, deleteOption)
|
||||||
err = wait.PollImmediate(5*time.Second, 60*time.Second, func() (bool, error) {
|
err = wait.PollImmediate(5*time.Second, 60*time.Second, func() (bool, error) {
|
||||||
e := clientSet.PolicyV1beta1().Evictions(ns.Name).Evict(context.TODO(), eviction)
|
e := clientSet.PolicyV1beta1().Evictions(ns.Name).Evict(context.TODO(), eviction)
|
||||||
switch {
|
switch {
|
||||||
@ -292,12 +292,12 @@ func addPodConditionReady(pod *v1.Pod) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPDB() *v1beta1.PodDisruptionBudget {
|
func newPDB() *policyv1.PodDisruptionBudget {
|
||||||
return &v1beta1.PodDisruptionBudget{
|
return &policyv1.PodDisruptionBudget{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-pdb",
|
Name: "test-pdb",
|
||||||
},
|
},
|
||||||
Spec: v1beta1.PodDisruptionBudgetSpec{
|
Spec: policyv1.PodDisruptionBudgetSpec{
|
||||||
MinAvailable: &intstr.IntOrString{
|
MinAvailable: &intstr.IntOrString{
|
||||||
Type: intstr.Int,
|
Type: intstr.Int,
|
||||||
IntVal: 0,
|
IntVal: 0,
|
||||||
@ -309,8 +309,8 @@ func newPDB() *v1beta1.PodDisruptionBudget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newEviction(ns, evictionName string, deleteOption metav1.DeleteOptions) *v1beta1.Eviction {
|
func newV1beta1Eviction(ns, evictionName string, deleteOption metav1.DeleteOptions) *policyv1beta1.Eviction {
|
||||||
return &v1beta1.Eviction{
|
return &policyv1beta1.Eviction{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
APIVersion: "Policy/v1beta1",
|
APIVersion: "Policy/v1beta1",
|
||||||
Kind: "Eviction",
|
Kind: "Eviction",
|
||||||
@ -348,7 +348,7 @@ func rmSetup(t *testing.T) (*httptest.Server, framework.CloseFunc, *disruption.D
|
|||||||
|
|
||||||
rm := disruption.NewDisruptionController(
|
rm := disruption.NewDisruptionController(
|
||||||
informers.Core().V1().Pods(),
|
informers.Core().V1().Pods(),
|
||||||
informers.Policy().V1beta1().PodDisruptionBudgets(),
|
informers.Policy().V1().PodDisruptionBudgets(),
|
||||||
informers.Core().V1().ReplicationControllers(),
|
informers.Core().V1().ReplicationControllers(),
|
||||||
informers.Apps().V1().ReplicaSets(),
|
informers.Apps().V1().ReplicaSets(),
|
||||||
informers.Apps().V1().Deployments(),
|
informers.Apps().V1().Deployments(),
|
||||||
|
@ -62,7 +62,7 @@ func initDisruptionController(t *testing.T, testCtx *testutils.TestContext) *dis
|
|||||||
|
|
||||||
dc := disruption.NewDisruptionController(
|
dc := disruption.NewDisruptionController(
|
||||||
informers.Core().V1().Pods(),
|
informers.Core().V1().Pods(),
|
||||||
informers.Policy().V1beta1().PodDisruptionBudgets(),
|
informers.Policy().V1().PodDisruptionBudgets(),
|
||||||
informers.Core().V1().ReplicationControllers(),
|
informers.Core().V1().ReplicationControllers(),
|
||||||
informers.Apps().V1().ReplicaSets(),
|
informers.Apps().V1().ReplicaSets(),
|
||||||
informers.Apps().V1().Deployments(),
|
informers.Apps().V1().Deployments(),
|
||||||
|
Loading…
Reference in New Issue
Block a user