Merge pull request #100724 from liggitt/eviction-v1beta1

Add policy/v1 Eviction support
This commit is contained in:
Kubernetes Prow Robot 2021-04-16 10:02:22 -07:00 committed by GitHub
commit 27a625cf89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1378 additions and 200 deletions

View File

@ -13595,6 +13595,35 @@
},
"type": "object"
},
"io.k8s.api.policy.v1.Eviction": {
"description": "Eviction evicts a pod from its node subject to certain policies and safety constraints. This is a subresource of Pod. A request to cause such an eviction is created by POSTing to .../pods/<pod name>/evictions.",
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"deleteOptions": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions",
"description": "DeleteOptions may be provided"
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"metadata": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta",
"description": "ObjectMeta describes the pod that is being evicted."
}
},
"type": "object",
"x-kubernetes-group-version-kind": [
{
"group": "policy",
"kind": "Eviction",
"version": "v1"
}
]
},
"io.k8s.api.policy.v1.PodDisruptionBudget": {
"description": "PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods",
"properties": {
@ -13779,35 +13808,6 @@
},
"type": "object"
},
"io.k8s.api.policy.v1beta1.Eviction": {
"description": "Eviction evicts a pod from its node subject to certain policies and safety constraints. This is a subresource of Pod. A request to cause such an eviction is created by POSTing to .../pods/<pod name>/evictions.",
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"deleteOptions": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions",
"description": "DeleteOptions may be provided"
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"metadata": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta",
"description": "ObjectMeta describes the pod that is being evicted."
}
},
"type": "object",
"x-kubernetes-group-version-kind": [
{
"group": "policy",
"kind": "Eviction",
"version": "v1beta1"
}
]
},
"io.k8s.api.policy.v1beta1.FSGroupStrategyOptions": {
"description": "FSGroupStrategyOptions defines the strategy type and options used to create the strategy.",
"properties": {
@ -25130,7 +25130,7 @@
"name": "body",
"required": true,
"schema": {
"$ref": "#/definitions/io.k8s.api.policy.v1beta1.Eviction"
"$ref": "#/definitions/io.k8s.api.policy.v1.Eviction"
}
}
],
@ -25143,19 +25143,19 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.policy.v1beta1.Eviction"
"$ref": "#/definitions/io.k8s.api.policy.v1.Eviction"
}
},
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/io.k8s.api.policy.v1beta1.Eviction"
"$ref": "#/definitions/io.k8s.api.policy.v1.Eviction"
}
},
"202": {
"description": "Accepted",
"schema": {
"$ref": "#/definitions/io.k8s.api.policy.v1beta1.Eviction"
"$ref": "#/definitions/io.k8s.api.policy.v1.Eviction"
}
},
"401": {
@ -25172,7 +25172,7 @@
"x-kubernetes-group-version-kind": {
"group": "policy",
"kind": "Eviction",
"version": "v1beta1"
"version": "v1"
}
}
},

View File

