2014-10-06 01:24:19 +00:00
|
|
|
/*
|
|
|
|
Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
|
|
|
|
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 kubectl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2014-11-14 19:56:41 +00:00
|
|
|
"io"
|
|
|
|
"sort"
|
2014-10-06 01:24:19 +00:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
|
|
|
"github.com/golang/glog"
|
|
|
|
)
|
|
|
|
|
2014-10-27 19:56:34 +00:00
|
|
|
// Describer generates output for the named resource or an error
|
|
|
|
// if the output could not be generated.
|
|
|
|
type Describer interface {
|
|
|
|
Describe(namespace, name string) (output string, err error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Describer returns the default describe functions for each of the standard
|
|
|
|
// Kubernetes types.
|
|
|
|
func DescriberFor(kind string, c *client.Client) (Describer, bool) {
|
|
|
|
switch kind {
|
|
|
|
case "Pod":
|
2014-11-14 01:42:50 +00:00
|
|
|
return &PodDescriber{c}, true
|
2014-10-27 19:56:34 +00:00
|
|
|
case "ReplicationController":
|
2014-11-14 01:42:50 +00:00
|
|
|
return &ReplicationControllerDescriber{c}, true
|
2014-10-27 19:56:34 +00:00
|
|
|
case "Service":
|
2014-11-14 01:42:50 +00:00
|
|
|
return &ServiceDescriber{c}, true
|
|
|
|
case "Minion", "Node":
|
|
|
|
return &MinionDescriber{c}, true
|
2014-10-27 19:56:34 +00:00
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
// PodDescriber generates information about a pod and the replication controllers that
|
|
|
|
// create it.
|
|
|
|
type PodDescriber struct {
|
2014-11-14 01:42:50 +00:00
|
|
|
client.Interface
|
2014-10-27 19:56:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *PodDescriber) Describe(namespace, name string) (string, error) {
|
2014-11-14 01:42:50 +00:00
|
|
|
rc := d.ReplicationControllers(namespace)
|
|
|
|
pc := d.Pods(namespace)
|
2014-10-06 01:24:19 +00:00
|
|
|
|
2014-10-27 19:56:34 +00:00
|
|
|
pod, err := pc.Get(name)
|
2014-10-06 01:24:19 +00:00
|
|
|
if err != nil {
|
2014-11-14 19:56:41 +00:00
|
|
|
events, err2 := d.Events(namespace).List(
|
|
|
|
labels.Everything(),
|
|
|
|
labels.Set{
|
|
|
|
"involvedObject.name": name,
|
|
|
|
"involvedObject.kind": "Pod",
|
|
|
|
"involvedObject.namespace": namespace,
|
|
|
|
}.AsSelector(),
|
|
|
|
)
|
|
|
|
if err2 == nil && len(events.Items) > 0 {
|
|
|
|
return tabbedString(func(out io.Writer) error {
|
|
|
|
fmt.Fprintf(out, "Pod '%v': error '%v', but found events.\n", name, err)
|
|
|
|
describeEvents(events, out)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
2014-10-06 01:24:19 +00:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2014-11-07 02:09:46 +00:00
|
|
|
// TODO: remove me when pods are converted
|
|
|
|
spec := &api.PodSpec{}
|
|
|
|
if err := api.Scheme.Convert(&pod.DesiredState.Manifest, spec); err != nil {
|
|
|
|
glog.Errorf("Unable to convert pod manifest: %v", err)
|
|
|
|
}
|
|
|
|
|
2014-11-14 19:56:41 +00:00
|
|
|
events, _ := d.Events(namespace).Search(pod)
|
|
|
|
|
|
|
|
return tabbedString(func(out io.Writer) error {
|
2014-10-22 17:02:02 +00:00
|
|
|
fmt.Fprintf(out, "Name:\t%s\n", pod.Name)
|
2014-11-07 02:09:46 +00:00
|
|
|
fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(spec))
|
2014-10-06 01:24:19 +00:00
|
|
|
fmt.Fprintf(out, "Host:\t%s\n", pod.CurrentState.Host+"/"+pod.CurrentState.HostIP)
|
|
|
|
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(pod.Labels))
|
|
|
|
fmt.Fprintf(out, "Status:\t%s\n", string(pod.CurrentState.Status))
|
2014-10-27 19:56:34 +00:00
|
|
|
fmt.Fprintf(out, "Replication Controllers:\t%s\n", getReplicationControllersForLabels(rc, labels.Set(pod.Labels)))
|
2014-11-14 19:56:41 +00:00
|
|
|
if events != nil {
|
|
|
|
describeEvents(events, out)
|
|
|
|
}
|
2014-10-06 01:24:19 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-10-27 19:56:34 +00:00
|
|
|
// ReplicationControllerDescriber generates information about a replication controller
|
|
|
|
// and the pods it has created.
|
|
|
|
type ReplicationControllerDescriber struct {
|
2014-11-14 01:42:50 +00:00
|
|
|
client.Interface
|
2014-10-27 19:56:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *ReplicationControllerDescriber) Describe(namespace, name string) (string, error) {
|
2014-11-14 01:42:50 +00:00
|
|
|
rc := d.ReplicationControllers(namespace)
|
|
|
|
pc := d.Pods(namespace)
|
2014-10-06 01:24:19 +00:00
|
|
|
|
2014-10-27 19:56:34 +00:00
|
|
|
controller, err := rc.Get(name)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2014-11-05 15:22:54 +00:00
|
|
|
running, waiting, succeeded, failed, err := getPodStatusForReplicationController(pc, controller)
|
2014-10-06 01:24:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2014-11-14 19:56:41 +00:00
|
|
|
events, _ := d.Events(namespace).Search(controller)
|
|
|
|
|
|
|
|
return tabbedString(func(out io.Writer) error {
|
2014-10-22 17:02:02 +00:00
|
|
|
fmt.Fprintf(out, "Name:\t%s\n", controller.Name)
|
2014-11-07 02:09:46 +00:00
|
|
|
fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&controller.Spec.Template.Spec))
|
|
|
|
fmt.Fprintf(out, "Selector:\t%s\n", formatLabels(controller.Spec.Selector))
|
2014-10-22 17:02:02 +00:00
|
|
|
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(controller.Labels))
|
2014-11-07 02:09:46 +00:00
|
|
|
fmt.Fprintf(out, "Replicas:\t%d current / %d desired\n", controller.Status.Replicas, controller.Spec.Replicas)
|
2014-11-05 15:22:54 +00:00
|
|
|
fmt.Fprintf(out, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
|
2014-11-14 19:56:41 +00:00
|
|
|
if events != nil {
|
|
|
|
describeEvents(events, out)
|
|
|
|
}
|
2014-10-06 01:24:19 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-10-27 19:56:34 +00:00
|
|
|
// ServiceDescriber generates information about a service.
|
|
|
|
type ServiceDescriber struct {
|
2014-11-14 01:42:50 +00:00
|
|
|
client.Interface
|
2014-10-27 19:56:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *ServiceDescriber) Describe(namespace, name string) (string, error) {
|
2014-11-14 01:42:50 +00:00
|
|
|
c := d.Services(namespace)
|
2014-10-27 19:56:34 +00:00
|
|
|
|
|
|
|
service, err := c.Get(name)
|
2014-10-06 01:24:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2014-11-14 19:56:41 +00:00
|
|
|
events, _ := d.Events(namespace).Search(service)
|
|
|
|
|
|
|
|
return tabbedString(func(out io.Writer) error {
|
2014-10-22 17:02:02 +00:00
|
|
|
fmt.Fprintf(out, "Name:\t%s\n", service.Name)
|
|
|
|
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(service.Labels))
|
2014-10-30 13:29:11 +00:00
|
|
|
fmt.Fprintf(out, "Selector:\t%s\n", formatLabels(service.Spec.Selector))
|
|
|
|
fmt.Fprintf(out, "Port:\t%d\n", service.Spec.Port)
|
2014-11-14 19:56:41 +00:00
|
|
|
if events != nil {
|
|
|
|
describeEvents(events, out)
|
|
|
|
}
|
2014-10-06 01:24:19 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-10-27 19:56:34 +00:00
|
|
|
// MinionDescriber generates information about a minion.
|
|
|
|
type MinionDescriber struct {
|
2014-11-14 01:42:50 +00:00
|
|
|
client.Interface
|
2014-10-27 19:56:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *MinionDescriber) Describe(namespace, name string) (string, error) {
|
2014-11-14 01:42:50 +00:00
|
|
|
mc := d.Minions()
|
2014-10-27 19:56:34 +00:00
|
|
|
minion, err := mc.Get(name)
|
2014-10-06 01:24:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2014-11-14 19:56:41 +00:00
|
|
|
events, _ := d.Events(namespace).Search(minion)
|
|
|
|
|
|
|
|
return tabbedString(func(out io.Writer) error {
|
2014-10-22 17:02:02 +00:00
|
|
|
fmt.Fprintf(out, "Name:\t%s\n", minion.Name)
|
2014-11-14 19:56:41 +00:00
|
|
|
if events != nil {
|
|
|
|
describeEvents(events, out)
|
|
|
|
}
|
2014-10-06 01:24:19 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-11-14 19:56:41 +00:00
|
|
|
type sortableEvents []api.Event
|
|
|
|
|
|
|
|
func (s sortableEvents) Len() int { return len(s) }
|
|
|
|
func (s sortableEvents) Less(i, j int) bool { return s[i].Timestamp.Before(s[j].Timestamp.Time) }
|
|
|
|
func (s sortableEvents) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
|
|
|
|
|
|
func describeEvents(el *api.EventList, w io.Writer) {
|
|
|
|
if len(el.Items) == 0 {
|
|
|
|
fmt.Fprint(w, "No events.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
sort.Sort(sortableEvents(el.Items))
|
|
|
|
fmt.Fprint(w, "Events:\nFrom\tSubobjectPath\tStatus\tReason\tMessage\n")
|
|
|
|
for _, e := range el.Items {
|
|
|
|
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\n",
|
|
|
|
e.Source, e.InvolvedObject.FieldPath, e.Status, e.Reason, e.Message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-06 01:24:19 +00:00
|
|
|
// 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).
|
2014-10-27 19:56:34 +00:00
|
|
|
func getReplicationControllersForLabels(c client.ReplicationControllerInterface, labelsToMatch labels.Labels) string {
|
2014-10-06 01:24:19 +00:00
|
|
|
// Get all replication controllers.
|
2014-10-21 21:14:35 +00:00
|
|
|
// TODO this needs a namespace scope as argument
|
2014-10-27 19:56:34 +00:00
|
|
|
rcs, err := c.List(labels.Everything())
|
2014-10-06 01:24:19 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.Fatalf("Error getting replication controllers: %v\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the ones that match labelsToMatch.
|
|
|
|
var matchingRCs []api.ReplicationController
|
2014-10-22 17:02:02 +00:00
|
|
|
for _, controller := range rcs.Items {
|
2014-11-07 02:09:46 +00:00
|
|
|
selector := labels.SelectorFromSet(controller.Spec.Selector)
|
2014-10-06 01:24:19 +00:00
|
|
|
if selector.Matches(labelsToMatch) {
|
2014-10-22 17:02:02 +00:00
|
|
|
matchingRCs = append(matchingRCs, controller)
|
2014-10-06 01:24:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Format the matching RC's into strings.
|
|
|
|
var rcStrings []string
|
2014-10-22 17:02:02 +00:00
|
|
|
for _, controller := range matchingRCs {
|
2014-11-07 02:09:46 +00:00
|
|
|
rcStrings = append(rcStrings, fmt.Sprintf("%s (%d/%d replicas created)", controller.Name, controller.Status.Replicas, controller.Spec.Replicas))
|
2014-10-06 01:24:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
list := strings.Join(rcStrings, ", ")
|
|
|
|
if list == "" {
|
|
|
|
return "<none>"
|
|
|
|
}
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
2014-11-05 15:22:54 +00:00
|
|
|
func getPodStatusForReplicationController(c client.PodInterface, controller *api.ReplicationController) (running, waiting, succeeded, failed int, err error) {
|
2014-11-07 02:09:46 +00:00
|
|
|
rcPods, err := c.List(labels.SelectorFromSet(controller.Spec.Selector))
|
2014-10-06 01:24:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, pod := range rcPods.Items {
|
2014-11-07 07:11:21 +00:00
|
|
|
switch pod.CurrentState.Status {
|
|
|
|
case api.PodRunning:
|
2014-10-06 01:24:19 +00:00
|
|
|
running++
|
2014-11-07 07:11:21 +00:00
|
|
|
case api.PodPending:
|
2014-10-06 01:24:19 +00:00
|
|
|
waiting++
|
2014-11-07 07:11:21 +00:00
|
|
|
case api.PodSucceeded:
|
2014-11-05 15:22:54 +00:00
|
|
|
succeeded++
|
2014-11-07 07:11:21 +00:00
|
|
|
case api.PodFailed:
|
2014-11-05 15:22:54 +00:00
|
|
|
failed++
|
2014-10-06 01:24:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|