mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 18:24:07 +00:00
Merge pull request #36495 from mwielgus/kubectl_pdb
Automatic merge from submit-queue Support for PodDisruptionBudget in Kubectl Based on: https://github.com/kubernetes/kubernetes/pull/35287 cc: @davidopp @soltysh @wojtek-t
This commit is contained in:
commit
5d894d5164
@ -69,6 +69,7 @@ go_library(
|
|||||||
"//pkg/apis/batch/v2alpha1:go_default_library",
|
"//pkg/apis/batch/v2alpha1:go_default_library",
|
||||||
"//pkg/apis/certificates:go_default_library",
|
"//pkg/apis/certificates:go_default_library",
|
||||||
"//pkg/apis/extensions:go_default_library",
|
"//pkg/apis/extensions:go_default_library",
|
||||||
|
"//pkg/apis/policy:go_default_library",
|
||||||
"//pkg/apis/rbac:go_default_library",
|
"//pkg/apis/rbac:go_default_library",
|
||||||
"//pkg/apis/storage:go_default_library",
|
"//pkg/apis/storage:go_default_library",
|
||||||
"//pkg/apis/storage/util:go_default_library",
|
"//pkg/apis/storage/util:go_default_library",
|
||||||
@ -155,6 +156,7 @@ go_test(
|
|||||||
"//pkg/apimachinery/registered:go_default_library",
|
"//pkg/apimachinery/registered:go_default_library",
|
||||||
"//pkg/apis/batch:go_default_library",
|
"//pkg/apis/batch:go_default_library",
|
||||||
"//pkg/apis/extensions:go_default_library",
|
"//pkg/apis/extensions:go_default_library",
|
||||||
|
"//pkg/apis/policy:go_default_library",
|
||||||
"//pkg/apis/storage:go_default_library",
|
"//pkg/apis/storage:go_default_library",
|
||||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||||
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
|
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
|
||||||
|
@ -40,6 +40,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
"k8s.io/kubernetes/pkg/apis/certificates"
|
"k8s.io/kubernetes/pkg/apis/certificates"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
"k8s.io/kubernetes/pkg/apis/storage"
|
"k8s.io/kubernetes/pkg/apis/storage"
|
||||||
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
|
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
|
||||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
@ -120,6 +121,7 @@ func describerMap(c clientset.Interface) map[unversioned.GroupKind]Describer {
|
|||||||
apps.Kind("StatefulSet"): &StatefulSetDescriber{c},
|
apps.Kind("StatefulSet"): &StatefulSetDescriber{c},
|
||||||
certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c},
|
certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c},
|
||||||
storage.Kind("StorageClass"): &StorageClassDescriber{c},
|
storage.Kind("StorageClass"): &StorageClassDescriber{c},
|
||||||
|
policy.Kind("PodDisruptionBudget"): &PodDisruptionBudgetDescriber{c},
|
||||||
}
|
}
|
||||||
|
|
||||||
return m
|
return m
|
||||||
@ -2407,6 +2409,41 @@ func (s *StorageClassDescriber) Describe(namespace, name string, describerSettin
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PodDisruptionBudgetDescriber struct {
|
||||||
|
clientset.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PodDisruptionBudgetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
|
||||||
|
pdb, err := p.Policy().PodDisruptionBudgets(namespace).Get(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return tabbedString(func(out io.Writer) error {
|
||||||
|
fmt.Fprintf(out, "Name:\t%s\n", pdb.Name)
|
||||||
|
fmt.Fprintf(out, "Min available:\t%s\n", pdb.Spec.MinAvailable.String())
|
||||||
|
if pdb.Spec.Selector != nil {
|
||||||
|
fmt.Fprintf(out, "Selector:\t%s\n", unversioned.FormatLabelSelector(pdb.Spec.Selector))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(out, "Selector:\t<unset>\n")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "Status:\n")
|
||||||
|
fmt.Fprintf(out, " Allowed disruptions:\t%d\n", pdb.Status.PodDisruptionsAllowed)
|
||||||
|
fmt.Fprintf(out, " Current:\t%d\n", pdb.Status.CurrentHealthy)
|
||||||
|
fmt.Fprintf(out, " Desired:\t%d\n", pdb.Status.DesiredHealthy)
|
||||||
|
fmt.Fprintf(out, " Total:\t%d\n", pdb.Status.ExpectedPods)
|
||||||
|
if describerSettings.ShowEvents {
|
||||||
|
events, err := p.Core().Events(namespace).Search(pdb)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if events != nil {
|
||||||
|
DescribeEvents(events, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types.
|
// newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types.
|
||||||
func newErrNoDescriber(types ...reflect.Type) error {
|
func newErrNoDescriber(types ...reflect.Type) error {
|
||||||
names := make([]string, 0, len(types))
|
names := make([]string, 0, len(types))
|
||||||
|
@ -31,9 +31,11 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
"k8s.io/kubernetes/pkg/apis/storage"
|
"k8s.io/kubernetes/pkg/apis/storage"
|
||||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||||
|
"k8s.io/kubernetes/pkg/util/intstr"
|
||||||
)
|
)
|
||||||
|
|
||||||
type describeClient struct {
|
type describeClient struct {
|
||||||
@ -694,6 +696,30 @@ func TestDescribeStorageClass(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDescribePodDisruptionBudget(t *testing.T) {
|
||||||
|
f := fake.NewSimpleClientset(&policy.PodDisruptionBudget{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Namespace: "ns1",
|
||||||
|
Name: "pdb1",
|
||||||
|
CreationTimestamp: unversioned.Time{Time: time.Now().Add(1.9e9)},
|
||||||
|
},
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
MinAvailable: intstr.FromInt(22),
|
||||||
|
},
|
||||||
|
Status: policy.PodDisruptionBudgetStatus{
|
||||||
|
PodDisruptionsAllowed: 5,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
s := PodDisruptionBudgetDescriber{f}
|
||||||
|
out, err := s.Describe("ns1", "pdb1", DescriberSettings{ShowEvents: true})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, "pdb1") {
|
||||||
|
t.Errorf("unexpected out: %s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDescribeEvents(t *testing.T) {
|
func TestDescribeEvents(t *testing.T) {
|
||||||
|
|
||||||
events := &api.EventList{
|
events := &api.EventList{
|
||||||
|
@ -40,6 +40,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
"k8s.io/kubernetes/pkg/apis/certificates"
|
"k8s.io/kubernetes/pkg/apis/certificates"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||||
"k8s.io/kubernetes/pkg/apis/storage"
|
"k8s.io/kubernetes/pkg/apis/storage"
|
||||||
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
|
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
|
||||||
@ -474,6 +475,7 @@ func (h *HumanReadablePrinter) AfterPrint(output io.Writer, res string) error {
|
|||||||
var (
|
var (
|
||||||
podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"}
|
podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"}
|
||||||
podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"}
|
podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"}
|
||||||
|
podDisruptionBudgetColumns = []string{"NAME", "MIN-AVAILABLE", "ALLOWED-DISRUPTIONS", "AGE"}
|
||||||
replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
|
replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
|
||||||
replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
|
replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
|
||||||
jobColumns = []string{"NAME", "DESIRED", "SUCCESSFUL", "AGE"}
|
jobColumns = []string{"NAME", "DESIRED", "SUCCESSFUL", "AGE"}
|
||||||
@ -536,6 +538,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
|
|||||||
h.Handler(podColumns, h.printPod)
|
h.Handler(podColumns, h.printPod)
|
||||||
h.Handler(podTemplateColumns, printPodTemplate)
|
h.Handler(podTemplateColumns, printPodTemplate)
|
||||||
h.Handler(podTemplateColumns, printPodTemplateList)
|
h.Handler(podTemplateColumns, printPodTemplateList)
|
||||||
|
h.Handler(podDisruptionBudgetColumns, printPodDisruptionBudget)
|
||||||
|
h.Handler(podDisruptionBudgetColumns, printPodDisruptionBudgetList)
|
||||||
h.Handler(replicationControllerColumns, printReplicationController)
|
h.Handler(replicationControllerColumns, printReplicationController)
|
||||||
h.Handler(replicationControllerColumns, printReplicationControllerList)
|
h.Handler(replicationControllerColumns, printReplicationControllerList)
|
||||||
h.Handler(replicaSetColumns, printReplicaSet)
|
h.Handler(replicaSetColumns, printReplicaSet)
|
||||||
@ -828,6 +832,37 @@ func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, options Pri
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printPodDisruptionBudget(pdb *policy.PodDisruptionBudget, w io.Writer, options PrintOptions) error {
|
||||||
|
// name, minavailable, selector
|
||||||
|
name := formatResourceName(options.Kind, pdb.Name, options.WithKind)
|
||||||
|
namespace := pdb.Namespace
|
||||||
|
|
||||||
|
if options.WithNamespace {
|
||||||
|
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\n",
|
||||||
|
name,
|
||||||
|
pdb.Spec.MinAvailable.String(),
|
||||||
|
pdb.Status.PodDisruptionsAllowed,
|
||||||
|
translateTimestamp(pdb.CreationTimestamp),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printPodDisruptionBudgetList(pdbList *policy.PodDisruptionBudgetList, w io.Writer, options PrintOptions) error {
|
||||||
|
for _, pdb := range pdbList.Items {
|
||||||
|
if err := printPodDisruptionBudget(&pdb, w, options); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(AdoHe): try to put wide output in a single method
|
// TODO(AdoHe): try to put wide output in a single method
|
||||||
func printReplicationController(controller *api.ReplicationController, w io.Writer, options PrintOptions) error {
|
func printReplicationController(controller *api.ReplicationController, w io.Writer, options PrintOptions) error {
|
||||||
name := formatResourceName(options.Kind, controller.Name, options.WithKind)
|
name := formatResourceName(options.Kind, controller.Name, options.WithKind)
|
||||||
|
@ -33,6 +33,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing"
|
kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
yamlserializer "k8s.io/kubernetes/pkg/runtime/serializer/yaml"
|
yamlserializer "k8s.io/kubernetes/pkg/runtime/serializer/yaml"
|
||||||
@ -1622,3 +1623,35 @@ func TestPrintService(t *testing.T) {
|
|||||||
buf.Reset()
|
buf.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrintPodDisruptionBudget(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
pdb policy.PodDisruptionBudget
|
||||||
|
expect string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
policy.PodDisruptionBudget{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Namespace: "ns1",
|
||||||
|
Name: "pdb1",
|
||||||
|
CreationTimestamp: unversioned.Time{Time: time.Now().Add(1.9e9)},
|
||||||
|
},
|
||||||
|
Spec: policy.PodDisruptionBudgetSpec{
|
||||||
|
MinAvailable: intstr.FromInt(22),
|
||||||
|
},
|
||||||
|
Status: policy.PodDisruptionBudgetStatus{
|
||||||
|
PodDisruptionsAllowed: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"pdb1\t22\t5\t0s\n",
|
||||||
|
}}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
for _, test := range tests {
|
||||||
|
printPodDisruptionBudget(&test.pdb, buf, PrintOptions{false, false, false, false, true, false, false, "", []string{}})
|
||||||
|
if buf.String() != test.expect {
|
||||||
|
t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
|
||||||
|
}
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user