@ -38,6 +38,16 @@ func init() {
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*v1.Eviction)(nil), (*policy.Eviction)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_Eviction_To_policy_Eviction(a.(*v1.Eviction), b.(*policy.Eviction), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*policy.Eviction)(nil), (*v1.Eviction)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_policy_Eviction_To_v1_Eviction(a.(*policy.Eviction), b.(*v1.Eviction), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*policy.PodDisruptionBudget)(nil), (*v1.PodDisruptionBudget)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_policy_PodDisruptionBudget_To_v1_PodDisruptionBudget(a.(*policy.PodDisruptionBudget), b.(*v1.PodDisruptionBudget), scope)
}); err != nil {
@ -81,6 +91,28 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil
}
func autoConvert_v1_Eviction_To_policy_Eviction(in *v1.Eviction, out *policy.Eviction, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.DeleteOptions = (*metav1.DeleteOptions)(unsafe.Pointer(in.DeleteOptions))
return nil
}
// Convert_v1_Eviction_To_policy_Eviction is an autogenerated conversion function.
func Convert_v1_Eviction_To_policy_Eviction(in *v1.Eviction, out *policy.Eviction, s conversion.Scope) error {
return autoConvert_v1_Eviction_To_policy_Eviction(in, out, s)
}
func autoConvert_policy_Eviction_To_v1_Eviction(in *policy.Eviction, out *v1.Eviction, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.DeleteOptions = (*metav1.DeleteOptions)(unsafe.Pointer(in.DeleteOptions))
return nil
}
// Convert_policy_Eviction_To_v1_Eviction is an autogenerated conversion function.
func Convert_policy_Eviction_To_v1_Eviction(in *policy.Eviction, out *v1.Eviction, s conversion.Scope) error {
return autoConvert_policy_Eviction_To_v1_Eviction(in, out, s)
}
func autoConvert_v1_PodDisruptionBudget_To_policy_PodDisruptionBudget(in *v1.PodDisruptionBudget, out *policy.PodDisruptionBudget, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1_PodDisruptionBudgetSpec_To_policy_PodDisruptionBudgetSpec(&in.Spec, &out.Spec, s); err != nil {

View File

@ -23,6 +23,7 @@ import (
"time"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@ -70,10 +71,23 @@ type EvictionREST struct {
var _ = rest.NamedCreater(&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
func (r *EvictionREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
return schema.GroupVersionKind{Group: "policy", Version: "v1beta1", 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
@ -309,7 +323,7 @@ func createTooManyRequestsError(name string) error {
// even without that, we can give a suggestion (even if small) that
// prevents well-behaved clients from hammering us.
err := errors.NewTooManyRequests("Cannot evict pod as it would violate the pod's disruption budget.", 10)
err.ErrStatus.Details.Causes = append(err.ErrStatus.Details.Causes, metav1.StatusCause{Type: "DisruptionBudget", Message: fmt.Sprintf("The disruption budget %s is still being processed by the server.", name)})
err.ErrStatus.Details.Causes = append(err.ErrStatus.Details.Causes, metav1.StatusCause{Type: policyv1.DisruptionBudgetCause, Message: fmt.Sprintf("The disruption budget %s is still being processed by the server.", name)})
return err
}
@ -327,7 +341,7 @@ func (r *EvictionREST) checkAndDecrement(namespace string, podName string, pdb p
}
if pdb.Status.DisruptionsAllowed == 0 {
err := errors.NewTooManyRequests("Cannot evict pod as it would violate the pod's disruption budget.", 0)
err.ErrStatus.Details.Causes = append(err.ErrStatus.Details.Causes, metav1.StatusCause{Type: "DisruptionBudget", Message: fmt.Sprintf("The disruption budget %s needs %d healthy pods and has %d currently", pdb.Name, pdb.Status.DesiredHealthy, pdb.Status.CurrentHealthy)})
err.ErrStatus.Details.Causes = append(err.ErrStatus.Details.Causes, metav1.StatusCause{Type: policyv1.DisruptionBudgetCause, Message: fmt.Sprintf("The disruption budget %s needs %d healthy pods and has %d currently", pdb.Name, pdb.Status.DesiredHealthy, pdb.Status.CurrentHealthy)})
return err
}

View File

@ -313,7 +313,7 @@ func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter generi
if legacyscheme.Scheme.IsVersionRegistered(schema.GroupVersion{Group: "autoscaling", Version: "v1"}) {
restStorageMap["replicationControllers/scale"] = controllerStorage.Scale
}
if legacyscheme.Scheme.IsVersionRegistered(schema.GroupVersion{Group: "policy", Version: "v1beta1"}) {
if podStorage.Eviction != nil {
restStorageMap["pods/eviction"] = podStorage.Eviction
}
if serviceAccountStorage.Token != nil {

View File

@ -47,10 +47,38 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func (m *Eviction) Reset() { *m = Eviction{} }
func (*Eviction) ProtoMessage() {}
func (*Eviction) Descriptor() ([]byte, []int) {
return fileDescriptor_2d50488813b2d18e, []int{0}
}
func (m *Eviction) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Eviction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
func (m *Eviction) XXX_Merge(src proto.Message) {
xxx_messageInfo_Eviction.Merge(m, src)
}
func (m *Eviction) XXX_Size() int {
return m.Size()
}
func (m *Eviction) XXX_DiscardUnknown() {
xxx_messageInfo_Eviction.DiscardUnknown(m)
}
var xxx_messageInfo_Eviction proto.InternalMessageInfo
func (m *PodDisruptionBudget) Reset() { *m = PodDisruptionBudget{} }
func (*PodDisruptionBudget) ProtoMessage() {}
func (*PodDisruptionBudget) Descriptor() ([]byte, []int) {
return fileDescriptor_2d50488813b2d18e, []int{0}
return fileDescriptor_2d50488813b2d18e, []int{1}
}
func (m *PodDisruptionBudget) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -78,7 +106,7 @@ var xxx_messageInfo_PodDisruptionBudget proto.InternalMessageInfo
func (m *PodDisruptionBudgetList) Reset() { *m = PodDisruptionBudgetList{} }
func (*PodDisruptionBudgetList) ProtoMessage() {}
func (*PodDisruptionBudgetList) Descriptor() ([]byte, []int) {
return fileDescriptor_2d50488813b2d18e, []int{1}
return fileDescriptor_2d50488813b2d18e, []int{2}
}
func (m *PodDisruptionBudgetList) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -106,7 +134,7 @@ var xxx_messageInfo_PodDisruptionBudgetList proto.InternalMessageInfo
func (m *PodDisruptionBudgetSpec) Reset() { *m = PodDisruptionBudgetSpec{} }
func (*PodDisruptionBudgetSpec) ProtoMessage() {}
func (*PodDisruptionBudgetSpec) Descriptor() ([]byte, []int) {
return fileDescriptor_2d50488813b2d18e, []int{2}
return fileDescriptor_2d50488813b2d18e, []int{3}
}
func (m *PodDisruptionBudgetSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -134,7 +162,7 @@ var xxx_messageInfo_PodDisruptionBudgetSpec proto.InternalMessageInfo
func (m *PodDisruptionBudgetStatus) Reset() { *m = PodDisruptionBudgetStatus{} }
func (*PodDisruptionBudgetStatus) ProtoMessage() {}
func (*PodDisruptionBudgetStatus) Descriptor() ([]byte, []int) {
return fileDescriptor_2d50488813b2d18e, []int{3}
return fileDescriptor_2d50488813b2d18e, []int{4}
}
func (m *PodDisruptionBudgetStatus) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -160,6 +188,7 @@ func (m *PodDisruptionBudgetStatus) XXX_DiscardUnknown() {
var xxx_messageInfo_PodDisruptionBudgetStatus proto.InternalMessageInfo
func init() {
proto.RegisterType((*Eviction)(nil), "k8s.io.api.policy.v1.Eviction")
proto.RegisterType((*PodDisruptionBudget)(nil), "k8s.io.api.policy.v1.PodDisruptionBudget")
proto.RegisterType((*PodDisruptionBudgetList)(nil), "k8s.io.api.policy.v1.PodDisruptionBudgetList")
proto.RegisterType((*PodDisruptionBudgetSpec)(nil), "k8s.io.api.policy.v1.PodDisruptionBudgetSpec")
@ -172,55 +201,103 @@ func init() {
}
var fileDescriptor_2d50488813b2d18e = []byte{
// 766 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0xdf, 0x6e, 0xe3, 0x44,
0x14, 0xc6, 0xe3, 0xa4, 0x29, 0x65, 0x36, 0x8d, 0xaa, 0x61, 0x81, 0x90, 0x0b, 0x77, 0xd5, 0xab,
0x82, 0xb4, 0x63, 0xba, 0x8b, 0x50, 0x85, 0x04, 0x62, 0xbd, 0x59, 0xc1, 0x22, 0x4a, 0x56, 0x53,
0x10, 0x12, 0x02, 0x89, 0x89, 0x7d, 0xd6, 0x19, 0x62, 0x7b, 0xac, 0x99, 0xb1, 0xd9, 0x5c, 0xc1,
0x23, 0xf0, 0x0a, 0x3c, 0x0a, 0x57, 0xf4, 0x0a, 0xed, 0xe5, 0x5e, 0x45, 0xd4, 0xbc, 0x08, 0xf2,
0xd8, 0xf9, 0xe3, 0x24, 0x55, 0x03, 0x77, 0x99, 0x39, 0xe7, 0xfb, 0x9d, 0x39, 0xdf, 0x39, 0x0e,
0xfa, 0x78, 0x72, 0xae, 0x08, 0x17, 0xce, 0x24, 0x1d, 0x81, 0x8c, 0x41, 0x83, 0x72, 0x32, 0x88,
0x7d, 0x21, 0x9d, 0x2a, 0xc0, 0x12, 0xee, 0x24, 0x22, 0xe4, 0xde, 0xd4, 0xc9, 0xce, 0x9c, 0x00,
0x62, 0x90, 0x4c, 0x83, 0x4f, 0x12, 0x29, 0xb4, 0xc0, 0x77, 0xcb, 0x2c, 0xc2, 0x12, 0x4e, 0xca,
0x2c, 0x92, 0x9d, 0xf5, 0xef, 0x07, 0x5c, 0x8f, 0xd3, 0x11, 0xf1, 0x44, 0xe4, 0x04, 0x22, 0x10,
0x8e, 0x49, 0x1e, 0xa5, 0xcf, 0xcd, 0xc9, 0x1c, 0xcc, 0xaf, 0x12, 0xd2, 0xff, 0x60, 0x59, 0x2a,
0x62, 0xde, 0x98, 0xc7, 0x20, 0xa7, 0x4e, 0x32, 0x09, 0x8a, 0x0b, 0xe5, 0x44, 0xa0, 0xd9, 0x96,
0xd2, 0x7d, 0xe7, 0x26, 0x95, 0x4c, 0x63, 0xcd, 0x23, 0xd8, 0x10, 0x7c, 0x78, 0x9b, 0x40, 0x79,
0x63, 0x88, 0xd8, 0x86, 0xee, 0xe1, 0x4d, 0xba, 0x54, 0xf3, 0xd0, 0xe1, 0xb1, 0x56, 0x5a, 0xae,
0x8b, 0x4e, 0x7e, 0x6f, 0xa2, 0x37, 0x9e, 0x09, 0x7f, 0xc0, 0x95, 0x4c, 0x13, 0xcd, 0x45, 0xec,
0xa6, 0x7e, 0x00, 0x1a, 0xff, 0x88, 0x0e, 0x8a, 0x86, 0x7c, 0xa6, 0x59, 0xcf, 0xba, 0x67, 0x9d,
0xde, 0x79, 0xf0, 0x3e, 0x59, 0x7a, 0xb8, 0xe0, 0x93, 0x64, 0x12, 0x14, 0x17, 0x8a, 0x14, 0xd9,
0x24, 0x3b, 0x23, 0xc3, 0xd1, 0x4f, 0xe0, 0xe9, 0x0b, 0xd0, 0xcc, 0xc5, 0x57, 0xb3, 0xe3, 0x46,
0x3e, 0x3b, 0x46, 0xcb, 0x3b, 0xba, 0xa0, 0xe2, 0x21, 0xda, 0x53, 0x09, 0x78, 0xbd, 0xa6, 0xa1,
0xdf, 0x27, 0xdb, 0x26, 0x44, 0xb6, 0x3c, 0xed, 0x32, 0x01, 0xcf, 0xed, 0x54, 0xe8, 0xbd, 0xe2,
0x44, 0x0d, 0x08, 0x7f, 0x8b, 0xf6, 0x95, 0x66, 0x3a, 0x55, 0xbd, 0x96, 0x41, 0x3a, 0xbb, 0x23,
0x8d, 0xcc, 0xed, 0x56, 0xd0, 0xfd, 0xf2, 0x4c, 0x2b, 0xdc, 0xc9, 0x9f, 0x16, 0x7a, 0x7b, 0x8b,
0xea, 0x4b, 0xae, 0x34, 0xfe, 0x7e, 0xc3, 0x27, 0xb2, 0x9b, 0x4f, 0x85, 0xda, 0xb8, 0x74, 0x54,
0x55, 0x3d, 0x98, 0xdf, 0xac, 0x78, 0xf4, 0x15, 0x6a, 0x73, 0x0d, 0x91, 0xea, 0x35, 0xef, 0xb5,
0x4e, 0xef, 0x3c, 0x78, 0x77, 0xe7, 0x8e, 0xdc, 0xc3, 0x8a, 0xda, 0x7e, 0x5a, 0xe8, 0x69, 0x89,
0x39, 0xf9, 0xab, 0xb9, 0xb5, 0x93, 0xc2, 0x44, 0xfc, 0x1c, 0x75, 0x22, 0x1e, 0x3f, 0xca, 0x18,
0x0f, 0xd9, 0x28, 0x84, 0x5b, 0xa7, 0x5e, 0x6c, 0x15, 0x29, 0xb7, 0x8a, 0x3c, 0x8d, 0xf5, 0x50,
0x5e, 0x6a, 0xc9, 0xe3, 0xc0, 0x3d, 0xca, 0x67, 0xc7, 0x9d, 0x8b, 0x15, 0x12, 0xad, 0x71, 0xf1,
0x0f, 0xe8, 0x40, 0x41, 0x08, 0x9e, 0x16, 0xb2, 0x9a, 0xfd, 0xc3, 0x1d, 0x1d, 0x63, 0x23, 0x08,
0x2f, 0x2b, 0xa9, 0xdb, 0x29, 0x2c, 0x9b, 0x9f, 0xe8, 0x02, 0x89, 0x43, 0xd4, 0x8d, 0xd8, 0x8b,
0x6f, 0x62, 0xb6, 0x68, 0xa4, 0xf5, 0x3f, 0x1b, 0xc1, 0xf9, 0xec, 0xb8, 0x7b, 0x51, 0x63, 0xd1,
0x35, 0xf6, 0xc9, 0x1f, 0x6d, 0xf4, 0xce, 0x8d, 0x0b, 0x85, 0xbf, 0x40, 0x58, 0x8c, 0x14, 0xc8,
0x0c, 0xfc, 0xcf, 0xca, 0xef, 0x8e, 0x8b, 0xd8, 0x18, 0xdb, 0x72, 0xfb, 0xd5, 0x80, 0xf0, 0x70,
0x23, 0x83, 0x6e, 0x51, 0xe1, 0x5f, 0xd0, 0xa1, 0x5f, 0x56, 0x01, 0xff, 0x99, 0xf0, 0xe7, 0x2b,
0xe1, 0xfe, 0xc7, 0x25, 0x27, 0x83, 0x55, 0xc8, 0x93, 0x58, 0xcb, 0xa9, 0xfb, 0x66, 0xf5, 0x94,
0xc3, 0x5a, 0x8c, 0xd6, 0xeb, 0x15, 0xcd, 0xf8, 0x0b, 0xa4, 0x7a, 0x14, 0x86, 0xe2, 0x67, 0xf0,
0x8d, 0xb9, 0xed, 0x65, 0x33, 0x83, 0x8d, 0x0c, 0xba, 0x45, 0x85, 0x3f, 0x41, 0x5d, 0x2f, 0x95,
0x12, 0x62, 0xfd, 0x39, 0xb0, 0x50, 0x8f, 0xa7, 0xbd, 0x3d, 0xc3, 0x79, 0xab, 0xe2, 0x74, 0x1f,
0xd7, 0xa2, 0x74, 0x2d, 0xbb, 0xd0, 0xfb, 0xa0, 0xb8, 0x04, 0x7f, 0xae, 0x6f, 0xd7, 0xf5, 0x83,
0x5a, 0x94, 0xae, 0x65, 0xe3, 0x73, 0xd4, 0x81, 0x17, 0x09, 0x78, 0x73, 0x2f, 0xf7, 0x8d, 0xfa,
0x6e, 0xa5, 0xee, 0x3c, 0x59, 0x89, 0xd1, 0x5a, 0x26, 0xf6, 0x10, 0xf2, 0x44, 0xec, 0x73, 0xd3,
0x4e, 0xef, 0x35, 0x33, 0x03, 0x67, 0xb7, 0xfd, 0x7d, 0x3c, 0xd7, 0x2d, 0xff, 0x18, 0x17, 0x57,
0x8a, 0xae, 0x60, 0xfb, 0x21, 0xc2, 0x9b, 0x63, 0xc2, 0x47, 0xa8, 0x35, 0x81, 0xa9, 0x59, 0x9f,
0xd7, 0x69, 0xf1, 0x13, 0x7f, 0x8a, 0xda, 0x19, 0x0b, 0x53, 0xa8, 0xbe, 0xa3, 0xf7, 0x76, 0x7b,
0xc7, 0xd7, 0x3c, 0x02, 0x5a, 0x0a, 0x3f, 0x6a, 0x9e, 0x5b, 0xee, 0xe9, 0xd5, 0xb5, 0xdd, 0x78,
0x79, 0x6d, 0x37, 0x5e, 0x5d, 0xdb, 0x8d, 0x5f, 0x73, 0xdb, 0xba, 0xca, 0x6d, 0xeb, 0x65, 0x6e,
0x5b, 0xaf, 0x72, 0xdb, 0xfa, 0x3b, 0xb7, 0xad, 0xdf, 0xfe, 0xb1, 0x1b, 0xdf, 0x35, 0xb3, 0xb3,
0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xca, 0x04, 0x93, 0xc0, 0x85, 0x07, 0x00, 0x00,
// 805 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x94, 0xdf, 0x8e, 0xdb, 0x44,
0x14, 0xc6, 0xe3, 0x64, 0xb3, 0x2c, 0xd3, 0x24, 0x5a, 0x86, 0x02, 0x4b, 0x2e, 0x1c, 0x94, 0xab,
0x05, 0xa9, 0x63, 0xb6, 0x45, 0x68, 0x85, 0x04, 0xa2, 0x6e, 0x56, 0x50, 0xd4, 0x25, 0xd5, 0x2c,
0x08, 0x09, 0x81, 0xc4, 0xc4, 0x3e, 0xcd, 0x0e, 0xb1, 0x3d, 0xd6, 0xcc, 0xd8, 0x34, 0x57, 0xf0,
0x08, 0xbc, 0x02, 0x8f, 0xc2, 0x15, 0x7b, 0x85, 0x7a, 0x59, 0x71, 0x11, 0xb1, 0xe6, 0x45, 0x90,
0xc7, 0xce, 0x1f, 0x27, 0x59, 0x35, 0xe5, 0x82, 0x3b, 0xcf, 0x99, 0xf3, 0xfd, 0x8e, 0xcf, 0x37,
0x67, 0x06, 0x7d, 0x3c, 0x39, 0x55, 0x84, 0x0b, 0x67, 0x92, 0x8c, 0x40, 0x46, 0xa0, 0x41, 0x39,
0x29, 0x44, 0xbe, 0x90, 0x4e, 0xb9, 0xc1, 0x62, 0xee, 0xc4, 0x22, 0xe0, 0xde, 0xd4, 0x49, 0x4f,
0x9c, 0x31, 0x44, 0x20, 0x99, 0x06, 0x9f, 0xc4, 0x52, 0x68, 0x81, 0x6f, 0x17, 0x59, 0x84, 0xc5,
0x9c, 0x14, 0x59, 0x24, 0x3d, 0xe9, 0xde, 0x19, 0x73, 0x7d, 0x99, 0x8c, 0x88, 0x27, 0x42, 0x67,
0x2c, 0xc6, 0xc2, 0x31, 0xc9, 0xa3, 0xe4, 0x89, 0x59, 0x99, 0x85, 0xf9, 0x2a, 0x20, 0xdd, 0x0f,
0x96, 0xa5, 0x42, 0xe6, 0x5d, 0xf2, 0x08, 0xe4, 0xd4, 0x89, 0x27, 0xe3, 0x3c, 0xa0, 0x9c, 0x10,
0x34, 0xdb, 0x52, 0xba, 0xeb, 0xdc, 0xa4, 0x92, 0x49, 0xa4, 0x79, 0x08, 0x1b, 0x82, 0x0f, 0x5f,
0x24, 0x50, 0xde, 0x25, 0x84, 0x6c, 0x43, 0x77, 0xef, 0x26, 0x5d, 0xa2, 0x79, 0xe0, 0xf0, 0x48,
0x2b, 0x2d, 0xd7, 0x45, 0xfd, 0xbf, 0x2c, 0x74, 0x70, 0x96, 0x72, 0x4f, 0x73, 0x11, 0xe1, 0x1f,
0xd0, 0x41, 0xde, 0x85, 0xcf, 0x34, 0x3b, 0xb2, 0xde, 0xb1, 0x8e, 0x6f, 0xdd, 0x7d, 0x9f, 0x2c,
0x8d, 0x5b, 0x40, 0x49, 0x3c, 0x19, 0xe7, 0x01, 0x45, 0xf2, 0x6c, 0x92, 0x9e, 0x90, 0xe1, 0xe8,
0x47, 0xf0, 0xf4, 0x39, 0x68, 0xe6, 0xe2, 0xab, 0x59, 0xaf, 0x96, 0xcd, 0x7a, 0x68, 0x19, 0xa3,
0x0b, 0x2a, 0x0e, 0x50, 0xdb, 0x87, 0x00, 0x34, 0x0c, 0xe3, 0xbc, 0xa2, 0x3a, 0xaa, 0x9b, 0x32,
0xf7, 0x76, 0x2b, 0x33, 0x58, 0x95, 0xba, 0xaf, 0x65, 0xb3, 0x5e, 0xbb, 0x12, 0xa2, 0x55, 0x78,
0xff, 0xb7, 0x3a, 0x7a, 0xfd, 0xb1, 0xf0, 0x07, 0x5c, 0xc9, 0xc4, 0x84, 0xdc, 0xc4, 0x1f, 0x83,
0xfe, 0x1f, 0xfa, 0x1c, 0xa2, 0x3d, 0x15, 0x83, 0x57, 0xb6, 0x77, 0x87, 0x6c, 0x1b, 0x3f, 0xb2,
0xe5, 0xd7, 0x2e, 0x62, 0xf0, 0xdc, 0x56, 0x89, 0xde, 0xcb, 0x57, 0xd4, 0x80, 0xf0, 0x37, 0x68,
0x5f, 0x69, 0xa6, 0x13, 0x75, 0xd4, 0x30, 0x48, 0x67, 0x77, 0xa4, 0x91, 0xb9, 0x9d, 0x12, 0xba,
0x5f, 0xac, 0x69, 0x89, 0xeb, 0xff, 0x61, 0xa1, 0xb7, 0xb6, 0xa8, 0x1e, 0x71, 0xa5, 0xf1, 0x77,
0x1b, 0x3e, 0x91, 0xdd, 0x7c, 0xca, 0xd5, 0xc6, 0xa5, 0xc3, 0xb2, 0xea, 0xc1, 0x3c, 0xb2, 0xe2,
0xd1, 0x97, 0xa8, 0xc9, 0x35, 0x84, 0xf9, 0x0c, 0x34, 0x8e, 0x6f, 0xdd, 0x7d, 0x77, 0xe7, 0x8e,
0xdc, 0x76, 0x49, 0x6d, 0x3e, 0xcc, 0xf5, 0xb4, 0xc0, 0xf4, 0xff, 0xac, 0x6f, 0xed, 0x24, 0x37,
0x11, 0x3f, 0x41, 0xad, 0x90, 0x47, 0xf7, 0x53, 0xc6, 0x03, 0x36, 0x0a, 0xe0, 0x85, 0xa7, 0x9e,
0x5f, 0x19, 0x52, 0x5c, 0x19, 0xf2, 0x30, 0xd2, 0x43, 0x79, 0xa1, 0x25, 0x8f, 0xc6, 0xee, 0x61,
0x36, 0xeb, 0xb5, 0xce, 0x57, 0x48, 0xb4, 0xc2, 0xc5, 0xdf, 0xa3, 0x03, 0x05, 0x01, 0x78, 0x5a,
0xc8, 0x97, 0x1b, 0xed, 0x47, 0x6c, 0x04, 0xc1, 0x45, 0x29, 0x75, 0x5b, 0xb9, 0x65, 0xf3, 0x15,
0x5d, 0x20, 0x71, 0x80, 0x3a, 0x21, 0x7b, 0xfa, 0x75, 0xc4, 0x16, 0x8d, 0x34, 0xfe, 0x63, 0x23,
0x38, 0x9b, 0xf5, 0x3a, 0xe7, 0x15, 0x16, 0x5d, 0x63, 0xf7, 0x7f, 0x6f, 0xa2, 0xb7, 0x6f, 0x1c,
0x28, 0xfc, 0x05, 0xc2, 0x62, 0xa4, 0x40, 0xa6, 0xe0, 0x7f, 0x56, 0x3c, 0x2a, 0x5c, 0x44, 0xc6,
0xd8, 0x86, 0xdb, 0x2d, 0x0f, 0x08, 0x0f, 0x37, 0x32, 0xe8, 0x16, 0x15, 0xfe, 0x19, 0xb5, 0xfd,
0xa2, 0x0a, 0xf8, 0x8f, 0x85, 0x3f, 0x1f, 0x09, 0xf7, 0x25, 0x87, 0x9c, 0x0c, 0x56, 0x21, 0x67,
0x91, 0x96, 0x53, 0xf7, 0x8d, 0xf2, 0x57, 0xda, 0x95, 0x3d, 0x5a, 0xad, 0x97, 0x37, 0xe3, 0x2f,
0x90, 0xea, 0x7e, 0x10, 0x88, 0x9f, 0xc0, 0x37, 0xe6, 0x36, 0x97, 0xcd, 0x0c, 0x36, 0x32, 0xe8,
0x16, 0x15, 0xfe, 0x04, 0x75, 0xbc, 0x44, 0x4a, 0x88, 0xf4, 0xe7, 0xc0, 0x02, 0x7d, 0x39, 0x3d,
0xda, 0x33, 0x9c, 0x37, 0x4b, 0x4e, 0xe7, 0x41, 0x65, 0x97, 0xae, 0x65, 0xe7, 0x7a, 0x1f, 0x14,
0x97, 0xe0, 0xcf, 0xf5, 0xcd, 0xaa, 0x7e, 0x50, 0xd9, 0xa5, 0x6b, 0xd9, 0xf8, 0x14, 0xb5, 0xe0,
0x69, 0x0c, 0xde, 0xdc, 0xcb, 0x7d, 0xa3, 0xbe, 0x5d, 0xaa, 0x5b, 0x67, 0x2b, 0x7b, 0xb4, 0x92,
0x89, 0x3d, 0x84, 0x3c, 0x11, 0xf9, 0xbc, 0x78, 0x9a, 0x5f, 0x31, 0x67, 0xe0, 0xec, 0x36, 0xbf,
0x0f, 0xe6, 0xba, 0xe5, 0xc3, 0xb8, 0x08, 0x29, 0xba, 0x82, 0xed, 0x06, 0x08, 0x6f, 0x1e, 0x13,
0x3e, 0x44, 0x8d, 0x09, 0x4c, 0xcd, 0xf8, 0xbc, 0x4a, 0xf3, 0x4f, 0xfc, 0x29, 0x6a, 0xa6, 0x2c,
0x48, 0xa0, 0xbc, 0x47, 0xef, 0xed, 0xf6, 0x1f, 0x5f, 0xf1, 0x10, 0x68, 0x21, 0xfc, 0xa8, 0x7e,
0x6a, 0xb9, 0xc7, 0x57, 0xd7, 0x76, 0xed, 0xd9, 0xb5, 0x5d, 0x7b, 0x7e, 0x6d, 0xd7, 0x7e, 0xc9,
0x6c, 0xeb, 0x2a, 0xb3, 0xad, 0x67, 0x99, 0x6d, 0x3d, 0xcf, 0x6c, 0xeb, 0xef, 0xcc, 0xb6, 0x7e,
0xfd, 0xc7, 0xae, 0x7d, 0x5b, 0x4f, 0x4f, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xce, 0x1b, 0x9d,
0x9f, 0x62, 0x08, 0x00, 0x00,
}
func (m *Eviction) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Eviction) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Eviction) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.DeleteOptions != nil {
{
size, err := m.DeleteOptions.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
{
size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *PodDisruptionBudget) Marshal() (dAtA []byte, err error) {
@ -474,6 +551,21 @@ func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v)
return base
}
func (m *Eviction) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = m.ObjectMeta.Size()
n += 1 + l + sovGenerated(uint64(l))
if m.DeleteOptions != nil {
l = m.DeleteOptions.Size()
n += 1 + l + sovGenerated(uint64(l))
}
return n
}
func (m *PodDisruptionBudget) Size() (n int) {
if m == nil {
return 0
@ -562,6 +654,17 @@ func sovGenerated(x uint64) (n int) {
func sozGenerated(x uint64) (n int) {
return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (this *Eviction) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&Eviction{`,
`ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`,
`DeleteOptions:` + strings.Replace(fmt.Sprintf("%v", this.DeleteOptions), "DeleteOptions", "v1.DeleteOptions", 1) + `,`,
`}`,
}, "")
return s
}
func (this *PodDisruptionBudget) String() string {
if this == nil {
return "nil"
@ -641,6 +744,125 @@ func valueToStringGenerated(v interface{}) string {
pv := reflect.Indirect(rv).Interface()
return fmt.Sprintf("*%v", pv)
}
func (m *Eviction) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Eviction: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Eviction: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field DeleteOptions", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.DeleteOptions == nil {
m.DeleteOptions = &v1.DeleteOptions{}
}
if err := m.DeleteOptions.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *PodDisruptionBudget) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View File

@ -29,6 +29,19 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v1";
// Eviction evicts a pod from its node subject to certain policies and safety constraints.
// This is a subresource of Pod. A request to cause such an eviction is
// created by POSTing to .../pods/<pod name>/evictions.
message Eviction {
// ObjectMeta describes the pod that is being evicted.
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1;
// DeleteOptions may be provided
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.DeleteOptions deleteOptions = 2;
}
// PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods
message PodDisruptionBudget {
// Standard object's metadata.

View File

@ -44,6 +44,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&PodDisruptionBudget{},
&PodDisruptionBudgetList{},
&Eviction{},
)
// Add the watch version that applies
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)

View File

@ -21,6 +21,9 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
)
// DisruptionBudgetCause is the status cause returned for eviction failures caused by PodDisruptionBudget violations.
const DisruptionBudgetCause metav1.CauseType = "DisruptionBudget"
// PodDisruptionBudgetSpec is a description of a PodDisruptionBudget.
type PodDisruptionBudgetSpec struct {
// An eviction is allowed if at least "minAvailable" pods selected by
@ -148,3 +151,22 @@ type PodDisruptionBudgetList struct {
// Items is a list of PodDisruptionBudgets
Items []PodDisruptionBudget `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// +genclient
// +genclient:noVerbs
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Eviction evicts a pod from its node subject to certain policies and safety constraints.
// This is a subresource of Pod. A request to cause such an eviction is
// created by POSTing to .../pods/<pod name>/evictions.
type Eviction struct {
metav1.TypeMeta `json:",inline"`
// ObjectMeta describes the pod that is being evicted.
// +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// DeleteOptions may be provided
// +optional
DeleteOptions *metav1.DeleteOptions `json:"deleteOptions,omitempty" protobuf:"bytes,2,opt,name=deleteOptions"`
}

View File

@ -27,6 +27,16 @@ package v1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_Eviction = map[string]string{
"": "Eviction evicts a pod from its node subject to certain policies and safety constraints. This is a subresource of Pod. A request to cause such an eviction is created by POSTing to .../pods/<pod name>/evictions.",
"metadata": "ObjectMeta describes the pod that is being evicted.",
"deleteOptions": "DeleteOptions may be provided",
}
func (Eviction) SwaggerDoc() map[string]string {
return map_Eviction
}
var map_PodDisruptionBudget = map[string]string{
"": "PodDisruptionBudget is an object to define the max disruption that can be caused to a collection of pods",
"metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",

View File

@ -26,6 +26,37 @@ import (
intstr "k8s.io/apimachinery/pkg/util/intstr"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Eviction) DeepCopyInto(out *Eviction) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.DeleteOptions != nil {
in, out := &in.DeleteOptions, &out.DeleteOptions
*out = new(metav1.DeleteOptions)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Eviction.
func (in *Eviction) DeepCopy() *Eviction {
if in == nil {
return nil
}
out := new(Eviction)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Eviction) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PodDisruptionBudget) DeepCopyInto(out *PodDisruptionBudget) {
*out = *in

View File

@ -0,0 +1,55 @@
{
"kind": "Eviction",
"apiVersion": "policy/v1",
"metadata": {
"name": "2",
"generateName": "3",
"namespace": "4",
"selfLink": "5",
"uid": "7",
"resourceVersion": "11042405498087606203",
"generation": 8071137005907523419,
"creationTimestamp": null,
"deletionGracePeriodSeconds": -4955867275792137171,
"labels": {
"7": "8"
},
"annotations": {
"9": "10"
},
"ownerReferences": [
{
"apiVersion": "11",
"kind": "12",
"name": "13",
"uid": "Dz廔ȇ{sŊƏp",
"controller": false,
"blockOwnerDeletion": true
}
],
"finalizers": [
"14"
],
"clusterName": "15",
"managedFields": [
{
"manager": "16",
"operation": "鐊唊飙Ş-U圴÷a/ɔ}摁(湗Ć]",
"apiVersion": "17",
"fieldsType": "18"
}
]
},
"deleteOptions": {
"gracePeriodSeconds": 3850803321873644574,
"preconditions": {
"uid": "枊a8衍`Ĩɘ.蘯6ċV夸eɑeʤ脽ě",
"resourceVersion": "19"
},
"orphanDependents": true,
"propagationPolicy": "蓏Ŋ",
"dryRun": [
"20"
]
}
}

Binary file not shown.

View File

@ -0,0 +1,40 @@
apiVersion: policy/v1
deleteOptions:
dryRun:
- "20"
gracePeriodSeconds: 3850803321873644574
orphanDependents: true
preconditions:
resourceVersion: "19"
uid: 枊a8衍`Ĩɘ.蘯6ċV夸eɑeʤ脽ě
propagationPolicy: 蓏Ŋ
kind: Eviction
metadata:
annotations:
"9": "10"
clusterName: "15"
creationTimestamp: null
deletionGracePeriodSeconds: -4955867275792137171
finalizers:
- "14"
generateName: "3"
generation: 8071137005907523419
labels:
"7": "8"
managedFields:
- apiVersion: "17"
fieldsType: "18"
manager: "16"
operation: 鐊唊飙Ş-U圴÷a/ɔ}摁(湗Ć]
name: "2"
namespace: "4"
ownerReferences:
- apiVersion: "11"
blockOwnerDeletion: true
controller: false
kind: "12"
name: "13"
uid: Dz廔ȇ{sŊƏp
resourceVersion: "11042405498087606203"
selfLink: "5"
uid: "7"

View File

@ -123,7 +123,7 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
scope.err(err, w, req)
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()))
scope.err(err, w, req)
return

View File

@ -89,8 +89,14 @@ type RequestScope struct {
TableConvertor rest.TableConvertor
FieldManager *fieldmanager.FieldManager
Resource schema.GroupVersionResource
Kind schema.GroupVersionKind
Resource schema.GroupVersionResource
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
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)
}
// 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 {
// 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.

View File

@ -110,7 +110,7 @@ func UpdateResource(r rest.Updater, scope *RequestScope, admit admission.Interfa
scope.err(err, w, req)
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()))
scope.err(err, w, req)
return

View File

@ -251,6 +251,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
connecter, isConnecter := storage.(rest.Connecter)
storageMeta, isMetadata := storage.(rest.StorageMetadata)
storageVersionProvider, isStorageVersionProvider := storage.(rest.StorageVersionProvider)
gvAcceptor, _ := storage.(rest.GroupVersionAcceptor)
if !isMetadata {
storageMeta = defaultStorageMetadata{}
}
@ -587,6 +588,8 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Subresource: subresource,
Kind: fqKindToRegister,
AcceptsGroupVersionDelegate: gvAcceptor,
HubGroupVersion: schema.GroupVersion{Group: fqKindToRegister.Group, Version: runtime.APIVersionInternal},
MetaGroupVersion: metav1.SchemeGroupVersion,

View File

@ -92,6 +92,13 @@ type GroupVersionKindProvider interface {
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.
type Lister interface {
// NewList returns an empty object that can be used with the List call.

View File

@ -8984,6 +8984,22 @@ var schemaYAML = typed.YAMLObject(`types:
elementType:
namedType: io.k8s.api.core.v1.Toleration
elementRelationship: atomic
- name: io.k8s.api.policy.v1.Eviction
map:
fields:
- name: apiVersion
type:
scalar: string
- name: deleteOptions
type:
namedType: io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions
- name: kind
type:
scalar: string
- name: metadata
type:
namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
default: {}
- name: io.k8s.api.policy.v1.PodDisruptionBudget
map:
fields:

View File

@ -0,0 +1,256 @@
/*
Copyright 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.
*/
// Code generated by applyconfiguration-gen. DO NOT EDIT.
package v1
import (
policyv1 "k8s.io/api/policy/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
managedfields "k8s.io/apimachinery/pkg/util/managedfields"
internal "k8s.io/client-go/applyconfigurations/internal"
v1 "k8s.io/client-go/applyconfigurations/meta/v1"
)
// EvictionApplyConfiguration represents an declarative configuration of the Eviction type for use
// with apply.
type EvictionApplyConfiguration struct {
v1.TypeMetaApplyConfiguration `json:",inline"`
*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"`
DeleteOptions *v1.DeleteOptionsApplyConfiguration `json:"deleteOptions,omitempty"`
}
// Eviction constructs an declarative configuration of the Eviction type for use with
// apply.
func Eviction(name, namespace string) *EvictionApplyConfiguration {
b := &EvictionApplyConfiguration{}
b.WithName(name)
b.WithNamespace(namespace)
b.WithKind("Eviction")
b.WithAPIVersion("policy/v1")
return b
}
// ExtractEviction extracts the applied configuration owned by fieldManager from
// eviction. If no managedFields are found in eviction for fieldManager, a
// EvictionApplyConfiguration is returned with only the Name, Namespace (if applicable),
// APIVersion and Kind populated. Is is possible that no managed fields were found for because other
// field managers have taken ownership of all the fields previously owned by fieldManager, or because
// the fieldManager never owned fields any fields.
// eviction must be a unmodified Eviction API object that was retrieved from the Kubernetes API.
// ExtractEviction provides a way to perform a extract/modify-in-place/apply workflow.
// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously
// applied if another fieldManager has updated or force applied any of the previously applied fields.
// Experimental!
func ExtractEviction(eviction *policyv1.Eviction, fieldManager string) (*EvictionApplyConfiguration, error) {
b := &EvictionApplyConfiguration{}
err := managedfields.ExtractInto(eviction, internal.Parser().Type("io.k8s.api.policy.v1.Eviction"), fieldManager, b)
if err != nil {
return nil, err
}
b.WithName(eviction.Name)
b.WithNamespace(eviction.Namespace)
b.WithKind("Eviction")
b.WithAPIVersion("policy/v1")
return b, nil
}
// WithKind sets the Kind field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Kind field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithKind(value string) *EvictionApplyConfiguration {
b.Kind = &value
return b
}
// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the APIVersion field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithAPIVersion(value string) *EvictionApplyConfiguration {
b.APIVersion = &value
return b
}
// WithName sets the Name field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Name field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithName(value string) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.Name = &value
return b
}
// WithGenerateName sets the GenerateName field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the GenerateName field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithGenerateName(value string) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.GenerateName = &value
return b
}
// WithNamespace sets the Namespace field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Namespace field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithNamespace(value string) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.Namespace = &value
return b
}
// WithSelfLink sets the SelfLink field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the SelfLink field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithSelfLink(value string) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.SelfLink = &value
return b
}
// WithUID sets the UID field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the UID field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithUID(value types.UID) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.UID = &value
return b
}
// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the ResourceVersion field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithResourceVersion(value string) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.ResourceVersion = &value
return b
}
// WithGeneration sets the Generation field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Generation field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithGeneration(value int64) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.Generation = &value
return b
}
// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the CreationTimestamp field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithCreationTimestamp(value metav1.Time) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.CreationTimestamp = &value
return b
}
// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the DeletionTimestamp field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.DeletionTimestamp = &value
return b
}
// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.DeletionGracePeriodSeconds = &value
return b
}
// WithLabels puts the entries into the Labels field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, the entries provided by each call will be put on the Labels field,
// overwriting an existing map entries in Labels field with the same key.
func (b *EvictionApplyConfiguration) WithLabels(entries map[string]string) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
if b.Labels == nil && len(entries) > 0 {
b.Labels = make(map[string]string, len(entries))
}
for k, v := range entries {
b.Labels[k] = v
}
return b
}
// WithAnnotations puts the entries into the Annotations field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, the entries provided by each call will be put on the Annotations field,
// overwriting an existing map entries in Annotations field with the same key.
func (b *EvictionApplyConfiguration) WithAnnotations(entries map[string]string) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
if b.Annotations == nil && len(entries) > 0 {
b.Annotations = make(map[string]string, len(entries))
}
for k, v := range entries {
b.Annotations[k] = v
}
return b
}
// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the OwnerReferences field.
func (b *EvictionApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
for i := range values {
if values[i] == nil {
panic("nil value passed to WithOwnerReferences")
}
b.OwnerReferences = append(b.OwnerReferences, *values[i])
}
return b
}
// WithFinalizers adds the given value to the Finalizers field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Finalizers field.
func (b *EvictionApplyConfiguration) WithFinalizers(values ...string) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
for i := range values {
b.Finalizers = append(b.Finalizers, values[i])
}
return b
}
// WithClusterName sets the ClusterName field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the ClusterName field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithClusterName(value string) *EvictionApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
b.ClusterName = &value
return b
}
func (b *EvictionApplyConfiguration) ensureObjectMetaApplyConfigurationExists() {
if b.ObjectMetaApplyConfiguration == nil {
b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{}
}
}
// WithDeleteOptions sets the DeleteOptions field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the DeleteOptions field is set to the value of the last call.
func (b *EvictionApplyConfiguration) WithDeleteOptions(value *v1.DeleteOptionsApplyConfiguration) *EvictionApplyConfiguration {
b.DeleteOptions = value
return b
}

