1
2
3
4 package table
5
6 import (
7 "fmt"
8 "io"
9 "time"
10
11 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12 "k8s.io/utils/integer"
13 "sigs.k8s.io/cli-utils/pkg/print/common"
14 )
15
16
17
18
19
20 type ColumnDef struct {
21 ColumnName string
22 ColumnHeader string
23 ColumnWidth int
24 PrintResourceFunc func(w io.Writer, width int, r Resource) (int, error)
25 }
26
27
28 func (c ColumnDef) Name() string {
29 return c.ColumnName
30 }
31
32
33
34 func (c ColumnDef) Header() string {
35 return c.ColumnHeader
36 }
37
38
39 func (c ColumnDef) Width() int {
40 return c.ColumnWidth
41 }
42
43
44
45
46 func (c ColumnDef) PrintResource(w io.Writer, width int, r Resource) (int, error) {
47 return c.PrintResourceFunc(w, width, r)
48 }
49
50
51
52 func MustColumn(name string) ColumnDef {
53 c, found := columnDefinitions[name]
54 if !found {
55 panic(fmt.Errorf("unknown column name %q", name))
56 }
57 return c
58 }
59
60 var (
61 columnDefinitions = map[string]ColumnDef{
62
63
64 "namespace": {
65 ColumnName: "namespace",
66 ColumnHeader: "NAMESPACE",
67 ColumnWidth: 10,
68 PrintResourceFunc: func(w io.Writer, width int, r Resource) (int,
69 error) {
70 namespace := r.Identifier().Namespace
71 if len(namespace) > width {
72 namespace = namespace[:width]
73 }
74 _, err := fmt.Fprint(w, namespace)
75 return len(namespace), err
76 },
77 },
78
79
80 "resource": {
81 ColumnName: "resource",
82 ColumnHeader: "RESOURCE",
83 ColumnWidth: 40,
84 PrintResourceFunc: func(w io.Writer, width int, r Resource) (int,
85 error) {
86 text := fmt.Sprintf("%s/%s", r.Identifier().GroupKind.Kind,
87 r.Identifier().Name)
88 if len(text) > width {
89 text = text[:width]
90 }
91 _, err := fmt.Fprint(w, text)
92 return len(text), err
93 },
94 },
95
96
97 "status": {
98 ColumnName: "status",
99 ColumnHeader: "STATUS",
100 ColumnWidth: 10,
101 PrintResourceFunc: func(w io.Writer, width int, r Resource) (int,
102 error) {
103 rs := r.ResourceStatus()
104 if rs == nil {
105 return 0, nil
106 }
107 s := rs.Status.String()
108 if len(s) > width {
109 s = s[:width]
110 }
111 color, setColor := common.ColorForStatus(rs.Status)
112 var outputStatus string
113 if setColor {
114 outputStatus = common.SprintfWithColor(color, s)
115 } else {
116 outputStatus = s
117 }
118 _, err := fmt.Fprint(w, outputStatus)
119 return len(s), err
120 },
121 },
122
123
124 "conditions": {
125 ColumnName: "conditions",
126 ColumnHeader: "CONDITIONS",
127 ColumnWidth: 40,
128 PrintResourceFunc: func(w io.Writer, width int, r Resource) (int,
129 error) {
130 rs := r.ResourceStatus()
131 if rs == nil {
132 return 0, nil
133 }
134 u := rs.Resource
135 if u == nil {
136 return fmt.Fprintf(w, "-")
137 }
138
139 conditions, found, err := unstructured.NestedSlice(u.Object,
140 "status", "conditions")
141 if !found || err != nil || len(conditions) == 0 {
142 return fmt.Fprintf(w, "<None>")
143 }
144
145 realLength := 0
146 for i, cond := range conditions {
147 condition := cond.(map[string]interface{})
148 conditionType := condition["type"].(string)
149 conditionStatus := condition["status"].(string)
150 var color common.Color
151 switch conditionStatus {
152 case "True":
153 color = common.GREEN
154 case "False":
155 color = common.RED
156 default:
157 color = common.YELLOW
158 }
159 remainingWidth := width - realLength
160 if len(conditionType) > remainingWidth {
161 conditionType = conditionType[:remainingWidth]
162 }
163 _, err := fmt.Fprint(w, common.SprintfWithColor(color, conditionType))
164 if err != nil {
165 return realLength, err
166 }
167 realLength += len(conditionType)
168 if i < len(conditions)-1 && width-realLength > 2 {
169 _, err = fmt.Fprintf(w, ",")
170 if err != nil {
171 return realLength, err
172 }
173 realLength++
174 }
175 }
176 return realLength, nil
177 },
178 },
179
180
181 "age": {
182 ColumnName: "age",
183 ColumnHeader: "AGE",
184 ColumnWidth: 6,
185 PrintResourceFunc: func(w io.Writer, width int, r Resource) (i int, err error) {
186 rs := r.ResourceStatus()
187 if rs == nil {
188 return 0, nil
189 }
190 u := rs.Resource
191 if u == nil {
192 return fmt.Fprint(w, "-")
193 }
194
195 timestamp, found, err := unstructured.NestedString(u.Object,
196 "metadata", "creationTimestamp")
197 if !found || err != nil || timestamp == "" {
198 return fmt.Fprint(w, "-")
199 }
200 parsedTime, err := time.Parse(time.RFC3339, timestamp)
201 if err != nil {
202 return fmt.Fprint(w, "-")
203 }
204 age := time.Since(parsedTime)
205 switch {
206 case age.Seconds() <= 90:
207 return fmt.Fprintf(w, "%ds",
208 integer.RoundToInt32(age.Round(time.Second).Seconds()))
209 case age.Minutes() <= 90:
210 return fmt.Fprintf(w, "%dm",
211 integer.RoundToInt32(age.Round(time.Minute).Minutes()))
212 default:
213 return fmt.Fprintf(w, "%dh",
214 integer.RoundToInt32(age.Round(time.Hour).Hours()))
215 }
216 },
217 },
218
219
220
221 "message": {
222 ColumnName: "message",
223 ColumnHeader: "MESSAGE",
224 ColumnWidth: 40,
225 PrintResourceFunc: func(w io.Writer, width int, r Resource) (i int, err error) {
226 rs := r.ResourceStatus()
227 if rs == nil {
228 return 0, nil
229 }
230 var message string
231 if rs.Error != nil {
232 message = rs.Error.Error()
233 } else {
234 message = rs.Message
235 }
236 if len(message) > width {
237 message = message[:width]
238 }
239 return fmt.Fprint(w, message)
240 },
241 },
242 }
243 )
244
View as plain text