mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
allow evictions subresource to accept policy/v1 and policy/v1beta1
This commit is contained in:
parent
e22cd7dbc4
commit
33ad842480
@ -23,6 +23,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
policyv1 "k8s.io/api/policy/v1"
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
|
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
||||||
"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"
|
||||||
@ -70,10 +71,23 @@ type EvictionREST struct {
|
|||||||
|
|
||||||
var _ = rest.NamedCreater(&EvictionREST{})
|
var _ = rest.NamedCreater(&EvictionREST{})
|
||||||
var _ = rest.GroupVersionKindProvider(&EvictionREST{})
|
var _ = rest.GroupVersionKindProvider(&EvictionREST{})
|
||||||
|
var _ = rest.GroupVersionAcceptor(&EvictionREST{})
|
||||||
|
|
||||||
|
var v1Eviction = schema.GroupVersionKind{Group: "policy", Version: "v1", Kind: "Eviction"}
|
||||||
|
|
||||||
// GroupVersionKind specifies a particular GroupVersionKind to discovery
|
// GroupVersionKind specifies a particular GroupVersionKind to discovery
|
||||||
func (r *EvictionREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
func (r *EvictionREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
||||||
return schema.GroupVersionKind{Group: "policy", Version: "v1", Kind: "Eviction"}
|
return v1Eviction
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcceptsGroupVersion indicates both v1 and v1beta1 Eviction objects are acceptable
|
||||||
|
func (r *EvictionREST) AcceptsGroupVersion(gv schema.GroupVersion) bool {
|
||||||
|
switch gv {
|
||||||
|
case policyv1.SchemeGroupVersion, policyv1beta1.SchemeGroupVersion:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new eviction resource
|
// New creates a new eviction resource
|
||||||
|
@ -313,7 +313,7 @@ func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter generi
|
|||||||
if legacyscheme.Scheme.IsVersionRegistered(schema.GroupVersion{Group: "autoscaling", Version: "v1"}) {
|
if legacyscheme.Scheme.IsVersionRegistered(schema.GroupVersion{Group: "autoscaling", Version: "v1"}) {
|
||||||
restStorageMap["replicationControllers/scale"] = controllerStorage.Scale
|
restStorageMap["replicationControllers/scale"] = controllerStorage.Scale
|
||||||
}
|
}
|
||||||
if legacyscheme.Scheme.IsVersionRegistered(schema.GroupVersion{Group: "policy", Version: "v1beta1"}) {
|
if podStorage.Eviction != nil {
|
||||||
restStorageMap["pods/eviction"] = podStorage.Eviction
|
restStorageMap["pods/eviction"] = podStorage.Eviction
|
||||||
}
|
}
|
||||||
if serviceAccountStorage.Token != nil {
|
if serviceAccountStorage.Token != nil {
|
||||||
|
@ -123,7 +123,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
|||||||
scope.err(err, w, req)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if gvk.GroupVersion() != gv {
|
if !scope.AcceptsGroupVersion(gvk.GroupVersion()) {
|
||||||
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)", gvk.GroupVersion().String(), gv.String()))
|
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)", gvk.GroupVersion().String(), gv.String()))
|
||||||
scope.err(err, w, req)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
|
@ -91,6 +91,12 @@ type RequestScope struct {
|
|||||||
|
|
||||||
Resource schema.GroupVersionResource
|
Resource schema.GroupVersionResource
|
||||||
Kind schema.GroupVersionKind
|
Kind schema.GroupVersionKind
|
||||||
|
|
||||||
|
// AcceptsGroupVersionDelegate is an optional delegate that can be queried about whether a given GVK
|
||||||
|
// can be accepted in create or update requests. If nil, only scope.Kind is accepted.
|
||||||
|
// Note that this does not enable multi-version support for reads from a single endpoint.
|
||||||
|
AcceptsGroupVersionDelegate rest.GroupVersionAcceptor
|
||||||
|
|
||||||
Subresource string
|
Subresource string
|
||||||
|
|
||||||
MetaGroupVersion schema.GroupVersion
|
MetaGroupVersion schema.GroupVersion
|
||||||
@ -105,6 +111,17 @@ func (scope *RequestScope) err(err error, w http.ResponseWriter, req *http.Reque
|
|||||||
responsewriters.ErrorNegotiated(err, scope.Serializer, scope.Kind.GroupVersion(), w, req)
|
responsewriters.ErrorNegotiated(err, scope.Serializer, scope.Kind.GroupVersion(), w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AcceptsGroupVersion returns true if the specified GroupVersion is allowed
|
||||||
|
// in create and update requests.
|
||||||
|
func (scope *RequestScope) AcceptsGroupVersion(gv schema.GroupVersion) bool {
|
||||||
|
// If there's a custom acceptor, delegate to it. This is extremely rare.
|
||||||
|
if scope.AcceptsGroupVersionDelegate != nil {
|
||||||
|
return scope.AcceptsGroupVersionDelegate.AcceptsGroupVersion(gv)
|
||||||
|
}
|
||||||
|
// Fall back to only allowing the singular Kind. This is the typical behavior.
|
||||||
|
return gv == scope.Kind.GroupVersion()
|
||||||
|
}
|
||||||
|
|
||||||
func (scope *RequestScope) AllowsMediaTypeTransform(mimeType, mimeSubType string, gvk *schema.GroupVersionKind) bool {
|
func (scope *RequestScope) AllowsMediaTypeTransform(mimeType, mimeSubType string, gvk *schema.GroupVersionKind) bool {
|
||||||
// some handlers like CRDs can't serve all the mime types that PartialObjectMetadata or Table can - if
|
// some handlers like CRDs can't serve all the mime types that PartialObjectMetadata or Table can - if
|
||||||
// gvk is nil (no conversion) allow StandardSerializers to further restrict the set of mime types.
|
// gvk is nil (no conversion) allow StandardSerializers to further restrict the set of mime types.
|
||||||
|
@ -110,7 +110,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
|
|||||||
scope.err(err, w, req)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if gvk.GroupVersion() != defaultGVK.GroupVersion() {
|
if !scope.AcceptsGroupVersion(gvk.GroupVersion()) {
|
||||||
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%s)", gvk.GroupVersion(), defaultGVK.GroupVersion()))
|
err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%s)", gvk.GroupVersion(), defaultGVK.GroupVersion()))
|
||||||
scope.err(err, w, req)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
|
@ -251,6 +251,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
connecter, isConnecter := storage.(rest.Connecter)
|
connecter, isConnecter := storage.(rest.Connecter)
|
||||||
storageMeta, isMetadata := storage.(rest.StorageMetadata)
|
storageMeta, isMetadata := storage.(rest.StorageMetadata)
|
||||||
storageVersionProvider, isStorageVersionProvider := storage.(rest.StorageVersionProvider)
|
storageVersionProvider, isStorageVersionProvider := storage.(rest.StorageVersionProvider)
|
||||||
|
gvAcceptor, _ := storage.(rest.GroupVersionAcceptor)
|
||||||
if !isMetadata {
|
if !isMetadata {
|
||||||
storageMeta = defaultStorageMetadata{}
|
storageMeta = defaultStorageMetadata{}
|
||||||
}
|
}
|
||||||
@ -587,6 +588,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
Subresource: subresource,
|
Subresource: subresource,
|
||||||
Kind: fqKindToRegister,
|
Kind: fqKindToRegister,
|
||||||
|
|
||||||
|
AcceptsGroupVersionDelegate: gvAcceptor,
|
||||||
|
|
||||||
HubGroupVersion: schema.GroupVersion{Group: fqKindToRegister.Group, Version: runtime.APIVersionInternal},
|
HubGroupVersion: schema.GroupVersion{Group: fqKindToRegister.Group, Version: runtime.APIVersionInternal},
|
||||||
|
|
||||||
MetaGroupVersion: metav1.SchemeGroupVersion,
|
MetaGroupVersion: metav1.SchemeGroupVersion,
|
||||||
|
@ -92,6 +92,13 @@ type GroupVersionKindProvider interface {
|
|||||||
GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind
|
GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GroupVersionAcceptor is used to determine if a particular GroupVersion is acceptable to send to an endpoint.
|
||||||
|
// This is used for endpoints which accept multiple versions (which is extremely rare).
|
||||||
|
// The only known instance is pods/evictions which accepts policy/v1, but also policy/v1beta1 for backwards compatibility.
|
||||||
|
type GroupVersionAcceptor interface {
|
||||||
|
AcceptsGroupVersion(gv schema.GroupVersion) bool
|
||||||
|
}
|
||||||
|
|
||||||
// Lister is an object that can retrieve resources that match the provided field and label criteria.
|
// Lister is an object that can retrieve resources that match the provided field and label criteria.
|
||||||
type Lister interface {
|
type Lister interface {
|
||||||
// NewList returns an empty object that can be used with the List call.
|
// NewList returns an empty object that can be used with the List call.
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
policyv1 "k8s.io/api/policy/v1"
|
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"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
@ -285,7 +284,7 @@ var _ = SIGDescribe("DisruptionController", func() {
|
|||||||
pod, err := locateRunningPod(cs, ns)
|
pod, err := locateRunningPod(cs, ns)
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
e := &policyv1beta1.Eviction{
|
e := &policyv1.Eviction{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: pod.Name,
|
Name: pod.Name,
|
||||||
Namespace: ns,
|
Namespace: ns,
|
||||||
@ -293,7 +292,7 @@ var _ = SIGDescribe("DisruptionController", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.shouldDeny {
|
if c.shouldDeny {
|
||||||
err = cs.CoreV1().Pods(ns).Evict(context.TODO(), e)
|
err = cs.CoreV1().Pods(ns).EvictV1(context.TODO(), e)
|
||||||
gomega.Expect(err).Should(gomega.MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
|
gomega.Expect(err).Should(gomega.MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
|
||||||
} else {
|
} else {
|
||||||
// Only wait for running pods in the "allow" case
|
// Only wait for running pods in the "allow" case
|
||||||
@ -304,7 +303,7 @@ var _ = SIGDescribe("DisruptionController", func() {
|
|||||||
// Since disruptionAllowed starts out false, if an eviction is ever allowed,
|
// Since disruptionAllowed starts out false, if an eviction is ever allowed,
|
||||||
// that means the controller is working.
|
// that means the controller is working.
|
||||||
err = wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
|
err = wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
|
||||||
err = cs.CoreV1().Pods(ns).Evict(context.TODO(), e)
|
err = cs.CoreV1().Pods(ns).EvictV1(context.TODO(), e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@ -325,13 +324,13 @@ var _ = SIGDescribe("DisruptionController", func() {
|
|||||||
|
|
||||||
pod, err := locateRunningPod(cs, ns)
|
pod, err := locateRunningPod(cs, ns)
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
e := &policyv1beta1.Eviction{
|
e := &policyv1.Eviction{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: pod.Name,
|
Name: pod.Name,
|
||||||
Namespace: ns,
|
Namespace: ns,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = cs.CoreV1().Pods(ns).Evict(context.TODO(), e)
|
err = cs.CoreV1().Pods(ns).EvictV1(context.TODO(), e)
|
||||||
gomega.Expect(err).Should(gomega.MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
|
gomega.Expect(err).Should(gomega.MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
|
||||||
|
|
||||||
ginkgo.By("Updating the pdb to allow a pod to be evicted")
|
ginkgo.By("Updating the pdb to allow a pod to be evicted")
|
||||||
@ -344,7 +343,7 @@ var _ = SIGDescribe("DisruptionController", func() {
|
|||||||
ginkgo.By("Trying to evict the same pod we tried earlier which should now be evictable")
|
ginkgo.By("Trying to evict the same pod we tried earlier which should now be evictable")
|
||||||
waitForPodsOrDie(cs, ns, 3)
|
waitForPodsOrDie(cs, ns, 3)
|
||||||
waitForPdbToObserveHealthyPods(cs, ns, 3)
|
waitForPdbToObserveHealthyPods(cs, ns, 3)
|
||||||
err = cs.CoreV1().Pods(ns).Evict(context.TODO(), e)
|
err = cs.CoreV1().Pods(ns).EvictV1(context.TODO(), e)
|
||||||
framework.ExpectNoError(err) // the eviction is now allowed
|
framework.ExpectNoError(err) // the eviction is now allowed
|
||||||
|
|
||||||
ginkgo.By("Patching the pdb to disallow a pod to be evicted")
|
ginkgo.By("Patching the pdb to disallow a pod to be evicted")
|
||||||
@ -362,13 +361,13 @@ var _ = SIGDescribe("DisruptionController", func() {
|
|||||||
waitForPodsOrDie(cs, ns, 3)
|
waitForPodsOrDie(cs, ns, 3)
|
||||||
pod, err = locateRunningPod(cs, ns) // locate a new running pod
|
pod, err = locateRunningPod(cs, ns) // locate a new running pod
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
e = &policyv1beta1.Eviction{
|
e = &policyv1.Eviction{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: pod.Name,
|
Name: pod.Name,
|
||||||
Namespace: ns,
|
Namespace: ns,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = cs.CoreV1().Pods(ns).Evict(context.TODO(), e)
|
err = cs.CoreV1().Pods(ns).EvictV1(context.TODO(), e)
|
||||||
gomega.Expect(err).Should(gomega.MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
|
gomega.Expect(err).Should(gomega.MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
|
||||||
|
|
||||||
ginkgo.By("Deleting the pdb to allow a pod to be evicted")
|
ginkgo.By("Deleting the pdb to allow a pod to be evicted")
|
||||||
@ -376,7 +375,7 @@ var _ = SIGDescribe("DisruptionController", func() {
|
|||||||
|
|
||||||
ginkgo.By("Trying to evict the same pod we tried earlier which should now be evictable")
|
ginkgo.By("Trying to evict the same pod we tried earlier which should now be evictable")
|
||||||
waitForPodsOrDie(cs, ns, 3)
|
waitForPodsOrDie(cs, ns, 3)
|
||||||
err = cs.CoreV1().Pods(ns).Evict(context.TODO(), e)
|
err = cs.CoreV1().Pods(ns).EvictV1(context.TODO(), e)
|
||||||
framework.ExpectNoError(err) // the eviction is now allowed
|
framework.ExpectNoError(err) // the eviction is now allowed
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/onsi/ginkgo"
|
"github.com/onsi/ginkgo"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
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"
|
||||||
@ -423,7 +423,7 @@ var _ = utils.SIGDescribe("Pod Disks", func() {
|
|||||||
framework.ExpectNoError(podClient.Delete(context.TODO(), host0Pod.Name, *metav1.NewDeleteOptions(0)), "Unable to delete host0Pod")
|
framework.ExpectNoError(podClient.Delete(context.TODO(), host0Pod.Name, *metav1.NewDeleteOptions(0)), "Unable to delete host0Pod")
|
||||||
|
|
||||||
} else if disruptOp == evictPod {
|
} else if disruptOp == evictPod {
|
||||||
evictTarget := &policyv1beta1.Eviction{
|
evictTarget := &policyv1.Eviction{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: host0Pod.Name,
|
Name: host0Pod.Name,
|
||||||
Namespace: ns,
|
Namespace: ns,
|
||||||
@ -431,7 +431,7 @@ var _ = utils.SIGDescribe("Pod Disks", func() {
|
|||||||
}
|
}
|
||||||
ginkgo.By("evicting host0Pod")
|
ginkgo.By("evicting host0Pod")
|
||||||
err = wait.PollImmediate(framework.Poll, podEvictTimeout, func() (bool, error) {
|
err = wait.PollImmediate(framework.Poll, podEvictTimeout, func() (bool, error) {
|
||||||
if err := cs.CoreV1().Pods(ns).Evict(context.TODO(), evictTarget); err != nil {
|
if err := cs.CoreV1().Pods(ns).EvictV1(context.TODO(), evictTarget); err != nil {
|
||||||
framework.Logf("Failed to evict host0Pod, ignoring error: %v", err)
|
framework.Logf("Failed to evict host0Pod, ignoring error: %v", err)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
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"
|
||||||
@ -1128,7 +1128,7 @@ func testPodBindingEviction(c *testContext) {
|
|||||||
}).Do(context.TODO()).Error()
|
}).Do(context.TODO()).Error()
|
||||||
|
|
||||||
case gvr("", "v1", "pods/eviction"):
|
case gvr("", "v1", "pods/eviction"):
|
||||||
err = c.clientset.CoreV1().RESTClient().Post().Namespace(pod.GetNamespace()).Resource("pods").Name(pod.GetName()).SubResource("eviction").Body(&policyv1beta1.Eviction{
|
err = c.clientset.CoreV1().RESTClient().Post().Namespace(pod.GetNamespace()).Resource("pods").Name(pod.GetName()).SubResource("eviction").Body(&policyv1.Eviction{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: pod.GetName()},
|
ObjectMeta: metav1.ObjectMeta{Name: pod.GetName()},
|
||||||
DeleteOptions: &forceDelete,
|
DeleteOptions: &forceDelete,
|
||||||
}).Do(context.TODO()).Error()
|
}).Do(context.TODO()).Error()
|
||||||
|
@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
coordination "k8s.io/api/coordination/v1"
|
coordination "k8s.io/api/coordination/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
policy "k8s.io/api/policy/v1beta1"
|
policy "k8s.io/api/policy/v1"
|
||||||
storagev1 "k8s.io/api/storage/v1"
|
storagev1 "k8s.io/api/storage/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
@ -305,9 +305,9 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
createNode2NormalPodEviction := func(client clientset.Interface) func() error {
|
createNode2NormalPodEviction := func(client clientset.Interface) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
zero := int64(0)
|
zero := int64(0)
|
||||||
return client.PolicyV1beta1().Evictions("ns").Evict(context.TODO(), &policy.Eviction{
|
return client.PolicyV1().Evictions("ns").Evict(context.TODO(), &policy.Eviction{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
APIVersion: "policy/v1beta1",
|
APIVersion: "policy/v1",
|
||||||
Kind: "Eviction",
|
Kind: "Eviction",
|
||||||
},
|
},
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -321,9 +321,9 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
createNode2MirrorPodEviction := func(client clientset.Interface) func() error {
|
createNode2MirrorPodEviction := func(client clientset.Interface) func() error {
|
||||||
return func() error {
|
return func() error {
|
||||||
zero := int64(0)
|
zero := int64(0)
|
||||||
return client.PolicyV1beta1().Evictions("ns").Evict(context.TODO(), &policy.Eviction{
|
return client.PolicyV1().Evictions("ns").Evict(context.TODO(), &policy.Eviction{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
APIVersion: "policy/v1beta1",
|
APIVersion: "policy/v1",
|
||||||
Kind: "Eviction",
|
Kind: "Eviction",
|
||||||
},
|
},
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
@ -18,9 +18,11 @@ package evictions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
@ -31,6 +33,10 @@ import (
|
|||||||
policyv1beta1 "k8s.io/api/policy/v1beta1"
|
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"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
@ -110,10 +116,10 @@ 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 := newV1beta1Eviction(ns.Name, podName, deleteOption)
|
eviction := newV1Eviction(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.PolicyV1().Evictions(ns.Name).Evict(context.TODO(), eviction)
|
||||||
switch {
|
switch {
|
||||||
case apierrors.IsTooManyRequests(e):
|
case apierrors.IsTooManyRequests(e):
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -219,9 +225,9 @@ 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 := newV1beta1Eviction(ns.Name, pod.Name, deleteOption)
|
eviction := newV1Eviction(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.PolicyV1().Evictions(ns.Name).Evict(context.TODO(), eviction)
|
||||||
switch {
|
switch {
|
||||||
case apierrors.IsTooManyRequests(e):
|
case apierrors.IsTooManyRequests(e):
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -251,6 +257,97 @@ func TestTerminalPodEviction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestEvictionVersions ensures the eviction endpoint accepts and returns the correct API versions
|
||||||
|
func TestEvictionVersions(t *testing.T) {
|
||||||
|
s, closeFn, rm, informers, clientSet := rmSetup(t)
|
||||||
|
defer closeFn()
|
||||||
|
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
informers.Start(stopCh)
|
||||||
|
go rm.Run(stopCh)
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
config := restclient.Config{Host: s.URL}
|
||||||
|
|
||||||
|
ns := "default"
|
||||||
|
subresource := "eviction"
|
||||||
|
pod := newPod("test")
|
||||||
|
if _, err := clientSet.CoreV1().Pods(ns).Create(context.TODO(), pod, metav1.CreateOptions{}); err != nil {
|
||||||
|
t.Errorf("Failed to create pod: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicClient, err := dynamic.NewForConfig(&config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create clientset: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
podClient := dynamicClient.Resource(schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}).Namespace(ns)
|
||||||
|
|
||||||
|
// get should not be supported
|
||||||
|
if _, err := podClient.Get(context.TODO(), pod.Name, metav1.GetOptions{}, subresource); !apierrors.IsMethodNotSupported(err) {
|
||||||
|
t.Fatalf("expected MethodNotSupported for GET, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// patch should not be supported
|
||||||
|
for _, patchType := range []types.PatchType{types.JSONPatchType, types.MergePatchType, types.StrategicMergePatchType, types.ApplyPatchType} {
|
||||||
|
if _, err := podClient.Patch(context.TODO(), pod.Name, patchType, []byte{}, metav1.PatchOptions{}, subresource); !apierrors.IsMethodNotSupported(err) {
|
||||||
|
t.Fatalf("expected MethodNotSupported for GET, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedEvictions := []runtime.Object{
|
||||||
|
// v1beta1, no apiVersion/kind
|
||||||
|
&policyv1beta1.Eviction{
|
||||||
|
TypeMeta: metav1.TypeMeta{},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: pod.Name},
|
||||||
|
DeleteOptions: &metav1.DeleteOptions{DryRun: []string{metav1.DryRunAll}},
|
||||||
|
},
|
||||||
|
// v1beta1, apiVersion/kind
|
||||||
|
&policyv1beta1.Eviction{
|
||||||
|
TypeMeta: metav1.TypeMeta{APIVersion: "policy/v1beta1", Kind: "Eviction"},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: pod.Name},
|
||||||
|
DeleteOptions: &metav1.DeleteOptions{DryRun: []string{metav1.DryRunAll}},
|
||||||
|
},
|
||||||
|
// v1, no apiVersion/kind
|
||||||
|
&policyv1.Eviction{
|
||||||
|
TypeMeta: metav1.TypeMeta{},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: pod.Name},
|
||||||
|
DeleteOptions: &metav1.DeleteOptions{DryRun: []string{metav1.DryRunAll}},
|
||||||
|
},
|
||||||
|
// v1, apiVersion/kind
|
||||||
|
&policyv1.Eviction{
|
||||||
|
TypeMeta: metav1.TypeMeta{APIVersion: "policy/v1", Kind: "Eviction"},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: pod.Name},
|
||||||
|
DeleteOptions: &metav1.DeleteOptions{DryRun: []string{metav1.DryRunAll}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
v1Status := schema.GroupVersionKind{Version: "v1", Kind: "Status"}
|
||||||
|
for _, allowedEviction := range allowedEvictions {
|
||||||
|
data, _ := json.Marshal(allowedEviction)
|
||||||
|
u := &unstructured.Unstructured{}
|
||||||
|
json.Unmarshal(data, u)
|
||||||
|
result, err := podClient.Create(context.TODO(), u, metav1.CreateOptions{}, subresource)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error posting %s: %v", string(data), err)
|
||||||
|
}
|
||||||
|
if result.GroupVersionKind() != v1Status {
|
||||||
|
t.Fatalf("expected v1 Status, got %#v", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create unknown eviction version with apiVersion/kind should fail
|
||||||
|
u := &unstructured.Unstructured{Object: map[string]interface{}{
|
||||||
|
"metadata": map[string]interface{}{"name": pod.Name},
|
||||||
|
"apiVersion": "policy/v2",
|
||||||
|
"kind": "Eviction",
|
||||||
|
}}
|
||||||
|
if _, err := podClient.Create(context.TODO(), u, metav1.CreateOptions{}, subresource); err == nil {
|
||||||
|
t.Fatal("expected error posting unknown Eviction version, got none")
|
||||||
|
} else if !strings.Contains(err.Error(), "policy/v2") {
|
||||||
|
t.Fatalf("expected error about policy/v2, got %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newPod(podName string) *v1.Pod {
|
func newPod(podName string) *v1.Pod {
|
||||||
return &v1.Pod{
|
return &v1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -309,10 +406,10 @@ func newPDB() *policyv1.PodDisruptionBudget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newV1beta1Eviction(ns, evictionName string, deleteOption metav1.DeleteOptions) *policyv1beta1.Eviction {
|
func newV1Eviction(ns, evictionName string, deleteOption metav1.DeleteOptions) *policyv1.Eviction {
|
||||||
return &policyv1beta1.Eviction{
|
return &policyv1.Eviction{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
APIVersion: "Policy/v1beta1",
|
APIVersion: "policy/v1",
|
||||||
Kind: "Eviction",
|
Kind: "Eviction",
|
||||||
},
|
},
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Loading…
Reference in New Issue
Block a user