View File

@ -1139,6 +1139,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &applyconfigurationsnodev1beta1.SchedulingApplyConfiguration{}
// Group=policy, Version=v1
case policyv1.SchemeGroupVersion.WithKind("Eviction"):
return &applyconfigurationspolicyv1.EvictionApplyConfiguration{}
case policyv1.SchemeGroupVersion.WithKind("PodDisruptionBudget"):
return &applyconfigurationspolicyv1.PodDisruptionBudgetApplyConfiguration{}
case policyv1.SchemeGroupVersion.WithKind("PodDisruptionBudgetSpec"):

View File

@ -24,7 +24,8 @@ import (
"strings"
v1 "k8s.io/api/core/v1"
policy "k8s.io/api/policy/v1beta1"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
restclient "k8s.io/client-go/rest"
@ -78,7 +79,23 @@ func (c *FakePods) GetLogs(name string, opts *v1.PodLogOptions) *restclient.Requ
return fakeClient.Request()
}
func (c *FakePods) Evict(ctx context.Context, eviction *policy.Eviction) error {
func (c *FakePods) Evict(ctx context.Context, eviction *policyv1beta1.Eviction) error {
return c.EvictV1beta1(ctx, eviction)
}
func (c *FakePods) EvictV1(ctx context.Context, eviction *policyv1.Eviction) error {
action := core.CreateActionImpl{}
action.Verb = "create"
action.Namespace = c.ns
action.Resource = podsResource
action.Subresource = "eviction"
action.Object = eviction
_, err := c.Fake.Invokes(action, eviction)
return err
}
func (c *FakePods) EvictV1beta1(ctx context.Context, eviction *policyv1beta1.Eviction) error {
action := core.CreateActionImpl{}
action.Verb = "create"
action.Namespace = c.ns

View File

@ -20,7 +20,8 @@ import (
"context"
v1 "k8s.io/api/core/v1"
policy "k8s.io/api/policy/v1beta1"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/net"
"k8s.io/client-go/kubernetes/scheme"
@ -30,7 +31,16 @@ import (
// The PodExpansion interface allows manually adding extra methods to the PodInterface.
type PodExpansion interface {
Bind(ctx context.Context, binding *v1.Binding, opts metav1.CreateOptions) error
Evict(ctx context.Context, eviction *policy.Eviction) error
// Evict submits a policy/v1beta1 Eviction request to the pod's eviction subresource.
// Equivalent to calling EvictV1beta1.
// Deprecated: Use EvictV1() (supported in 1.22+) or EvictV1beta1().
Evict(ctx context.Context, eviction *policyv1beta1.Eviction) error
// EvictV1 submits a policy/v1 Eviction request to the pod's eviction subresource.
// Supported in 1.22+.
EvictV1(ctx context.Context, eviction *policyv1.Eviction) error
// EvictV1beta1 submits a policy/v1beta1 Eviction request to the pod's eviction subresource.
// Supported in 1.22+.
EvictV1beta1(ctx context.Context, eviction *policyv1beta1.Eviction) error
GetLogs(name string, opts *v1.PodLogOptions) *restclient.Request
ProxyGet(scheme, name, port, path string, params map[string]string) restclient.ResponseWrapper
}
@ -40,7 +50,18 @@ func (c *pods) Bind(ctx context.Context, binding *v1.Binding, opts metav1.Create
return c.client.Post().Namespace(c.ns).Resource("pods").Name(binding.Name).VersionedParams(&opts, scheme.ParameterCodec).SubResource("binding").Body(binding).Do(ctx).Error()
}
func (c *pods) Evict(ctx context.Context, eviction *policy.Eviction) error {
// Evict submits a policy/v1beta1 Eviction request to the pod's eviction subresource.
// Equivalent to calling EvictV1beta1.
// Deprecated: Use EvictV1() (supported in 1.22+) or EvictV1beta1().
func (c *pods) Evict(ctx context.Context, eviction *policyv1beta1.Eviction) error {
return c.client.Post().Namespace(c.ns).Resource("pods").Name(eviction.Name).SubResource("eviction").Body(eviction).Do(ctx).Error()
}
func (c *pods) EvictV1beta1(ctx context.Context, eviction *policyv1beta1.Eviction) error {
return c.client.Post().Namespace(c.ns).Resource("pods").Name(eviction.Name).SubResource("eviction").Body(eviction).Do(ctx).Error()
}
func (c *pods) EvictV1(ctx context.Context, eviction *policyv1.Eviction) error {
return c.client.Post().Namespace(c.ns).Resource("pods").Name(eviction.Name).SubResource("eviction").Body(eviction).Do(ctx).Error()
}

View File

@ -0,0 +1,48 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1
import (
rest "k8s.io/client-go/rest"
)
// EvictionsGetter has a method to return a EvictionInterface.
// A group's client should implement this interface.
type EvictionsGetter interface {
Evictions(namespace string) EvictionInterface
}
// EvictionInterface has methods to work with Eviction resources.
type EvictionInterface interface {
EvictionExpansion
}
// evictions implements EvictionInterface
type evictions struct {
client rest.Interface
ns string
}
// newEvictions returns a Evictions
func newEvictions(c *PolicyV1Client, namespace string) *evictions {
return &evictions{
client: c.RESTClient(),
ns: namespace,
}
}

View File

@ -0,0 +1,40 @@
/*
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 (
"context"
policy "k8s.io/api/policy/v1"
)
// The EvictionExpansion interface allows manually adding extra methods to the ScaleInterface.
type EvictionExpansion interface {
Evict(ctx context.Context, eviction *policy.Eviction) error
}
func (c *evictions) Evict(ctx context.Context, eviction *policy.Eviction) error {
return c.client.Post().
AbsPath("/api/v1").
Namespace(eviction.Namespace).
Resource("pods").
Name(eviction.Name).
SubResource("eviction").
Body(eviction).
Do(ctx).
Error()
}

View File

@ -0,0 +1,25 @@
/*
Copyright 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
// FakeEvictions implements EvictionInterface
type FakeEvictions struct {
Fake *FakePolicyV1
ns string
}

View File

@ -0,0 +1,37 @@
/*
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 fake
import (
"context"
policy "k8s.io/api/policy/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
core "k8s.io/client-go/testing"
)
func (c *FakeEvictions) Evict(ctx context.Context, eviction *policy.Eviction) error {
action := core.CreateActionImpl{}
action.Verb = "create"
action.Namespace = c.ns
action.Resource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
action.Subresource = "eviction"
action.Object = eviction
_, err := c.Fake.Invokes(action, eviction)
return err
}

View File

@ -28,6 +28,10 @@ type FakePolicyV1 struct {
*testing.Fake
}
func (c *FakePolicyV1) Evictions(namespace string) v1.EvictionInterface {
return &FakeEvictions{c, namespace}
}
func (c *FakePolicyV1) PodDisruptionBudgets(namespace string) v1.PodDisruptionBudgetInterface {
return &FakePodDisruptionBudgets{c, namespace}
}

View File

@ -26,6 +26,7 @@ import (
type PolicyV1Interface interface {
RESTClient() rest.Interface
EvictionsGetter
PodDisruptionBudgetsGetter
}
@ -34,6 +35,10 @@ type PolicyV1Client struct {
restClient rest.Interface
}
func (c *PolicyV1Client) Evictions(namespace string) EvictionInterface {
return newEvictions(c, namespace)
}
func (c *PolicyV1Client) PodDisruptionBudgets(namespace string) PodDisruptionBudgetInterface {
return newPodDisruptionBudgets(c, namespace)
}

View File

@ -0,0 +1,99 @@
/*
Copyright 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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1
import (
v1 "k8s.io/api/policy/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// EvictionLister helps list Evictions.
// All objects returned here must be treated as read-only.
type EvictionLister interface {
// List lists all Evictions in the indexer.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1.Eviction, err error)
// Evictions returns an object that can list and get Evictions.
Evictions(namespace string) EvictionNamespaceLister
EvictionListerExpansion
}
// evictionLister implements the EvictionLister interface.
type evictionLister struct {
indexer cache.Indexer
}
// NewEvictionLister returns a new EvictionLister.
func NewEvictionLister(indexer cache.Indexer) EvictionLister {
return &evictionLister{indexer: indexer}
}
// List lists all Evictions in the indexer.
func (s *evictionLister) List(selector labels.Selector) (ret []*v1.Eviction, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Eviction))
})
return ret, err
}
// Evictions returns an object that can list and get Evictions.
func (s *evictionLister) Evictions(namespace string) EvictionNamespaceLister {
return evictionNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// EvictionNamespaceLister helps list and get Evictions.
// All objects returned here must be treated as read-only.
type EvictionNamespaceLister interface {
// List lists all Evictions in the indexer for a given namespace.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1.Eviction, err error)
// Get retrieves the Eviction from the indexer for a given namespace and name.
// Objects returned here must be treated as read-only.
Get(name string) (*v1.Eviction, error)
EvictionNamespaceListerExpansion
}
// evictionNamespaceLister implements the EvictionNamespaceLister
// interface.
type evictionNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Evictions in the indexer for a given namespace.
func (s evictionNamespaceLister) List(selector labels.Selector) (ret []*v1.Eviction, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1.Eviction))
})
return ret, err
}
// Get retrieves the Eviction from the indexer for a given namespace and name.
func (s evictionNamespaceLister) Get(name string) (*v1.Eviction, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1.Resource("eviction"), name)
}
return obj.(*v1.Eviction), nil
}

View File

@ -17,3 +17,11 @@ limitations under the License.
// Code generated by lister-gen. DO NOT EDIT.
package v1
// EvictionListerExpansion allows custom methods to be added to
// EvictionLister.
type EvictionListerExpansion interface{}
// EvictionNamespaceListerExpansion allows custom methods to be added to
// EvictionNamespaceLister.
type EvictionNamespaceListerExpansion interface{}

View File

@ -34,7 +34,6 @@ import (
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -767,7 +766,7 @@ func TestDrain(t *testing.T) {
{
Name: "policy",
PreferredVersion: metav1.GroupVersionForDiscovery{
GroupVersion: "policy/v1beta1",
GroupVersion: "policy/v1",
},
},
},
@ -780,8 +779,10 @@ func TestDrain(t *testing.T) {
if testEviction {
resourceList.APIResources = []metav1.APIResource{
{
Name: drain.EvictionSubresource,
Kind: drain.EvictionKind,
Name: drain.EvictionSubresource,
Kind: drain.EvictionKind,
Group: "policy",
Version: "v1",
},
}
}
@ -849,7 +850,7 @@ func TestDrain(t *testing.T) {
if test.failUponEvictionOrDeletion {
return nil, errors.New("request failed")
}
return &http.Response{StatusCode: http.StatusCreated, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &policyv1beta1.Eviction{})}, nil
return &http.Response{StatusCode: http.StatusCreated, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &metav1.Status{})}, nil
default:
t.Fatalf("%s: unexpected request: %v %#v\n%#v", test.description, req.Method, req.URL, req)
return nil, nil

View File

@ -565,6 +565,7 @@ func (f *TestFactory) KubernetesClientSet() (*kubernetes.Clientset, error) {
clientset.AppsV1beta2().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AppsV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.PolicyV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.PolicyV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.DiscoveryClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
return clientset, nil

View File

@ -24,12 +24,14 @@ import (
"time"
corev1 "k8s.io/api/core/v1"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/cli-runtime/pkg/resource"
@ -103,35 +105,21 @@ type waitForDeleteParams struct {
// CheckEvictionSupport uses Discovery API to find out if the server support
// eviction subresource If support, it will return its groupVersion; Otherwise,
// it will return an empty string
func CheckEvictionSupport(clientset kubernetes.Interface) (string, error) {
// it will return an empty GroupVersion
func CheckEvictionSupport(clientset kubernetes.Interface) (schema.GroupVersion, error) {
discoveryClient := clientset.Discovery()
groupList, err := discoveryClient.ServerGroups()
if err != nil {
return "", err
}
foundPolicyGroup := false
var policyGroupVersion string
for _, group := range groupList.Groups {
if group.Name == "policy" {
foundPolicyGroup = true
policyGroupVersion = group.PreferredVersion.GroupVersion
break
}
}
if !foundPolicyGroup {
return "", nil
}
// version info available in subresources since v1.8.0 in https://github.com/kubernetes/kubernetes/pull/49971
resourceList, err := discoveryClient.ServerResourcesForGroupVersion("v1")
if err != nil {
return "", err
return schema.GroupVersion{}, err
}
for _, resource := range resourceList.APIResources {
if resource.Name == EvictionSubresource && resource.Kind == EvictionKind {
return policyGroupVersion, nil
if resource.Name == EvictionSubresource && resource.Kind == EvictionKind && len(resource.Group) > 0 && len(resource.Version) > 0 {
return schema.GroupVersion{Group: resource.Group, Version: resource.Version}, nil
}
}
return "", nil
return schema.GroupVersion{}, nil
}
func (d *Helper) makeDeleteOptions() metav1.DeleteOptions {
@ -157,7 +145,7 @@ func (d *Helper) DeletePod(pod corev1.Pod) error {
}
// EvictPod will evict the give pod, or return an error if it couldn't
func (d *Helper) EvictPod(pod corev1.Pod, policyGroupVersion string) error {
func (d *Helper) EvictPod(pod corev1.Pod, evictionGroupVersion schema.GroupVersion) error {
if d.DryRunStrategy == cmdutil.DryRunServer {
if err := d.DryRunVerifier.HasSupport(pod.GroupVersionKind()); err != nil {
return err
@ -165,20 +153,30 @@ func (d *Helper) EvictPod(pod corev1.Pod, policyGroupVersion string) error {
}
delOpts := d.makeDeleteOptions()
eviction := &policyv1beta1.Eviction{
TypeMeta: metav1.TypeMeta{
APIVersion: policyGroupVersion,
Kind: EvictionKind,
},
ObjectMeta: metav1.ObjectMeta{
Name: pod.Name,
Namespace: pod.Namespace,
},
DeleteOptions: &delOpts,
}
// Remember to change change the URL manipulation func when Eviction's version change
return d.Client.PolicyV1beta1().Evictions(eviction.Namespace).Evict(d.getContext(), eviction)
switch evictionGroupVersion {
case policyv1.SchemeGroupVersion:
// send policy/v1 if the server supports it
eviction := &policyv1.Eviction{
ObjectMeta: metav1.ObjectMeta{
Name: pod.Name,
Namespace: pod.Namespace,
},
DeleteOptions: &delOpts,
}
return d.Client.PolicyV1().Evictions(eviction.Namespace).Evict(context.TODO(), eviction)
default:
// otherwise, fall back to policy/v1beta1, supported by all servers that support the eviction subresource
eviction := &policyv1beta1.Eviction{
ObjectMeta: metav1.ObjectMeta{
Name: pod.Name,
Namespace: pod.Namespace,
},
DeleteOptions: &delOpts,
}
return d.Client.PolicyV1beta1().Evictions(eviction.Namespace).Evict(context.TODO(), eviction)
}
}
// GetPodsForDeletion receives resource info for a node, and returns those pods as PodDeleteList,
@ -259,20 +257,20 @@ func (d *Helper) DeleteOrEvictPods(pods []corev1.Pod) error {
}
if !d.DisableEviction {
policyGroupVersion, err := CheckEvictionSupport(d.Client)
evictionGroupVersion, err := CheckEvictionSupport(d.Client)
if err != nil {
return err
}
if len(policyGroupVersion) > 0 {
return d.evictPods(pods, policyGroupVersion, getPodFn)
if !evictionGroupVersion.Empty() {
return d.evictPods(pods, evictionGroupVersion, getPodFn)
}
}
return d.deletePods(pods, getPodFn)
}
func (d *Helper) evictPods(pods []corev1.Pod, policyGroupVersion string, getPodFn func(namespace, name string) (*corev1.Pod, error)) error {
func (d *Helper) evictPods(pods []corev1.Pod, evictionGroupVersion schema.GroupVersion, getPodFn func(namespace, name string) (*corev1.Pod, error)) error {
returnCh := make(chan error, 1)
// 0 timeout means infinite, we use MaxInt64 to represent it.
var globalTimeout time.Duration
@ -313,7 +311,7 @@ func (d *Helper) evictPods(pods []corev1.Pod, policyGroupVersion string, getPodF
refreshPod = false
}
err := d.EvictPod(activePod, policyGroupVersion)
err := d.EvictPod(activePod, evictionGroupVersion)
if err == nil {
break
} else if apierrors.IsNotFound(err) {

View File

@ -29,6 +29,7 @@ import (
"time"
corev1 "k8s.io/api/core/v1"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -214,13 +215,20 @@ func createPods(ifCreateNewPods bool) (map[string]corev1.Pod, []corev1.Pod) {
return podMap, podSlice
}
func addCoreNonEvictionSupport(t *testing.T, k *fake.Clientset) {
coreResources := &metav1.APIResourceList{
GroupVersion: "v1",
}
k.Resources = append(k.Resources, coreResources)
}
// addEvictionSupport implements simple fake eviction support on the fake.Clientset
func addEvictionSupport(t *testing.T, k *fake.Clientset) {
func addEvictionSupport(t *testing.T, k *fake.Clientset, version string) {
podsEviction := metav1.APIResource{
Name: "pods/eviction",
Kind: "Eviction",
Group: "",
Version: "v1",
Group: "policy",
Version: version,
}
coreResources := &metav1.APIResourceList{
GroupVersion: "v1",
@ -238,13 +246,26 @@ func addEvictionSupport(t *testing.T, k *fake.Clientset) {
return false, nil, nil
}
eviction := *action.(ktest.CreateAction).GetObject().(*policyv1beta1.Eviction)
namespace := ""
name := ""
switch version {
case "v1":
eviction := *action.(ktest.CreateAction).GetObject().(*policyv1.Eviction)
namespace = eviction.Namespace
name = eviction.Name
case "v1beta1":
eviction := *action.(ktest.CreateAction).GetObject().(*policyv1beta1.Eviction)
namespace = eviction.Namespace
name = eviction.Name
default:
t.Errorf("unknown version %s", version)
}
// Avoid the lock
go func() {
err := k.CoreV1().Pods(eviction.Namespace).Delete(context.TODO(), eviction.Name, metav1.DeleteOptions{})
err := k.CoreV1().Pods(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil {
// Errorf because we can't call Fatalf from another goroutine
t.Errorf("failed to delete pod: %s/%s", eviction.Namespace, eviction.Name)
t.Errorf("failed to delete pod: %s/%s", namespace, name)
}
}()
@ -253,22 +274,23 @@ func addEvictionSupport(t *testing.T, k *fake.Clientset) {
}
func TestCheckEvictionSupport(t *testing.T) {
for _, evictionSupported := range []bool{true, false} {
evictionSupported := evictionSupported
t.Run(fmt.Sprintf("evictionSupported=%v", evictionSupported),
for _, evictionVersion := range []string{"", "v1", "v1beta1"} {
t.Run(fmt.Sprintf("evictionVersion=%v", evictionVersion),
func(t *testing.T) {
k := fake.NewSimpleClientset()
if evictionSupported {
addEvictionSupport(t, k)
if len(evictionVersion) > 0 {
addEvictionSupport(t, k, evictionVersion)
} else {
addCoreNonEvictionSupport(t, k)
}
apiGroup, err := CheckEvictionSupport(k)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedAPIGroup := ""
if evictionSupported {
expectedAPIGroup = "policy/v1"
expectedAPIGroup := schema.GroupVersion{}
if len(evictionVersion) > 0 {
expectedAPIGroup = schema.GroupVersion{Group: "policy", Version: evictionVersion}
}
if apiGroup != expectedAPIGroup {
t.Fatalf("expected apigroup %q, actual=%q", expectedAPIGroup, apiGroup)
@ -312,7 +334,7 @@ func TestDeleteOrEvict(t *testing.T) {
}
// Create 4 pods, and try to remove the first 2
var expectedEvictions []policyv1beta1.Eviction
var expectedEvictions []policyv1.Eviction
var create []runtime.Object
deletePods := []corev1.Pod{}
for i := 1; i <= 4; i++ {
@ -325,9 +347,7 @@ func TestDeleteOrEvict(t *testing.T) {
deletePods = append(deletePods, *pod)
if tc.evictionSupported && !tc.disableEviction {
eviction := policyv1beta1.Eviction{}
eviction.Kind = "Eviction"
eviction.APIVersion = "policy/v1"
eviction := policyv1.Eviction{}
eviction.Namespace = pod.Namespace
eviction.Name = pod.Name
@ -344,7 +364,9 @@ func TestDeleteOrEvict(t *testing.T) {
// Build the fake client
k := fake.NewSimpleClientset(create...)
if tc.evictionSupported {
addEvictionSupport(t, k)
addEvictionSupport(t, k, "v1")
} else {
addCoreNonEvictionSupport(t, k)
}
h.Client = k
h.DisableEviction = tc.disableEviction
@ -372,19 +394,19 @@ func TestDeleteOrEvict(t *testing.T) {
}
// Test that pods were evicted as expected
var actualEvictions []policyv1beta1.Eviction
var actualEvictions []policyv1.Eviction
for _, action := range k.Actions() {
if action.GetVerb() != "create" || action.GetResource().Resource != "pods" || action.GetSubresource() != "eviction" {
continue
}
eviction := *action.(ktest.CreateAction).GetObject().(*policyv1beta1.Eviction)
eviction := *action.(ktest.CreateAction).GetObject().(*policyv1.Eviction)
actualEvictions = append(actualEvictions, eviction)
}
sort.Slice(actualEvictions, func(i, j int) bool {
return actualEvictions[i].Name < actualEvictions[j].Name
})
if !reflect.DeepEqual(actualEvictions, expectedEvictions) {
t.Errorf("%s: unexpected evictions; actual %v; expected %v", tc.description, actualEvictions, expectedEvictions)
t.Errorf("%s: unexpected evictions; actual\n\t%v\nexpected\n\t%v", tc.description, actualEvictions, expectedEvictions)
}
})
}

View File

@ -704,6 +704,14 @@
description: PodDisruptionBudget API must support list and deletecollection operations.
release: v1.21
file: test/e2e/apps/disruption.go
- testname: 'PodDisruptionBudget: block an eviction until the PDB is updated to allow
it'
codename: '[sig-apps] DisruptionController should block an eviction until the PDB
is updated to allow it [Conformance]'
description: Eviction API must block an eviction until the PDB is updated to allow
it
release: v1.22
file: test/e2e/apps/disruption.go
- testname: 'PodDisruptionBudget: create, update, patch, and delete object'
codename: '[sig-apps] DisruptionController should create a PodDisruptionBudget [Conformance]'
description: PodDisruptionBudget API must support create, update, patch, and delete

View File

@ -23,12 +23,10 @@ import (
jsonpatch "github.com/evanphx/json-patch"
"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -285,7 +283,7 @@ var _ = SIGDescribe("DisruptionController", func() {
pod, err := locateRunningPod(cs, ns)
framework.ExpectNoError(err)
e := &policyv1beta1.Eviction{
e := &policyv1.Eviction{
ObjectMeta: metav1.ObjectMeta{
Name: pod.Name,
Namespace: ns,
@ -293,8 +291,9 @@ var _ = SIGDescribe("DisruptionController", func() {
}
if c.shouldDeny {
err = cs.CoreV1().Pods(ns).Evict(context.TODO(), e)
gomega.Expect(err).Should(gomega.MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
err = cs.CoreV1().Pods(ns).EvictV1(context.TODO(), e)
framework.ExpectError(err, "pod eviction should fail")
framework.ExpectEqual(apierrors.HasStatusCause(err, policyv1.DisruptionBudgetCause), true, "pod eviction should fail with DisruptionBudget cause")
} else {
// Only wait for running pods in the "allow" case
// because one of shouldDeny cases relies on the
@ -304,7 +303,7 @@ var _ = SIGDescribe("DisruptionController", func() {
// Since disruptionAllowed starts out false, if an eviction is ever allowed,
// that means the controller is working.
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 {
return false, nil
}
@ -315,7 +314,12 @@ var _ = SIGDescribe("DisruptionController", func() {
})
}
ginkgo.It("should block an eviction until the PDB is updated to allow it", func() {
/*
Release : v1.22
Testname: PodDisruptionBudget: block an eviction until the PDB is updated to allow it
Description: Eviction API must block an eviction until the PDB is updated to allow it
*/
framework.ConformanceIt("should block an eviction until the PDB is updated to allow it", func() {
ginkgo.By("Creating a pdb that targets all three pods in a test replica set")
createPDBMinAvailableOrDie(cs, ns, defaultName, intstr.FromInt(3), defaultLabels)
createReplicaSetOrDie(cs, ns, 3, false)
@ -325,14 +329,15 @@ var _ = SIGDescribe("DisruptionController", func() {
pod, err := locateRunningPod(cs, ns)
framework.ExpectNoError(err)
e := &policyv1beta1.Eviction{
e := &policyv1.Eviction{
ObjectMeta: metav1.ObjectMeta{
Name: pod.Name,
Namespace: ns,
},
}
err = cs.CoreV1().Pods(ns).Evict(context.TODO(), e)
gomega.Expect(err).Should(gomega.MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
err = cs.CoreV1().Pods(ns).EvictV1(context.TODO(), e)
framework.ExpectError(err, "pod eviction should fail")
framework.ExpectEqual(apierrors.HasStatusCause(err, policyv1.DisruptionBudgetCause), true, "pod eviction should fail with DisruptionBudget cause")
ginkgo.By("Updating the pdb to allow a pod to be evicted")
updatePDBOrDie(cs, ns, defaultName, func(pdb *policyv1.PodDisruptionBudget) *policyv1.PodDisruptionBudget {
@ -344,7 +349,7 @@ var _ = SIGDescribe("DisruptionController", func() {
ginkgo.By("Trying to evict the same pod we tried earlier which should now be evictable")
waitForPodsOrDie(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
ginkgo.By("Patching the pdb to disallow a pod to be evicted")
@ -362,21 +367,22 @@ var _ = SIGDescribe("DisruptionController", func() {
waitForPodsOrDie(cs, ns, 3)
pod, err = locateRunningPod(cs, ns) // locate a new running pod
framework.ExpectNoError(err)
e = &policyv1beta1.Eviction{
e = &policyv1.Eviction{
ObjectMeta: metav1.ObjectMeta{
Name: pod.Name,
Namespace: ns,
},
}
err = cs.CoreV1().Pods(ns).Evict(context.TODO(), e)
gomega.Expect(err).Should(gomega.MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
err = cs.CoreV1().Pods(ns).EvictV1(context.TODO(), e)
framework.ExpectError(err, "pod eviction should fail")
framework.ExpectEqual(apierrors.HasStatusCause(err, policyv1.DisruptionBudgetCause), true, "pod eviction should fail with DisruptionBudget cause")
ginkgo.By("Deleting the pdb to allow a pod to be evicted")
deletePDBOrDie(cs, ns, defaultName)
ginkgo.By("Trying to evict the same pod we tried earlier which should now be evictable")
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
})

View File

@ -30,7 +30,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/onsi/ginkgo"
v1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
policyv1 "k8s.io/api/policy/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"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")
} else if disruptOp == evictPod {
evictTarget := &policyv1beta1.Eviction{
evictTarget := &policyv1.Eviction{
ObjectMeta: metav1.ObjectMeta{
Name: host0Pod.Name,
Namespace: ns,
@ -431,7 +431,7 @@ var _ = utils.SIGDescribe("Pod Disks", func() {
}
ginkgo.By("evicting host0Pod")
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)
return false, nil
}

View File

@ -42,7 +42,7 @@ import (
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
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"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -1128,7 +1128,7 @@ func testPodBindingEviction(c *testContext) {
}).Do(context.TODO()).Error()
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()},
DeleteOptions: &forceDelete,
}).Do(context.TODO()).Error()

View File

@ -26,7 +26,7 @@ import (
coordination "k8s.io/api/coordination/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"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
@ -305,9 +305,9 @@ func TestNodeAuthorizer(t *testing.T) {
createNode2NormalPodEviction := func(client clientset.Interface) func() error {
return func() error {
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{
APIVersion: "policy/v1beta1",
APIVersion: "policy/v1",
Kind: "Eviction",
},
ObjectMeta: metav1.ObjectMeta{
@ -321,9 +321,9 @@ func TestNodeAuthorizer(t *testing.T) {
createNode2MirrorPodEviction := func(client clientset.Interface) func() error {
return func() error {
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{
APIVersion: "policy/v1beta1",
APIVersion: "policy/v1",
Kind: "Eviction",
},
ObjectMeta: metav1.ObjectMeta{

View File

@ -18,9 +18,11 @@ package evictions
import (
"context"
"encoding/json"
"fmt"
"net/http/httptest"
"reflect"
"strings"
"sync"
"sync/atomic"
"testing"
@ -31,6 +33,10 @@ import (
policyv1beta1 "k8s.io/api/policy/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
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"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
@ -110,10 +116,10 @@ func TestConcurrentEvictionRequests(t *testing.T) {
go func(id int, errCh chan error) {
defer wg.Done()
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) {
e := clientSet.PolicyV1beta1().Evictions(ns.Name).Evict(context.TODO(), eviction)
e := clientSet.PolicyV1().Evictions(ns.Name).Evict(context.TODO(), eviction)
switch {
case apierrors.IsTooManyRequests(e):
return false, nil
@ -219,9 +225,9 @@ func TestTerminalPodEviction(t *testing.T) {
t.Fatalf("Error while listing pod disruption budget")
}
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) {
e := clientSet.PolicyV1beta1().Evictions(ns.Name).Evict(context.TODO(), eviction)
e := clientSet.PolicyV1().Evictions(ns.Name).Evict(context.TODO(), eviction)
switch {
case apierrors.IsTooManyRequests(e):
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 {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
@ -309,10 +406,10 @@ func newPDB() *policyv1.PodDisruptionBudget {
}
}
func newV1beta1Eviction(ns, evictionName string, deleteOption metav1.DeleteOptions) *policyv1beta1.Eviction {
return &policyv1beta1.Eviction{
func newV1Eviction(ns, evictionName string, deleteOption metav1.DeleteOptions) *policyv1.Eviction {
return &policyv1.Eviction{
TypeMeta: metav1.TypeMeta{
APIVersion: "Policy/v1beta1",
APIVersion: "policy/v1",
Kind: "Eviction",
},
ObjectMeta: metav1.ObjectMeta{