1 Star 0 Fork 0

zhuchance / kubernetes

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
describe.go 18.32 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
/*
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"
"io"
"reflect"
"sort"
"strings"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/golang/glog"
)
// Describer generates output for the named resource or an error
// if the output could not be generated. Implementors typically
// abstract the retrieval of the named object from a remote server.
type Describer interface {
Describe(namespace, name string) (output string, err error)
}
// ObjectDescriber is an interface for displaying arbitrary objects with extra
// information. Use when an object is in hand (on disk, or already retrieved).
// Implementors may ignore the additional information passed on extra, or use it
// by default. ObjectDescribers may return ErrNoDescriber if no suitable describer
// is found.
type ObjectDescriber interface {
DescribeObject(object interface{}, extra ...interface{}) (output string, err error)
}
// ErrNoDescriber is a structured error indicating the provided object or objects
// cannot be described.
type ErrNoDescriber struct {
Types []string
}
// Error implements the error interface.
func (e ErrNoDescriber) Error() string {
return fmt.Sprintf("no describer has been defined for %v", e.Types)
}
// 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":
return &PodDescriber{c}, true
case "ReplicationController":
return &ReplicationControllerDescriber{c}, true
case "Service":
return &ServiceDescriber{c}, true
case "Minion", "Node":
return &NodeDescriber{c}, true
case "LimitRange":
return &LimitRangeDescriber{c}, true
case "ResourceQuota":
return &ResourceQuotaDescriber{c}, true
}
return nil, false
}
// DefaultObjectDescriber can describe the default Kubernetes objects.
var DefaultObjectDescriber ObjectDescriber
func init() {
d := &Describers{}
err := d.Add(
describeLimitRange,
describeQuota,
describePod,
describeService,
describeReplicationController,
describeNode,
)
if err != nil {
glog.Fatalf("Cannot register describers: %v", err)
}
DefaultObjectDescriber = d
}
// LimitRangeDescriber generates information about a limit range
type LimitRangeDescriber struct {
client.Interface
}
func (d *LimitRangeDescriber) Describe(namespace, name string) (string, error) {
lr := d.LimitRanges(namespace)
limitRange, err := lr.Get(name)
if err != nil {
return "", err
}
return describeLimitRange(limitRange)
}
func describeLimitRange(limitRange *api.LimitRange) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", limitRange.Name)
fmt.Fprintf(out, "Type\tResource\tMin\tMax\n")
fmt.Fprintf(out, "----\t--------\t---\t---\n")
for i := range limitRange.Spec.Limits {
item := limitRange.Spec.Limits[i]
maxResources := item.Max
minResources := item.Min
set := map[api.ResourceName]bool{}
for k := range maxResources {
set[k] = true
}
for k := range minResources {
set[k] = true
}
for k := range set {
// if no value is set, we output -
maxValue := "-"
minValue := "-"
maxQuantity, maxQuantityFound := maxResources[k]
if maxQuantityFound {
maxValue = maxQuantity.String()
}
minQuantity, minQuantityFound := minResources[k]
if minQuantityFound {
minValue = minQuantity.String()
}
msg := "%v\t%v\t%v\t%v\n"
fmt.Fprintf(out, msg, item.Type, k, minValue, maxValue)
}
}
return nil
})
}
// ResourceQuotaDescriber generates information about a resource quota
type ResourceQuotaDescriber struct {
client.Interface
}
func (d *ResourceQuotaDescriber) Describe(namespace, name string) (string, error) {
rq := d.ResourceQuotas(namespace)
resourceQuota, err := rq.Get(name)
if err != nil {
return "", err
}
return describeQuota(resourceQuota)
}
func describeQuota(resourceQuota *api.ResourceQuota) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", resourceQuota.Name)
fmt.Fprintf(out, "Resource\tUsed\tHard\n")
fmt.Fprintf(out, "--------\t----\t----\n")
resources := []api.ResourceName{}
for resource := range resourceQuota.Status.Hard {
resources = append(resources, resource)
}
sort.Sort(SortableResourceNames(resources))
msg := "%v\t%v\t%v\n"
for i := range resources {
resource := resources[i]
hardQuantity := resourceQuota.Status.Hard[resource]
usedQuantity := resourceQuota.Status.Used[resource]
fmt.Fprintf(out, msg, resource, usedQuantity.String(), hardQuantity.String())
}
return nil
})
}
// PodDescriber generates information about a pod and the replication controllers that
// create it.
type PodDescriber struct {
client.Interface
}
func (d *PodDescriber) Describe(namespace, name string) (string, error) {
rc := d.ReplicationControllers(namespace)
pc := d.Pods(namespace)
pod, err := pc.Get(name)
if err != nil {
events, err2 := d.Events(namespace).List(
labels.Everything(),
labels.Set{
"involvedObject.name": name,
"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
})
}
return "", err
}
var events *api.EventList
if ref, err := api.GetReference(pod); err != nil {
glog.Errorf("Unable to construct reference to '%#v': %v", pod, err)
} else {
ref.Kind = "" // Find BoundPod objects, too!
events, _ = d.Events(namespace).Search(ref)
}
rcs, err := getReplicationControllersForLabels(rc, labels.Set(pod.Labels))
if err != nil {
return "", err
}
return describePod(pod, rcs, events)
}
func describePod(pod *api.Pod, rcs []api.ReplicationController, events *api.EventList) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", pod.Name)
fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&pod.Spec))
fmt.Fprintf(out, "Host:\t%s\n", pod.Status.Host+"/"+pod.Status.HostIP)
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(pod.Labels))
fmt.Fprintf(out, "Status:\t%s\n", string(pod.Status.Phase))
fmt.Fprintf(out, "Replication Controllers:\t%s\n", printReplicationControllersByLabels(rcs))
if len(pod.Status.Conditions) > 0 {
fmt.Fprint(out, "Conditions:\n Type\tStatus\n")
for _, c := range pod.Status.Conditions {
fmt.Fprintf(out, " %v \t%v \n",
c.Type,
c.Status)
}
}
if events != nil {
describeEvents(events, out)
}
return nil
})
}
// ReplicationControllerDescriber generates information about a replication controller
// and the pods it has created.
type ReplicationControllerDescriber struct {
client.Interface
}
func (d *ReplicationControllerDescriber) Describe(namespace, name string) (string, error) {
rc := d.ReplicationControllers(namespace)
pc := d.Pods(namespace)
controller, err := rc.Get(name)
if err != nil {
return "", err
}
running, waiting, succeeded, failed, err := getPodStatusForReplicationController(pc, controller)
if err != nil {
return "", err
}
events, _ := d.Events(namespace).Search(controller)
return describeReplicationController(controller, events, running, waiting, succeeded, failed)
}
func describeReplicationController(controller *api.ReplicationController, events *api.EventList, running, waiting, succeeded, failed int) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", controller.Name)
if controller.Spec.Template != nil {
fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&controller.Spec.Template.Spec))
} else {
fmt.Fprintf(out, "Image(s):\t%s\n", "<no template>")
}
fmt.Fprintf(out, "Selector:\t%s\n", formatLabels(controller.Spec.Selector))
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(controller.Labels))
fmt.Fprintf(out, "Replicas:\t%d current / %d desired\n", controller.Status.Replicas, controller.Spec.Replicas)
fmt.Fprintf(out, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
if events != nil {
describeEvents(events, out)
}
return nil
})
}
// ServiceDescriber generates information about a service.
type ServiceDescriber struct {
client.Interface
}
func (d *ServiceDescriber) Describe(namespace, name string) (string, error) {
c := d.Services(namespace)
service, err := c.Get(name)
if err != nil {
return "", err
}
endpoints, _ := d.Endpoints(namespace).Get(name)
events, _ := d.Events(namespace).Search(service)
return describeService(service, endpoints, events)
}
func describeService(service *api.Service, endpoints *api.Endpoints, events *api.EventList) (string, error) {
if endpoints == nil {
endpoints = &api.Endpoints{}
}
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", service.Name)
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(service.Labels))
fmt.Fprintf(out, "Selector:\t%s\n", formatLabels(service.Spec.Selector))
fmt.Fprintf(out, "IP:\t%s\n", service.Spec.PortalIP)
if len(service.Spec.PublicIPs) > 0 {
list := strings.Join(service.Spec.PublicIPs, ", ")
fmt.Fprintf(out, "Public IPs:\t%s\n", list)
}
fmt.Fprintf(out, "Port:\t%d\n", service.Spec.Port)
fmt.Fprintf(out, "Endpoints:\t%s\n", formatEndpoints(endpoints.Endpoints))
fmt.Fprintf(out, "Session Affinity:\t%s\n", service.Spec.SessionAffinity)
if events != nil {
describeEvents(events, out)
}
return nil
})
}
// NodeDescriber generates information about a node.
type NodeDescriber struct {
client.Interface
}
func (d *NodeDescriber) Describe(namespace, name string) (string, error) {
mc := d.Nodes()
node, err := mc.Get(name)
if err != nil {
return "", err
}
var pods []api.Pod
allPods, err := d.Pods(namespace).List(labels.Everything())
if err != nil {
return "", err
}
for _, pod := range allPods.Items {
if pod.Status.Host != name {
continue
}
pods = append(pods, pod)
}
events, _ := d.Events(namespace).Search(node)
return describeNode(node, pods, events)
}
func describeNode(node *api.Node, pods []api.Pod, events *api.EventList) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", node.Name)
if len(node.Status.Conditions) > 0 {
fmt.Fprint(out, "Conditions:\n Type\tStatus\tLastProbeTime\tLastTransitionTime\tReason\tMessage\n")
for _, c := range node.Status.Conditions {
fmt.Fprintf(out, " %v \t%v \t%s \t%s \t%v \t%v\n",
c.Type,
c.Status,
c.LastProbeTime.Time.Format(time.RFC1123Z),
c.LastTransitionTime.Time.Format(time.RFC1123Z),
c.Reason,
c.Message)
}
}
var addresses []string
for _, address := range node.Status.Addresses {
addresses = append(addresses, address.Address)
}
fmt.Fprintf(out, "Addresses:\t%s\n", strings.Join(addresses, ","))
if len(node.Spec.Capacity) > 0 {
fmt.Fprintf(out, "Capacity:\n")
for resource, value := range node.Spec.Capacity {
fmt.Fprintf(out, " %s:\t%s\n", resource, value.String())
}
}
if len(node.Spec.PodCIDR) > 0 {
fmt.Fprintf(out, "PodCIDR:\t%s\n", node.Spec.PodCIDR)
}
if len(node.Spec.ExternalID) > 0 {
fmt.Fprintf(out, "ExternalID:\t%s\n", node.Spec.ExternalID)
}
fmt.Fprintf(out, "Pods:\t(%d in total)\n", len(pods))
for _, pod := range pods {
fmt.Fprintf(out, " %s\n", pod.Name)
}
if events != nil {
describeEvents(events, out)
}
return nil
})
}
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:\n FirstSeen\tLastSeen\tCount\tFrom\tSubobjectPath\tReason\tMessage\n")
for _, e := range el.Items {
fmt.Fprintf(w, " %s\t%s\t%d\t%v\t%v\t%v\t%v\n",
e.FirstTimestamp.Time.Format(time.RFC1123Z),
e.LastTimestamp.Time.Format(time.RFC1123Z),
e.Count,
e.Source,
e.InvolvedObject.FieldPath,
e.Reason,
e.Message)
}
}
// 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(labels.Everything())
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
for _, controller := range matchingRCs {
rcStrings = append(rcStrings, fmt.Sprintf("%s (%d/%d replicas created)", controller.Name, controller.Status.Replicas, controller.Spec.Replicas))
}
list := strings.Join(rcStrings, ", ")
if list == "" {
return "<none>"
}
return list
}
func getPodStatusForReplicationController(c client.PodInterface, controller *api.ReplicationController) (running, waiting, succeeded, failed int, err error) {
rcPods, err := c.List(labels.SelectorFromSet(controller.Spec.Selector))
if err != nil {
return
}
for _, pod := range rcPods.Items {
switch pod.Status.Phase {
case api.PodRunning:
running++
case api.PodPending:
waiting++
case api.PodSucceeded:
succeeded++
case api.PodFailed:
failed++
}
}
return
}
// newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types.
func newErrNoDescriber(types ...reflect.Type) error {
names := []string{}
for _, t := range types {
names = append(names, t.String())
}
return ErrNoDescriber{Types: names}
}
// Describers implements ObjectDescriber against functions registered via Add. Those functions can
// be strongly typed. Types are exactly matched (no conversion or assignable checks).
type Describers struct {
searchFns map[reflect.Type][]typeFunc
}
// DescribeObject implements ObjectDescriber and will attempt to print the provided object to a string,
// if at least one describer function has been registered with the exact types passed, or if any
// describer can print the exact object in its first argument (the remainder will be provided empty
// values). If no function registered with Add can satisfy the passed objects, an ErrNoDescriber will
// be returned
// TODO: reorder and partial match extra.
func (d *Describers) DescribeObject(exact interface{}, extra ...interface{}) (string, error) {
exactType := reflect.TypeOf(exact)
fns, ok := d.searchFns[exactType]
if !ok {
return "", newErrNoDescriber(exactType)
}
if len(extra) == 0 {
for _, typeFn := range fns {
if len(typeFn.Extra) == 0 {
return typeFn.Describe(exact, extra...)
}
}
typeFn := fns[0]
for _, t := range typeFn.Extra {
v := reflect.New(t).Elem()
extra = append(extra, v.Interface())
}
return fns[0].Describe(exact, extra...)
}
types := []reflect.Type{}
for _, obj := range extra {
types = append(types, reflect.TypeOf(obj))
}
for _, typeFn := range fns {
if typeFn.Matches(types) {
return typeFn.Describe(exact, extra...)
}
}
return "", newErrNoDescriber(append([]reflect.Type{exactType}, types...)...)
}
// Add adds one or more describer functions to the Describer. The passed function must
// match the signature:
//
// func(...) (string, error)
//
// Any number of arguments may be provided.
func (d *Describers) Add(fns ...interface{}) error {
for _, fn := range fns {
fv := reflect.ValueOf(fn)
ft := fv.Type()
if ft.Kind() != reflect.Func {
return fmt.Errorf("expected func, got: %v", ft)
}
if ft.NumIn() == 0 {
return fmt.Errorf("expected at least one 'in' params, got: %v", ft)
}
if ft.NumOut() != 2 {
return fmt.Errorf("expected two 'out' params - (string, error), got: %v", ft)
}
types := []reflect.Type{}
for i := 0; i < ft.NumIn(); i++ {
types = append(types, ft.In(i))
}
if ft.Out(0) != reflect.TypeOf(string("")) {
return fmt.Errorf("expected string return, got: %v", ft)
}
var forErrorType error
// This convolution is necessary, otherwise TypeOf picks up on the fact
// that forErrorType is nil.
errorType := reflect.TypeOf(&forErrorType).Elem()
if ft.Out(1) != errorType {
return fmt.Errorf("expected error return, got: %v", ft)
}
exact := types[0]
extra := types[1:]
if d.searchFns == nil {
d.searchFns = make(map[reflect.Type][]typeFunc)
}
fns := d.searchFns[exact]
fn := typeFunc{Extra: extra, Fn: fv}
fns = append(fns, fn)
d.searchFns[exact] = fns
}
return nil
}
// typeFunc holds information about a describer function and the types it accepts
type typeFunc struct {
Extra []reflect.Type
Fn reflect.Value
}
// Matches returns true when the passed types exactly match the Extra list.
// TODO: allow unordered types to be matched and reorderd.
func (fn typeFunc) Matches(types []reflect.Type) bool {
if len(fn.Extra) != len(types) {
return false
}
for i := range types {
if fn.Extra[i] != types[i] {
return false
}
}
return true
}
// Describe invokes the nested function with the exact number of arguments.
func (fn typeFunc) Describe(exact interface{}, extra ...interface{}) (string, error) {
values := []reflect.Value{reflect.ValueOf(exact)}
for _, obj := range extra {
values = append(values, reflect.ValueOf(obj))
}
out := fn.Fn.Call(values)
s := out[0].Interface().(string)
var err error
if !out[1].IsNil() {
err = out[1].Interface().(error)
}
return s, err
}
Go
1
https://gitee.com/meoom/kubernetes.git
git@gitee.com:meoom/kubernetes.git
meoom
kubernetes
kubernetes
v0.13.2

搜索帮助