/
printers.go
217 lines (192 loc) · 5.81 KB
/
printers.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
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package printers
import (
"context"
"fmt"
"io"
"sort"
"golang.org/x/sync/errgroup"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"github.com/tohjustin/kube-lineage/internal/client"
"github.com/tohjustin/kube-lineage/internal/graph"
)
type sortableGroupKind []schema.GroupKind
func (s sortableGroupKind) Len() int { return len(s) }
func (s sortableGroupKind) Less(i, j int) bool { return lessGroupKind(s[i], s[j]) }
func (s sortableGroupKind) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func lessGroupKind(lhs, rhs schema.GroupKind) bool {
return lhs.String() < rhs.String()
}
type Interface interface {
Print(w io.Writer, nodeMap graph.NodeMap, rootUID types.UID, maxDepth uint, depsIsDependencies bool) error
}
type tablePrinter struct {
configFlags *HumanPrintFlags
outputFormat string
// client for fetching server-printed tables when printing in split output
// format
client client.Interface
}
func (p *tablePrinter) Print(w io.Writer, nodeMap graph.NodeMap, rootUID types.UID, maxDepth uint, depsIsDependencies bool) error {
root, ok := nodeMap[rootUID]
if !ok {
return fmt.Errorf("requested object (uid: %s) not found in list of fetched objects", rootUID)
}
if p.configFlags.IsSplitOutputFormat(p.outputFormat) {
if p.client == nil {
return fmt.Errorf("client must be provided to get server-printed tables")
}
return p.printTablesByGK(w, nodeMap, maxDepth)
}
return p.printTable(w, nodeMap, root, maxDepth, depsIsDependencies)
}
func (p *tablePrinter) printTable(w io.Writer, nodeMap graph.NodeMap, root *graph.Node, maxDepth uint, depsIsDependencies bool) error {
// Generate Table to print
showGroup := false
if sg := p.configFlags.ShowGroup; sg != nil {
showGroup = *sg
}
showGroupFn := createShowGroupFn(nodeMap, showGroup, maxDepth)
t, err := nodeMapToTable(nodeMap, root, maxDepth, depsIsDependencies, showGroupFn)
if err != nil {
return err
}
// Setup Table printer
p.configFlags.SetShowNamespace(shouldShowNamespace(nodeMap, maxDepth))
tableprinter, err := p.configFlags.ToPrinter(p.outputFormat)
if err != nil {
return err
}
return tableprinter.PrintObj(t, w)
}
func (p *tablePrinter) printTablesByGK(w io.Writer, nodeMap graph.NodeMap, maxDepth uint) error {
// Generate Tables to print
showGroup, showNamespace := false, false
if sg := p.configFlags.ShowGroup; sg != nil {
showGroup = *sg
}
if sg := p.configFlags.ShowNamespace; sg != nil {
showNamespace = *sg
}
showGroupFn := createShowGroupFn(nodeMap, showGroup, maxDepth)
showNamespaceFn := createShowNamespaceFn(nodeMap, showNamespace, maxDepth)
tListByGK, err := p.nodeMapToTableByGK(nodeMap, maxDepth)
if err != nil {
return err
}
// Sort Tables by GroupKind
var gkList sortableGroupKind
for gk := range tListByGK {
gkList = append(gkList, gk)
}
sort.Sort(gkList)
for ix, gk := range gkList {
if t, ok := tListByGK[gk]; ok {
// Setup Table printer
tgk := gk
if !showGroupFn(gk.Kind) {
tgk = schema.GroupKind{Kind: gk.Kind}
}
p.configFlags.SetShowNamespace(showNamespaceFn(gk))
tableprinter, err := p.configFlags.ToPrinterWithGK(p.outputFormat, tgk)
if err != nil {
return err
}
// Setup Table printer
err = tableprinter.PrintObj(t, w)
if err != nil {
return err
}
if ix != len(gkList)-1 {
fmt.Fprintf(w, "\n")
}
}
}
return nil
}
//nolint:funlen,gocognit
func (p *tablePrinter) nodeMapToTableByGK(nodeMap graph.NodeMap, maxDepth uint) (map[schema.GroupKind](*metav1.Table), error) {
// Filter objects to print based on depth
objUIDs := []types.UID{}
for uid, node := range nodeMap {
if maxDepth == 0 || node.Depth <= maxDepth {
objUIDs = append(objUIDs, uid)
}
}
// Group objects by GroupKind & Namespace
nodesByGKAndNS := map[schema.GroupKind](map[string]graph.NodeList){}
for _, uid := range objUIDs {
if node, ok := nodeMap[uid]; ok {
gk := schema.GroupKind{Group: node.Group, Kind: node.Kind}
ns := node.Namespace
if _, ok := nodesByGKAndNS[gk]; !ok {
nodesByGKAndNS[gk] = map[string]graph.NodeList{}
}
nodesByGKAndNS[gk][ns] = append(nodesByGKAndNS[gk][ns], node)
}
}
// Fan-out to get server-print tables for all objects
eg, ctx := errgroup.WithContext(context.Background())
tableByGKAndNS := map[schema.GroupKind](map[string]*metav1.Table){}
for gk, nodesByNS := range nodesByGKAndNS {
if len(gk.Kind) == 0 {
continue
}
for ns, nodes := range nodesByNS {
if len(nodes) == 0 {
continue
}
gk, api, ns, names := gk, client.APIResource(nodes[0].GetAPIResource()), ns, []string{}
for _, n := range nodes {
names = append(names, n.Name)
}
// Sort TableRows by name
sortedNames := sets.NewString(names...).List()
eg.Go(func() error {
table, err := p.client.GetTable(ctx, client.GetTableOptions{
APIResource: api,
Namespace: ns,
Names: sortedNames,
})
if err != nil || table == nil {
return err
}
if _, ok := tableByGKAndNS[gk]; !ok {
tableByGKAndNS[gk] = map[string]*metav1.Table{}
}
if t, ok := tableByGKAndNS[gk][ns]; !ok {
tableByGKAndNS[gk][ns] = table
} else {
t.Rows = append(t.Rows, table.Rows...)
}
return nil
})
}
}
if err := eg.Wait(); err != nil {
return nil, err
}
// Sort TableRows by namespace
tableByGK := map[schema.GroupKind]*metav1.Table{}
for gk, tableByNS := range tableByGKAndNS {
var nsList []string
for ns := range tableByNS {
nsList = append(nsList, ns)
}
sortedNSList := sets.NewString(nsList...).List()
var table *metav1.Table
for _, ns := range sortedNSList {
if t, ok := tableByNS[ns]; ok {
if table == nil {
table = t
} else {
table.Rows = append(table.Rows, t.Rows...)
}
}
}
tableByGK[gk] = table
}
return tableByGK, nil
}