Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support eviction and statefulset in kubectl drain #35483

Merged
merged 1 commit into from Nov 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions pkg/api/errors/errors.go
Expand Up @@ -413,6 +413,17 @@ func IsInternalError(err error) bool {
return reasonForError(err) == unversioned.StatusReasonInternalError
}

// IsTooManyRequests determines if err is an error which indicates that there are too many requests
// that the server cannot handle.
// TODO: update IsTooManyRequests() when the TooManyRequests(429) error returned from the API server has a non-empty Reason field
func IsTooManyRequests(err error) bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I missed something, why can't this be written in the same way as the functions above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TooManyRequest error returned in this case only has a status code with empty Reason field.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, could you open any issue and paste the err your received?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #36409.

switch t := err.(type) {
case APIStatus:
return t.Status().Code == StatusTooManyRequests
}
return false
}

// IsUnexpectedServerError returns true if the server response was not in the expected API format,
// and may be the result of another HTTP actor.
func IsUnexpectedServerError(err error) bool {
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/policy/types.go
Expand Up @@ -90,6 +90,9 @@ type PodDisruptionBudgetList struct {
Items []PodDisruptionBudget `json:"items"`
}

// +genclient=true
// +noMethods=true

// 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>/eviction.
Expand Down
Expand Up @@ -14,6 +14,8 @@ go_library(
name = "go_default_library",
srcs = [
"doc.go",
"eviction.go",
"eviction_expansion.go",
"generated_expansion.go",
"poddisruptionbudget.go",
"policy_client.go",
Expand Down
@@ -0,0 +1,46 @@
/*
Copyright 2016 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 internalversion

import (
restclient "k8s.io/kubernetes/pkg/client/restclient"
)

// 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 restclient.Interface
ns string
}

// newEvictions returns a Evictions
func newEvictions(c *PolicyClient, namespace string) *evictions {
return &evictions{
client: c.RESTClient(),
ns: namespace,
}
}
@@ -0,0 +1,38 @@
/*
Copyright 2016 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 internalversion

import (
policy "k8s.io/kubernetes/pkg/apis/policy"
)

// The EvictionExpansion interface allows manually adding extra methods to the ScaleInterface.
type EvictionExpansion interface {
Evict(eviction *policy.Eviction) error
}

func (c *evictions) Evict(eviction *policy.Eviction) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't hack the pkg/restclient.
Could you get the c.Client.(*restclient.RESTClient), modify its versionedAPIPath, use the modified RESTClient to send request?

Copy link
Member

@liggitt liggitt Nov 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are we posting scale subresources to different API groups? I'd expect evict to be done similarly. Hard coding policy API version and pods eviction subresources here seems wrong, and exporting the versioned URL from rest client definitely seems wrong

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@caesarxuchao Correct me if I'm wrong.
Based on our offline discussion with @caesarxuchao , we have thought about using a similar approach as scale subresource. But what we found is no one is actually calling Update() to post scale subresource.
And I guess if we try to call this function to post a scale, we will get a similar error as eviction.

return c.client.Post().
AbsPath("/api/v1").
Namespace(eviction.Namespace).
Resource("pods").
Name(eviction.Name).
SubResource("eviction").
Body(eviction).
Do().
Error()
}
Expand Up @@ -14,6 +14,8 @@ go_library(
name = "go_default_library",
srcs = [
"doc.go",
"fake_eviction.go",
"fake_eviction_expansion.go",
"fake_poddisruptionbudget.go",
"fake_policy_client.go",
],
Expand Down
@@ -0,0 +1,23 @@
/*
Copyright 2016 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

// FakeEvictions implements EvictionInterface
type FakeEvictions struct {
Fake *FakePolicy
ns string
}
@@ -0,0 +1,33 @@
/*
Copyright 2016 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 (
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
policy "k8s.io/kubernetes/pkg/apis/policy"
core "k8s.io/kubernetes/pkg/client/testing/core"
)

func (c *FakeEvictions) Evict(eviction *policy.Eviction) error {
action := core.GetActionImpl{}
action.Verb = "post"
action.Namespace = c.ns
action.Resource = unversioned.GroupVersionResource{Group: "", Version: "", Resource: "pods"}
action.Subresource = "eviction"
_, err := c.Fake.Invokes(action, eviction)
return err
}
Expand Up @@ -26,6 +26,10 @@ type FakePolicy struct {
*core.Fake
}

func (c *FakePolicy) Evictions(namespace string) internalversion.EvictionInterface {
return &FakeEvictions{c, namespace}
}

func (c *FakePolicy) PodDisruptionBudgets(namespace string) internalversion.PodDisruptionBudgetInterface {
return &FakePodDisruptionBudgets{c, namespace}
}
Expand Down
Expand Up @@ -24,6 +24,7 @@ import (

type PolicyInterface interface {
RESTClient() restclient.Interface
EvictionsGetter
PodDisruptionBudgetsGetter
}

Expand All @@ -32,6 +33,10 @@ type PolicyClient struct {
restClient restclient.Interface
}

func (c *PolicyClient) Evictions(namespace string) EvictionInterface {
return newEvictions(c, namespace)
}

func (c *PolicyClient) PodDisruptionBudgets(namespace string) PodDisruptionBudgetInterface {
return newPodDisruptionBudgets(c, namespace)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/client/listers/policy/internalversion/BUILD
Expand Up @@ -13,6 +13,7 @@ load(
go_library(
name = "go_default_library",
srcs = [
"eviction.go",
"expansion_generated.go",
"poddisruptionbudget.go",
],
Expand Down
94 changes: 94 additions & 0 deletions pkg/client/listers/policy/internalversion/eviction.go
@@ -0,0 +1,94 @@
/*
Copyright 2016 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.
*/

// This file was automatically generated by lister-gen with arguments: --input-dirs=[k8s.io/kubernetes/pkg/api,k8s.io/kubernetes/pkg/api/v1,k8s.io/kubernetes/pkg/apis/abac,k8s.io/kubernetes/pkg/apis/abac/v0,k8s.io/kubernetes/pkg/apis/abac/v1beta1,k8s.io/kubernetes/pkg/apis/apps,k8s.io/kubernetes/pkg/apis/apps/v1beta1,k8s.io/kubernetes/pkg/apis/authentication,k8s.io/kubernetes/pkg/apis/authentication/v1beta1,k8s.io/kubernetes/pkg/apis/authorization,k8s.io/kubernetes/pkg/apis/authorization/v1beta1,k8s.io/kubernetes/pkg/apis/autoscaling,k8s.io/kubernetes/pkg/apis/autoscaling/v1,k8s.io/kubernetes/pkg/apis/batch,k8s.io/kubernetes/pkg/apis/batch/v1,k8s.io/kubernetes/pkg/apis/batch/v2alpha1,k8s.io/kubernetes/pkg/apis/certificates,k8s.io/kubernetes/pkg/apis/certificates/v1alpha1,k8s.io/kubernetes/pkg/apis/componentconfig,k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1,k8s.io/kubernetes/pkg/apis/extensions,k8s.io/kubernetes/pkg/apis/extensions/v1beta1,k8s.io/kubernetes/pkg/apis/imagepolicy,k8s.io/kubernetes/pkg/apis/imagepolicy/v1alpha1,k8s.io/kubernetes/pkg/apis/policy,k8s.io/kubernetes/pkg/apis/policy/v1alpha1,k8s.io/kubernetes/pkg/apis/policy/v1beta1,k8s.io/kubernetes/pkg/apis/rbac,k8s.io/kubernetes/pkg/apis/rbac/v1alpha1,k8s.io/kubernetes/pkg/apis/storage,k8s.io/kubernetes/pkg/apis/storage/v1beta1]

package internalversion

import (
"k8s.io/kubernetes/pkg/api/errors"
policy "k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/labels"
)

// EvictionLister helps list Evictions.
type EvictionLister interface {
// List lists all Evictions in the indexer.
List(selector labels.Selector) (ret []*policy.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 []*policy.Eviction, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*policy.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.
type EvictionNamespaceLister interface {
// List lists all Evictions in the indexer for a given namespace.
List(selector labels.Selector) (ret []*policy.Eviction, err error)
// Get retrieves the Eviction from the indexer for a given namespace and name.
Get(name string) (*policy.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 []*policy.Eviction, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*policy.Eviction))
})
return ret, err
}

// Get retrieves the Eviction from the indexer for a given namespace and name.
func (s evictionNamespaceLister) Get(name string) (*policy.Eviction, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(policy.Resource("eviction"), name)
}
return obj.(*policy.Eviction), nil
}
Expand Up @@ -18,6 +18,14 @@ limitations under the License.

package internalversion

// EvictionListerExpansion allows custom methods to be added to
// EvictionLister.
type EvictionListerExpansion interface{}

// EvictionNamespaceListerExpansion allows custom methods to be added to
// EvictionNamespaeLister.
type EvictionNamespaceListerExpansion interface{}

// PodDisruptionBudgetListerExpansion allows custom methods to be added to
// PodDisruptionBudgetLister.
type PodDisruptionBudgetListerExpansion interface{}
Expand Down
2 changes: 2 additions & 0 deletions pkg/kubectl/cmd/BUILD
Expand Up @@ -70,6 +70,7 @@ go_library(
"//pkg/apimachinery/registered:go_default_library",
"//pkg/apis/batch/v1:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/restclient:go_default_library",
Expand Down Expand Up @@ -173,6 +174,7 @@ go_test(
"//pkg/apimachinery/registered:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/restclient/fake:go_default_library",
"//pkg/client/typed/dynamic:go_default_library",
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubectl/cmd/cmd.go
Expand Up @@ -255,7 +255,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
NewCmdTop(f, out, err),
NewCmdCordon(f, out),
NewCmdUncordon(f, out),
NewCmdDrain(f, out),
NewCmdDrain(f, out, err),
NewCmdTaint(f, out),
},
},
Expand Down