diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index 5b6aebb4118..ccf9db8c12d 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -28,7 +28,6 @@ import ( "strings" "time" - "github.com/golang/glog" "k8s.io/kubernetes/federation/apis/federation" fed_clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset" "k8s.io/kubernetes/pkg/api" @@ -38,6 +37,7 @@ import ( "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/batch" + "k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/extensions" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" client "k8s.io/kubernetes/pkg/client/unversioned" @@ -48,8 +48,11 @@ import ( "k8s.io/kubernetes/pkg/kubelet/qos" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/types" + utilcertificates "k8s.io/kubernetes/pkg/util/certificates" "k8s.io/kubernetes/pkg/util/intstr" "k8s.io/kubernetes/pkg/util/sets" + + "github.com/golang/glog" ) // Describer generates output for the named resource or an error @@ -101,17 +104,18 @@ func describerMap(c *client.Client) map[unversioned.GroupKind]Describer { api.Kind("Endpoints"): &EndpointsDescriber{c}, api.Kind("ConfigMap"): &ConfigMapDescriber{c}, - extensions.Kind("ReplicaSet"): &ReplicaSetDescriber{c}, - extensions.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c}, - extensions.Kind("NetworkPolicy"): &NetworkPolicyDescriber{c}, - autoscaling.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c}, - extensions.Kind("DaemonSet"): &DaemonSetDescriber{c}, - extensions.Kind("Deployment"): &DeploymentDescriber{adapter.FromUnversionedClient(c)}, - extensions.Kind("Job"): &JobDescriber{c}, - batch.Kind("Job"): &JobDescriber{c}, - batch.Kind("ScheduledJob"): &ScheduledJobDescriber{adapter.FromUnversionedClient(c)}, - apps.Kind("PetSet"): &PetSetDescriber{c}, - extensions.Kind("Ingress"): &IngressDescriber{c}, + extensions.Kind("ReplicaSet"): &ReplicaSetDescriber{c}, + extensions.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c}, + extensions.Kind("NetworkPolicy"): &NetworkPolicyDescriber{c}, + autoscaling.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c}, + extensions.Kind("DaemonSet"): &DaemonSetDescriber{c}, + extensions.Kind("Deployment"): &DeploymentDescriber{adapter.FromUnversionedClient(c)}, + extensions.Kind("Job"): &JobDescriber{c}, + extensions.Kind("Ingress"): &IngressDescriber{c}, + batch.Kind("Job"): &JobDescriber{c}, + batch.Kind("ScheduledJob"): &ScheduledJobDescriber{adapter.FromUnversionedClient(c)}, + apps.Kind("PetSet"): &PetSetDescriber{c}, + certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c}, } return m @@ -1883,6 +1887,74 @@ func (p *PetSetDescriber) Describe(namespace, name string, describerSettings Des }) } +type CertificateSigningRequestDescriber struct { + client *client.Client +} + +func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { + csr, err := p.client.Certificates().CertificateSigningRequests().Get(name) + if err != nil { + return "", err + } + + cr, err := utilcertificates.ParseCertificateRequestObject(csr) + if err != nil { + return "", fmt.Errorf("Error parsing CSR: %v", err) + } + status, err := extractCSRStatus(csr) + if err != nil { + return "", err + } + + printListHelper := func(out io.Writer, prefix, name string, values []string) { + if len(values) == 0 { + return + } + fmt.Fprintf(out, prefix+name+":\t") + fmt.Fprintf(out, strings.Join(values, "\n"+prefix+"\t")) + fmt.Fprintf(out, "\n") + } + + return tabbedString(func(out io.Writer) error { + fmt.Fprintf(out, "Name:\t%s\n", csr.Name) + fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(csr.Labels)) + fmt.Fprintf(out, "Annotations:\t%s\n", labels.FormatLabels(csr.Annotations)) + fmt.Fprintf(out, "CreationTimestamp:\t%s\n", csr.CreationTimestamp.Time.Format(time.RFC1123Z)) + fmt.Fprintf(out, "Requesting User:\t%s\n", csr.Spec.Username) + fmt.Fprintf(out, "Status:\t%s\n", status) + + fmt.Fprintf(out, "Subject:\n") + fmt.Fprintf(out, "\tCommon Name:\t%s\n", cr.Subject.CommonName) + fmt.Fprintf(out, "\tSerial Number:\t%s\n", cr.Subject.SerialNumber) + printListHelper(out, "\t", "Organization", cr.Subject.Organization) + printListHelper(out, "\t", "Organizational Unit", cr.Subject.OrganizationalUnit) + printListHelper(out, "\t", "Country", cr.Subject.Country) + printListHelper(out, "\t", "Locality", cr.Subject.Locality) + printListHelper(out, "\t", "Province", cr.Subject.Province) + printListHelper(out, "\t", "StreetAddress", cr.Subject.StreetAddress) + printListHelper(out, "\t", "PostalCode", cr.Subject.PostalCode) + + if len(cr.DNSNames)+len(cr.EmailAddresses)+len(cr.IPAddresses) > 0 { + fmt.Fprintf(out, "Subject Alternative Names:\n") + printListHelper(out, "\t", "DNS Names", cr.DNSNames) + printListHelper(out, "\t", "Email Addresses", cr.EmailAddresses) + var ipaddrs []string + for _, ipaddr := range cr.IPAddresses { + ipaddrs = append(ipaddrs, ipaddr.String()) + } + printListHelper(out, "\t", "IP Addresses", ipaddrs) + } + + if describerSettings.ShowEvents { + events, _ := p.client.Events(namespace).Search(csr) + if events != nil { + DescribeEvents(events, out) + } + } + return nil + }) +} + // HorizontalPodAutoscalerDescriber generates information about a horizontal pod autoscaler. type HorizontalPodAutoscalerDescriber struct { client *client.Client diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 04d6990d466..ddf99d0ebb1 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -39,6 +39,7 @@ import ( "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/batch" + "k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/labels" @@ -468,6 +469,7 @@ var configMapColumns = []string{"NAME", "DATA", "AGE"} var podSecurityPolicyColumns = []string{"NAME", "PRIV", "CAPS", "VOLUMEPLUGINS", "SELINUX", "RUNASUSER"} var clusterColumns = []string{"NAME", "STATUS", "VERSION", "AGE"} var networkPolicyColumns = []string{"NAME", "POD-SELECTOR", "AGE"} +var certificateSigningRequestColumns = []string{"NAME", "AGE", "REQUESTOR", "CONDITION"} // addDefaultHandlers adds print handlers for default Kubernetes types. func (h *HumanReadablePrinter) addDefaultHandlers() { @@ -537,6 +539,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() { h.Handler(clusterRoleColumns, printClusterRoleList) h.Handler(clusterRoleBindingColumns, printClusterRoleBinding) h.Handler(clusterRoleBindingColumns, printClusterRoleBindingList) + h.Handler(certificateSigningRequestColumns, printCertificateSigningRequest) + h.Handler(certificateSigningRequestColumns, printCertificateSigningRequestList) } func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error { @@ -1695,6 +1699,67 @@ func printClusterRoleBindingList(list *rbac.ClusterRoleBindingList, w io.Writer, return nil } +func printCertificateSigningRequest(csr *certificates.CertificateSigningRequest, w io.Writer, options PrintOptions) error { + name := formatResourceName(options.Kind, csr.Name, options.WithKind) + meta := csr.ObjectMeta + + status, err := extractCSRStatus(csr) + if err != nil { + return err + } + + if _, err := fmt.Fprintf( + w, "%s\t%s\t%s\t%s", + name, + translateTimestamp(meta.CreationTimestamp), + csr.Spec.Username, + status, + ); err != nil { + return err + } + if _, err := fmt.Fprint(w, AppendLabels(meta.Labels, options.ColumnLabels)); err != nil { + return err + } + _, err = fmt.Fprint(w, AppendAllLabels(options.ShowLabels, meta.Labels)) + return err +} + +func extractCSRStatus(csr *certificates.CertificateSigningRequest) (string, error) { + var approved, denied bool + for _, c := range csr.Status.Conditions { + switch c.Type { + case certificates.CertificateApproved: + approved = true + case certificates.CertificateDenied: + denied = true + default: + return "", fmt.Errorf("unknown csr conditon %q", c) + } + } + var status string + // must be in order of presidence + if denied { + status += "Denied" + } else if approved { + status += "Approved" + } else { + status += "Pending" + } + if len(csr.Status.Certificate) > 0 { + status += ",Issued" + } + return status, nil +} + +func printCertificateSigningRequestList(list *certificates.CertificateSigningRequestList, w io.Writer, options PrintOptions) error { + for i := range list.Items { + if err := printCertificateSigningRequest(&list.Items[i], w, options); err != nil { + return err + } + } + return nil +} + func printComponentStatus(item *api.ComponentStatus, w io.Writer, options PrintOptions) error { name := formatResourceName(options.Kind, item.Name, options.WithKind)