什么是Printer Printer可以对Kubernetes中的资源按一定格式进行打印输出,Printer主要供Kubectl get命令使用。目前Kubectl主要实现了JsonPrinter, YAMLPrinter, NamePrinter, TemplatePrinter, JSONPathPrinter, CustomColumnsPrinter, VersionedPrinter, HumanReadablePrinter。
在对这些Printer进行分析之前,先来看下/pkg/kubectl/resource_printer.go的GetPrinter()函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
func  GetPrinter (format, formatArgument string , noHeaders bool )  (ResourcePrinter, bool , error)   {
	var  printer ResourcePrinter
	switch  format {
	case  "json" :
		printer = &JSONPrinter{}
	case  "yaml" :
		printer = &YAMLPrinter{}
	case  "name" :
		printer = &NamePrinter{
			
			Typer:   api.Scheme,
			Decoder: api.Codecs.UniversalDecoder(),
		}
	
	case  "template" , "go-template" :
		if  len (formatArgument) == 0  {
			return  nil , false , fmt.Errorf("template format specified but no template given" )
		}
		var  err error
		printer, err = NewTemplatePrinter([]byte (formatArgument))
		if  err != nil  {
			return  nil , false , fmt.Errorf("error parsing template %s, %v\n" , formatArgument, err)
		}
	case  "templatefile" , "go-template-file" :
		if  len (formatArgument) == 0  {
			return  nil , false , fmt.Errorf("templatefile format specified but no template file given" )
		}
		data, err := ioutil.ReadFile(formatArgument)
		if  err != nil  {
			return  nil , false , fmt.Errorf("error reading template %s, %v\n" , formatArgument, err)
		}
		printer, err = NewTemplatePrinter(data)
		if  err != nil  {
			return  nil , false , fmt.Errorf("error parsing template %s, %v\n" , string (data), err)
		}
	
	case  "jsonpath" :
		if  len (formatArgument) == 0  {
			return  nil , false , fmt.Errorf("jsonpath template format specified but no template given" )
		}
		var  err error
		printer, err = NewJSONPathPrinter(formatArgument)
		if  err != nil  {
			return  nil , false , fmt.Errorf("error parsing jsonpath %s, %v\n" , formatArgument, err)
		}
	case  "jsonpath-file" :
		if  len (formatArgument) == 0  {
			return  nil , false , fmt.Errorf("jsonpath file format specified but no template file file given" )
		}
		data, err := ioutil.ReadFile(formatArgument)
		if  err != nil  {
			return  nil , false , fmt.Errorf("error reading template %s, %v\n" , formatArgument, err)
		}
		printer, err = NewJSONPathPrinter(string (data))
		if  err != nil  {
			return  nil , false , fmt.Errorf("error parsing template %s, %v\n" , string (data), err)
		}
	
	case  "custom-columns" :
		var  err error
		if  printer, err = NewCustomColumnsPrinterFromSpec(formatArgument, api.Codecs.UniversalDecoder(), noHeaders); err != nil  {
			return  nil , false , err
		}
	case  "custom-columns-file" :
		file, err := os.Open(formatArgument)
		if  err != nil  {
			return  nil , false , fmt.Errorf("error reading template %s, %v\n" , formatArgument, err)
		}
		defer  file.Close()
		if  printer, err = NewCustomColumnsPrinterFromTemplate(file, api.Codecs.UniversalDecoder()); err != nil  {
			return  nil , false , err
		}
	case  "wide" :
		
		fallthrough 
	case  "" :
		return  nil , false , nil 
	default :
		return  nil , false , fmt.Errorf("output format %q not recognized" , format)
	}
	return  printer, true , nil 
}
在GetPrinter()函数中,根据传入的参数format,会生成不同类型的Printer。需要注意的是,除了”wide”, “”以外,其他的format类型都视为标准类型(generic)。非标准类型通常是和”人”相关,如”wide”和””最后都是由HumanReadablePrinter实现。
JSONPrinter JSONPrinter可以以JSON的格式打印Object。JSONPrinter调用了json.MarshalIndent()来对Obj进行JSON格式化打印。json.MarshalIndent()函数的功能和Marshal一致,只是把Object格式化成人性化阅读的JSON,有点类似”| python -mjson.tool”的功能。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
type  JSONPrinter struct  {
}
func  (p *JSONPrinter)  AfterPrint (w io.Writer, res string )  error   {
	return  nil 
}
func  (p *JSONPrinter)  PrintObj (obj runtime.Object, w io.Writer)  error   {
	switch  obj := obj.(type ) {
	case  *runtime.Unknown:
		var  buf bytes.Buffer
		err := json.Indent(&buf, obj.Raw, "" , "    " )
		if  err != nil  {
			return  err
		}
		buf.WriteRune('\n' )
		_, err = buf.WriteTo(w)
		return  err
	}
	data, err := json.MarshalIndent(obj, "" , "    " )
	if  err != nil  {
		return  err
	}
	data = append (data, '\n' )
	_, err = w.Write(data)
	return  err
}
func  (p *JSONPrinter)  HandledResources ()  []string   {
	return  []string {}
}
调用例子:1
kubectl get pods -o json
YAMLPrinter YAMLPrinter可以以YAML的格式打印Object。YAMLPrinter通过调用yaml.Marshal()把Object转换成YAML格式并进行打印。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
type  YAMLPrinter struct  {
	version   string 
	converter runtime.ObjectConvertor
}
func  (p *YAMLPrinter)  AfterPrint (w io.Writer, res string )  error   {
	return  nil 
}
func  (p *YAMLPrinter)  PrintObj (obj runtime.Object, w io.Writer)  error   {
	switch  obj := obj.(type ) {
	case  *runtime.Unknown:
		data, err := yaml.JSONToYAML(obj.Raw)
		if  err != nil  {
			return  err
		}
		_, err = w.Write(data)
		return  err
	}
	
	output, err := yaml.Marshal(obj)
	if  err != nil  {
		return  err
	}
	_, err = fmt.Fprint(w, string (output))
	return  err
}
调用例子:1
kubectl get pods -o yaml
NamePrinter NamePrinter可以打印资源类型和名称:如pod/ubuntu-ssh-589933015-2lhgb。NamePrinter先获判断Object是否为列表,如果是列表,则递归调用PrintObj;否则先获取Object的name,然后获取Object的Kind,并转换成单数形式的GVR,然后返回。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
type  NamePrinter struct  {
	Decoder runtime.Decoder
	Typer   runtime.ObjectTyper
}
func  (p *NamePrinter)  AfterPrint (w io.Writer, res string )  error   {
	return  nil 
}
func  (p *NamePrinter)  PrintObj (obj runtime.Object, w io.Writer)  error   {
	if  meta.IsListType(obj) {
		items, err := meta.ExtractList(obj)
		if  err != nil  {
			return  err
		}
		if  errs := runtime.DecodeList(items, p.Decoder, runtime.UnstructuredJSONScheme); len (errs) > 0  {
			return  utilerrors.NewAggregate(errs)
		}
		for  _, obj := range  items {
			if  err := p.PrintObj(obj, w); err != nil  {
				return  err
			}
		}
		return  nil 
	}
	name := "<unknown>" 
	
	if  acc, err := meta.Accessor(obj); err == nil  {
		if  n := acc.GetName(); len (n) > 0  {
			name = n
		}
	}
	if  kind := obj.GetObjectKind().GroupVersionKind(); len (kind.Kind) == 0  {
		
		
		
		if  gvks, _, err := p.Typer.ObjectKinds(obj); err == nil  {
			
			_, resource := meta.KindToResource(gvks[0 ])
			fmt.Fprintf(w, "%s/%s\n" , resource.Resource, name)
		} else  {
			fmt.Fprintf(w, "<unknown>/%s\n" , name)
		}
	} else  {
		
		
		_, resource := meta.KindToResource(kind)
		fmt.Fprintf(w, "%s/%s\n" , resource.Resource, name)
	}
	return  nil 
}
调用例子:1
2
3
#  kubectl get pods -o name
pod/nginx-1487191267-0t8x3
pod/ubuntu-ssh-589933015-2lhgb
TemplatePrinter TemplatePrinter基于txt/template包实现。txt/template一般的用法为: 定义模板:1
tmpl, err := template.New("test" ).Parse("hello, {{.}}" )
执行:1
err = tmpl.Execute(os.Stdout, name)
TemplatePrinter定义如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
type  TemplatePrinter struct  {
	rawTemplate string 
	template    *template.Template
}
func  NewTemplatePrinter (tmpl []byte )  (*TemplatePrinter, error)   {
	t, err := template.New("output" ).
		Funcs(template.FuncMap{"exists" : exists}).
		Parse(string (tmpl))
	if  err != nil  {
		return  nil , err
	}
	return  &TemplatePrinter{
		rawTemplate: string (tmpl),
		template:    t,
	}, nil 
}
func  (p *TemplatePrinter)  AfterPrint (w io.Writer, res string )  error   {
	return  nil 
}
func  (p *TemplatePrinter)  PrintObj (obj runtime.Object, w io.Writer)  error   {
	var  data []byte 
	var  err error
	if  unstructured, ok := obj.(*runtime.Unstructured); ok {
		data, err = json.Marshal(unstructured.Object)
	} else  {
		data, err = json.Marshal(obj)
	}
	if  err != nil  {
		return  err
	}
	out := map [string ]interface {}{}
	if  err := json.Unmarshal(data, &out); err != nil  {
		return  err
	}
	if  err = p.safeExecute(w, out); err != nil  {
		
		
		
		fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n" , err)
		fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n" , p.rawTemplate)
		fmt.Fprintf(w, "\traw data was:\n\t\t%v\n" , string (data))
		fmt.Fprintf(w, "\tobject given to template engine was:\n\t\t%+v\n\n" , out)
		return  fmt.Errorf("error executing template %q: %v" , p.rawTemplate, err)
	}
	return  nil 
}
func  (p *TemplatePrinter)  HandledResources ()  []string   {
	return  []string {}
}
调用例子:1
2
#  kubectl get pods nginx-1487191267-0t8x3 -o go-template="The name is {{.metadata.name}}"
The name is nginx-1487191267-0t8x3
或者1
2
#  kubectl get pods nginx-1487191267-0t8x3 -o go-template-file=template 
The name is nginx-1487191267-0t8x3
其中go-template-file=template的template为文件,内容为:1
The name is {{.metadata.name}}
JSONPathPrinter JSONPathPrinter实现了一套匹配规则,详见https://kubernetes.io/docs/user-guide/jsonpath/。  jsonpath具体在/pkg/util/jsonpath/jsonpath.go中实现,具体分析此处略。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
type  JSONPathPrinter struct  {
	rawTemplate string 
	*jsonpath.JSONPath
}
func  NewJSONPathPrinter (tmpl string )  (*JSONPathPrinter, error)   {
	j := jsonpath.New("out" )
	if  err := j.Parse(tmpl); err != nil  {
		return  nil , err
	}
	return  &JSONPathPrinter{tmpl, j}, nil 
}
func  (j *JSONPathPrinter)  AfterPrint (w io.Writer, res string )  error   {
	return  nil 
}
func  (j *JSONPathPrinter)  PrintObj (obj runtime.Object, w io.Writer)  error   {
	var  queryObj interface {} = obj
	if  meta.IsListType(obj) {
		data, err := json.Marshal(obj)
		if  err != nil  {
			return  err
		}
		queryObj = map [string ]interface {}{}
		if  err := json.Unmarshal(data, &queryObj); err != nil  {
			return  err
		}
	}
	if  unknown, ok := obj.(*runtime.Unknown); ok {
		data, err := json.Marshal(unknown)
		if  err != nil  {
			return  err
		}
		queryObj = map [string ]interface {}{}
		if  err := json.Unmarshal(data, &queryObj); err != nil  {
			return  err
		}
	}
	if  unstructured, ok := obj.(*runtime.Unstructured); ok {
		queryObj = unstructured.Object
	}
	if  err := j.JSONPath.Execute(w, queryObj); err != nil  {
		fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n" , err)
		fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n" , j.rawTemplate)
		fmt.Fprintf(w, "\tobject given to jsonpath engine was:\n\t\t%#v\n\n" , queryObj)
		return  fmt.Errorf("error executing jsonpath %q: %v\n" , j.rawTemplate, err)
	}
	return  nil 
}
func  (p *JSONPathPrinter)  HandledResources ()  []string   {
	return  []string {}
}
调用例子:1
2
#  kubectl get pods -o jsonpath={.items[*].metadata.name}
nginx-1487191267-0t8x3 ubuntu-ssh-589933015-2lhgb
或者1
2
#  kubectl get pods -o jsonpath-file=template
nginx-1487191267-0t8x3 ubuntu-ssh-589933015-2lhgb
其中jsonpath-file=template的template为文件,内容为:1
{.items[*].metadata.name}
CustomColumnsPrinter CustomColumnsPrinter允许用户定义列名,是JSONPath的加强版。支持从命令行直接输入模板和从文件读取模板两种方式。
读取命令行模板的CustomColumnsPrinter生成函数如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func  NewCustomColumnsPrinterFromSpec (spec string , decoder runtime.Decoder, noHeaders bool )  (*CustomColumnsPrinter, error)   {
	if  len (spec) == 0  {
		return  nil , fmt.Errorf("custom-columns format specified but no custom columns given" )
	}
	
	parts := strings.Split(spec, "," )
	columns := make ([]Column, len (parts))
	for  ix := range  parts {
		colSpec := strings.Split(parts[ix], ":" )
		if  len (colSpec) != 2  {
			return  nil , fmt.Errorf("unexpected custom-columns spec: %s, expected <header>:<json-path-expr>" , parts[ix])
		}
		spec, err := massageJSONPath(colSpec[1 ])
		if  err != nil  {
			return  nil , err
		}
		columns[ix] = Column{Header: colSpec[0 ], FieldSpec: spec}
	}
	return  &CustomColumnsPrinter{Columns: columns, Decoder: decoder, NoHeaders: noHeaders}, nil 
}
读取文件模板的CustomColumnsPrinter生成函数如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//***kubectl get pods -o custom-columns-file=/home/fankang/template***//
func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader, decoder runtime.Decoder) (*CustomColumnsPrinter, error) {
	scanner := bufio.NewScanner(templateReader)
	if !scanner.Scan() {
		return nil, fmt.Errorf("invalid template, missing header line. Expected format is one line of space separated headers, one line of space separated column specs.")
	}
	//***headers: [Type Name]***//
	headers := splitOnWhitespace(scanner.Text())
	if !scanner.Scan() {
		return nil, fmt.Errorf("invalid template, missing spec line. Expected format is one line of space separated headers, one line of space separated column specs.")
	}
	//***specs: [{.kind} {.metadata.name}]***//
	specs := splitOnWhitespace(scanner.Text())
	//***如果headers和specs长度不一致,则直接返回***//
	if len(headers) != len(specs) {
		return nil, fmt.Errorf("number of headers (%d) and field specifications (%d) don't match", len(headers), len(specs))
	}
	columns := make([]Column, len(headers))
	for ix := range headers {
		spec, err := massageJSONPath(specs[ix])
		if err != nil {
			return nil, err
		}
		//***组装成Column***//
		columns[ix] = Column{
			Header:    headers[ix],
			FieldSpec: spec,
		}
	}
	//***返回CustomColumnsPrinter***//
	return &CustomColumnsPrinter{Columns: columns, Decoder: decoder, NoHeaders: false}, nil
}
CustomColumnsPrinter的PrintObj()方法如下,可以看出,调用了jsonpath.New()来解析模板。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
	w := tabwriter.NewWriter(out, columnwidth, tabwidth, padding, padding_character, flags)
	if !s.NoHeaders {
		headers := make([]string, len(s.Columns))
		for ix := range s.Columns {
			headers[ix] = s.Columns[ix].Header
		}
		fmt.Fprintln(w, strings.Join(headers, "\t"))
	}
	parsers := make([]*jsonpath.JSONPath, len(s.Columns))
	for ix := range s.Columns {
		parsers[ix] = jsonpath.New(fmt.Sprintf("column%d", ix))
		if err := parsers[ix].Parse(s.Columns[ix].FieldSpec); err != nil {
			return err
		}
	}
	if meta.IsListType(obj) {
		objs, err := meta.ExtractList(obj)
		if err != nil {
			return err
		}
		for ix := range objs {
			if err := s.printOneObject(objs[ix], parsers, w); err != nil {
				return err
			}
		}
	} else {
		if err := s.printOneObject(obj, parsers, w); err != nil {
			return err
		}
	}
	return w.Flush()
}
调用例子:1
2
3
4
#  kubectl get pods -o custom-columns=Type:{.kind},Name:{.metadata.name}
Type      Name
Pod       nginx-1487191267-0t8x3
Pod       ubuntu-ssh-589933015-2lhgb
或者1
2
3
4
#  kubectl get pods -o custom-columns-file=template
Type      Name
Pod       nginx-1487191267-0t8x3
Pod       ubuntu-ssh-589933015-2lhgb
其中custom-columns-file=template的template为文件,内容为:1
2
Type              Name
{.kind}           {.metadata.name}
VersionedPrinter VersionedPrinter是对其他Printer的封装,先把Object转换成对应版本的Object,然后再封装的Printer进行打印。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// VersionedPrinter takes runtime objects and ensures they are converted to a given API version
// prior to being passed to a nested printer.
type VersionedPrinter struct {
	printer   ResourcePrinter
	converter runtime.ObjectConvertor
	versions  []unversioned.GroupVersion
}
// NewVersionedPrinter wraps a printer to convert objects to a known API version prior to printing.
func NewVersionedPrinter(printer ResourcePrinter, converter runtime.ObjectConvertor, versions ...unversioned.GroupVersion) ResourcePrinter {
	return &VersionedPrinter{
		printer:   printer,
		converter: converter,
		versions:  versions,
	}
}
func (p *VersionedPrinter) AfterPrint(w io.Writer, res string) error {
	return nil
}
// PrintObj implements ResourcePrinter
func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
	if len(p.versions) == 0 {
		return fmt.Errorf("no version specified, object cannot be converted")
	}
	converted, err := p.converter.ConvertToVersion(obj, unversioned.GroupVersions(p.versions))
	if err != nil {
		return err
	}
	return p.printer.PrintObj(converted, w)
}
version由kubectl get pods --output-version='v1' -o yaml -w的–output-version指定,但没什么作用,而且kubectl get代码中只有Watch会使用VersionedPrinter,所以还待更深入的研究。
HumanReadablePrinter HumanReadablePrinter可以”人性化”地输出内容。HumanReadablePrinter中有handlerMap,handlerMap记录了待打印的对象到处理函数的映射关系。系统中每种资源都有对应的打印函数。
其中kubectl -o wide调用的也是HumanReadablePrinter。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
type HumanReadablePrinter struct {
	handlerMap   map[reflect.Type]*handlerEntry
	options      PrintOptions
	lastType     reflect.Type
	hiddenObjNum int
}
// NewHumanReadablePrinter creates a HumanReadablePrinter.
//***创建HumanReadablePrinter***//
func NewHumanReadablePrinter(options PrintOptions) *HumanReadablePrinter {
	printer := &HumanReadablePrinter{
		handlerMap: make(map[reflect.Type]*handlerEntry),
		options:    options,
	}
	printer.addDefaultHandlers()
	return printer
}
//***打印第一行标题***//
func (h *HumanReadablePrinter) printHeader(columnNames []string, w io.Writer) error {
	if _, err := fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t")); err != nil {
		return err
	}
	return nil
}
//***打印Pod***//
func (h *HumanReadablePrinter) printPod(pod *api.Pod, w io.Writer, options PrintOptions) error {
	if err := printPodBase(pod, w, options); err != nil {
		return err
	}
	return nil
}
func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error {
	name := formatResourceName(options.Kind, pod.Name, options.WithKind)
	namespace := pod.Namespace
	restarts := 0
	totalContainers := len(pod.Spec.Containers)
	readyContainers := 0
	reason := string(pod.Status.Phase)
	if pod.Status.Reason != "" {
		reason = pod.Status.Reason
	}
	initializing := false
	for i := range pod.Status.InitContainerStatuses {
		container := pod.Status.InitContainerStatuses[i]
		restarts += int(container.RestartCount)
		switch {
		case container.State.Terminated != nil && container.State.Terminated.ExitCode == 0:
			continue
		case container.State.Terminated != nil:
			// initialization is failed
			if len(container.State.Terminated.Reason) == 0 {
				if container.State.Terminated.Signal != 0 {
					reason = fmt.Sprintf("Init:Signal:%d", container.State.Terminated.Signal)
				} else {
					reason = fmt.Sprintf("Init:ExitCode:%d", container.State.Terminated.ExitCode)
				}
			} else {
				reason = "Init:" + container.State.Terminated.Reason
			}
			initializing = true
		case container.State.Waiting != nil && len(container.State.Waiting.Reason) > 0 && container.State.Waiting.Reason != "PodInitializing":
			reason = "Init:" + container.State.Waiting.Reason
			initializing = true
		default:
			reason = fmt.Sprintf("Init:%d/%d", i, len(pod.Spec.InitContainers))
			initializing = true
		}
		break
	}
	if !initializing {
		restarts = 0
		for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- {
			container := pod.Status.ContainerStatuses[i]
			restarts += int(container.RestartCount)
			if container.State.Waiting != nil && container.State.Waiting.Reason != "" {
				reason = container.State.Waiting.Reason
			} else if container.State.Terminated != nil && container.State.Terminated.Reason != "" {
				reason = container.State.Terminated.Reason
			} else if container.State.Terminated != nil && container.State.Terminated.Reason == "" {
				if container.State.Terminated.Signal != 0 {
					reason = fmt.Sprintf("Signal:%d", container.State.Terminated.Signal)
				} else {
					reason = fmt.Sprintf("ExitCode:%d", container.State.Terminated.ExitCode)
				}
			} else if container.Ready && container.State.Running != nil {
				readyContainers++
			}
		}
	}
	if pod.DeletionTimestamp != nil && pod.Status.Reason == node.NodeUnreachablePodReason {
		reason = "Unknown"
	} else if pod.DeletionTimestamp != nil {
		reason = "Terminating"
	}
	if options.WithNamespace {
		if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
			return err
		}
	}
	if _, err := fmt.Fprintf(w, "%s\t%d/%d\t%s\t%d\t%s",
		name,
		readyContainers,
		totalContainers,
		reason,
		restarts,
		translateTimestamp(pod.CreationTimestamp),
	); err != nil {
		return err
	}
	//***-o wide则写入IP和NODE***//
	if options.Wide {
		nodeName := pod.Spec.NodeName
		podIP := pod.Status.PodIP
		if podIP == "" {
			podIP = "<none>"
		}
		if _, err := fmt.Fprintf(w, "\t%s\t%s",
			podIP,
			nodeName,
		); err != nil {
			return err
		}
	}
	if _, err := fmt.Fprint(w, AppendLabels(pod.Labels, options.ColumnLabels)); err != nil {
		return err
	}
	if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, pod.Labels)); err != nil {
		return err
	}
	return nil
}
调用例子:1
2
3
4
#  kubectl get pods
NAME                         READY     STATUS    RESTARTS   AGE
nginx-1487191267-0t8x3       1/1       Running   2          1d
ubuntu-ssh-589933015-2lhgb   1/1       Running   21         19d
或者1
2
3
4
#  kubectl get pods -o wide
NAME                         READY     STATUS    RESTARTS   AGE       IP           NODE
nginx-1487191267-0t8x3       1/1       Running   2          1d        172.17.0.4   fankang
ubuntu-ssh-589933015-2lhgb   1/1       Running   21         19d       172.17.0.5   fankang