diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 6902e6e3c40..2d6683ceac9 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -333,6 +333,7 @@ _kubectl_get() must_have_one_noun+=("pod") must_have_one_noun+=("podsecuritypolicy") must_have_one_noun+=("podtemplate") + must_have_one_noun+=("replicaset") must_have_one_noun+=("replicationcontroller") must_have_one_noun+=("resourcequota") must_have_one_noun+=("secret") @@ -399,6 +400,7 @@ _kubectl_describe() must_have_one_noun+=("persistentvolume") must_have_one_noun+=("persistentvolumeclaim") must_have_one_noun+=("pod") + must_have_one_noun+=("replicaset") must_have_one_noun+=("replicationcontroller") must_have_one_noun+=("resourcequota") must_have_one_noun+=("secret") @@ -831,6 +833,7 @@ _kubectl_delete() must_have_one_noun+=("pod") must_have_one_noun+=("podsecuritypolicy") must_have_one_noun+=("podtemplate") + must_have_one_noun+=("replicaset") must_have_one_noun+=("replicationcontroller") must_have_one_noun+=("resourcequota") must_have_one_noun+=("secret") @@ -1975,6 +1978,7 @@ _kubectl_label() must_have_one_noun+=("pod") must_have_one_noun+=("podsecuritypolicy") must_have_one_noun+=("podtemplate") + must_have_one_noun+=("replicaset") must_have_one_noun+=("replicationcontroller") must_have_one_noun+=("resourcequota") must_have_one_noun+=("secret") diff --git a/pkg/api/unversioned/helpers.go b/pkg/api/unversioned/helpers.go index c8451a96bbe..066832dc145 100644 --- a/pkg/api/unversioned/helpers.go +++ b/pkg/api/unversioned/helpers.go @@ -78,3 +78,17 @@ func SetAsLabelSelector(ls labels.Set) *LabelSelector { return selector } + +// FormatLabelSelector convert labelSelector into plain string +func FormatLabelSelector(labelSelector *LabelSelector) string { + var l string + if selector, err := LabelSelectorAsSelector(labelSelector); err != nil { + l = selector.String() + } else { + l = "" + } + if l == "" { + l = "" + } + return l +} diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index a48dac48aa9..db792e9b465 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -87,6 +87,7 @@ 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("DaemonSet"): &DaemonSetDescriber{c}, extensions.Kind("Deployment"): &DeploymentDescriber{clientset.FromUnversionedClient(c)}, @@ -421,10 +422,7 @@ type PodDescriber struct { } func (d *PodDescriber) Describe(namespace, name string) (string, error) { - rc := d.ReplicationControllers(namespace) - pc := d.Pods(namespace) - - pod, err := pc.Get(name) + pod, err := d.Pods(namespace).Get(name) if err != nil { eventsInterface := d.Events(namespace) selector := eventsInterface.GetFieldSelector(&name, &namespace, nil, nil) @@ -448,15 +446,10 @@ func (d *PodDescriber) Describe(namespace, name string) (string, error) { events, _ = d.Events(namespace).Search(ref) } - rcs, err := getReplicationControllersForLabels(rc, labels.Set(pod.Labels)) - if err != nil { - return "", err - } - - return describePod(pod, rcs, events) + return describePod(pod, events) } -func describePod(pod *api.Pod, rcs []api.ReplicationController, events *api.EventList) (string, error) { +func describePod(pod *api.Pod, events *api.EventList) (string, error) { return tabbedString(func(out io.Writer) error { fmt.Fprintf(out, "Name:\t%s\n", pod.Name) fmt.Fprintf(out, "Namespace:\t%s\n", pod.Namespace) @@ -475,10 +468,6 @@ func describePod(pod *api.Pod, rcs []api.ReplicationController, events *api.Even fmt.Fprintf(out, "Reason:\t%s\n", pod.Status.Reason) fmt.Fprintf(out, "Message:\t%s\n", pod.Status.Message) fmt.Fprintf(out, "IP:\t%s\n", pod.Status.PodIP) - var matchingRCs []*api.ReplicationController - for _, rc := range rcs { - matchingRCs = append(matchingRCs, &rc) - } fmt.Fprintf(out, "Controllers:\t%s\n", printControllers(pod.Annotations)) fmt.Fprintf(out, "Containers:\n") describeContainers(pod, out) @@ -899,6 +888,58 @@ func DescribePodTemplate(template *api.PodTemplateSpec) (string, error) { }) } +// ReplicaSetDescriber generates information about a ReplicaSet and the pods it has created. +type ReplicaSetDescriber struct { + client.Interface +} + +func (d *ReplicaSetDescriber) Describe(namespace, name string) (string, error) { + rsc := d.Extensions().ReplicaSets(namespace) + pc := d.Pods(namespace) + + rs, err := rsc.Get(name) + if err != nil { + return "", err + } + + selector, err := unversioned.LabelSelectorAsSelector(rs.Spec.Selector) + if err != nil { + return "", err + } + + running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector) + if err != nil { + return "", err + } + + events, _ := d.Events(namespace).Search(rs) + + return describeReplicaSet(rs, events, running, waiting, succeeded, failed) +} + +func describeReplicaSet(rs *extensions.ReplicaSet, events *api.EventList, running, waiting, succeeded, failed int) (string, error) { + return tabbedString(func(out io.Writer) error { + fmt.Fprintf(out, "Name:\t%s\n", rs.Name) + fmt.Fprintf(out, "Namespace:\t%s\n", rs.Namespace) + if rs.Spec.Template != nil { + fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&rs.Spec.Template.Spec)) + } else { + fmt.Fprintf(out, "Image(s):\t%s\n", "") + } + fmt.Fprintf(out, "Selector:\t%s\n", unversioned.FormatLabelSelector(rs.Spec.Selector)) + fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(rs.Labels)) + fmt.Fprintf(out, "Replicas:\t%d current / %d desired\n", rs.Status.Replicas, rs.Spec.Replicas) + fmt.Fprintf(out, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed) + if rs.Spec.Template != nil { + describeVolumes(rs.Spec.Template.Spec.Volumes, out) + } + if events != nil { + DescribeEvents(events, out) + } + return nil + }) +} + // JobDescriber generates information about a job and the pods it has created. type JobDescriber struct { client *client.Client @@ -1668,29 +1709,6 @@ func getDaemonSetsForLabels(c client.DaemonSetInterface, labelsToMatch labels.La return matchingDaemonSets, nil } -// Get all replication controllers whose selectors would match a given set of -// labels. -// TODO Move this to pkg/client and ideally implement it server-side (instead -// of getting all RC's and searching through them manually). -func getReplicationControllersForLabels(c client.ReplicationControllerInterface, labelsToMatch labels.Labels) ([]api.ReplicationController, error) { - // Get all replication controllers. - // TODO this needs a namespace scope as argument - rcs, err := c.List(api.ListOptions{}) - if err != nil { - return nil, fmt.Errorf("error getting replication controllers: %v", err) - } - - // Find the ones that match labelsToMatch. - var matchingRCs []api.ReplicationController - for _, controller := range rcs.Items { - selector := labels.SelectorFromSet(controller.Spec.Selector) - if selector.Matches(labelsToMatch) { - matchingRCs = append(matchingRCs, controller) - } - } - return matchingRCs, nil -} - func printReplicationControllersByLabels(matchingRCs []*api.ReplicationController) string { // Format the matching RC's into strings. var rcStrings []string diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index d3c4b6ff00c..633ee2ade6e 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -397,6 +397,7 @@ func (h *HumanReadablePrinter) HandledResources() []string { var podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"} var podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"} var replicationControllerColumns = []string{"CONTROLLER", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "REPLICAS", "AGE"} +var replicaSetColumns = []string{"CONTROLLER", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "REPLICAS", "AGE"} var jobColumns = []string{"JOB", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "SUCCESSFUL"} var serviceColumns = []string{"NAME", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "SELECTOR", "AGE"} var ingressColumns = []string{"NAME", "RULE", "BACKEND", "ADDRESS"} @@ -427,6 +428,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() { h.Handler(podTemplateColumns, printPodTemplateList) h.Handler(replicationControllerColumns, printReplicationController) h.Handler(replicationControllerColumns, printReplicationControllerList) + h.Handler(replicaSetColumns, printReplicaSet) + h.Handler(replicaSetColumns, printReplicaSetList) h.Handler(daemonSetColumns, printDaemonSet) h.Handler(daemonSetColumns, printDaemonSetList) h.Handler(jobColumns, printJob) @@ -743,6 +746,63 @@ func printReplicationControllerList(list *api.ReplicationControllerList, w io.Wr return nil } +func printReplicaSet(rs *extensions.ReplicaSet, w io.Writer, options PrintOptions) error { + name := rs.Name + namespace := rs.Namespace + containers := rs.Spec.Template.Spec.Containers + var firstContainer api.Container + if len(containers) > 0 { + firstContainer, containers = containers[0], containers[1:] + } + + if options.WithNamespace { + if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil { + return err + } + } + if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s", + name, + firstContainer.Name, + firstContainer.Image, + unversioned.FormatLabelSelector(rs.Spec.Selector), + rs.Spec.Replicas, + translateTimestamp(rs.CreationTimestamp), + ); err != nil { + return err + } + if _, err := fmt.Fprint(w, appendLabels(rs.Labels, options.ColumnLabels)); err != nil { + return err + } + if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, rs.Labels)); err != nil { + return err + } + + // Lay out all the other containers on separate lines. + extraLinePrefix := "\t" + if options.WithNamespace { + extraLinePrefix = "\t\t" + } + for _, container := range containers { + _, err := fmt.Fprintf(w, "%s%s\t%s\t%s\t%s", extraLinePrefix, container.Name, container.Image, "", "") + if err != nil { + return err + } + if _, err := fmt.Fprint(w, appendLabelTabs(options.ColumnLabels)); err != nil { + return err + } + } + return nil +} + +func printReplicaSetList(list *extensions.ReplicaSetList, w io.Writer, options PrintOptions) error { + for _, rs := range list.Items { + if err := printReplicaSet(&rs, w, options); err != nil { + return err + } + } + return nil +} + func printJob(job *extensions.Job, w io.Writer, options PrintOptions) error { name := job.Name namespace := job.Namespace