diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index 98281ad122b..1187d7b31f0 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -40,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/storage" storageutil "k8s.io/kubernetes/pkg/apis/storage/util" 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}, certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c}, storage.Kind("StorageClass"): &StorageClassDescriber{c}, + policy.Kind("PodDisruptionBudget"): &PodDisruptionBudgetDescriber{c}, } return m @@ -2395,6 +2397,40 @@ 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\n") + } + fmt.Fprintf(out, "Status:\n") + 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. func newErrNoDescriber(types ...reflect.Type) error { names := make([]string, 0, len(types)) diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index cb136f8d192..2eebb6f1cd5 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -40,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/storage" storageutil "k8s.io/kubernetes/pkg/apis/storage/util" @@ -474,6 +475,7 @@ func (h *HumanReadablePrinter) AfterPrint(output io.Writer, res string) error { var ( podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"} podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"} + podDisruptionBudgetColumns = []string{"NAME", "MIN-AVAILABLE", "SELECTOR", "DISRUPTIONS"} replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"} replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"} jobColumns = []string{"NAME", "DESIRED", "SUCCESSFUL", "AGE"} @@ -536,6 +538,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() { h.Handler(podColumns, h.printPod) h.Handler(podTemplateColumns, printPodTemplate) h.Handler(podTemplateColumns, printPodTemplateList) + h.Handler(podDisruptionBudgetColumns, printPodDisruptionBudget) + h.Handler(podDisruptionBudgetColumns, printPodDisruptionBudgetList) h.Handler(replicationControllerColumns, printReplicationController) h.Handler(replicationControllerColumns, printReplicationControllerList) h.Handler(replicaSetColumns, printReplicaSet) @@ -828,6 +832,46 @@ func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, options Pri 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 + } + } + + selector := "" + if pdb.Spec.Selector != nil { + selector = unversioned.FormatLabelSelector(pdb.Spec.Selector) + } + disruptions := "Disallowed" + if pdb.Status.PodDisruptionAllowed { + disruptions = "Allowed" + } + if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", + name, + pdb.Spec.MinAvailable.String(), + selector, + disruptions, + ); 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 func printReplicationController(controller *api.ReplicationController, w io.Writer, options PrintOptions) error { name := formatResourceName(options.Kind, controller.Name, options.WithKind)