/
explain.go
119 lines (108 loc) · 3.3 KB
/
explain.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
package swagger
import (
"fmt"
"github.com/mattfenwick/collections/pkg/slice"
"github.com/olekukonko/tablewriter"
"github.com/pkg/errors"
"golang.org/x/exp/maps"
"strings"
)
type ExplainArgs struct {
Format string
ApiVersions []string
Resources []string
KubeVersions []string
Depth int
Paths []string
}
func RunExplain(args *ExplainArgs) {
allowApiVersion := allower(args.ApiVersions)
allowResource := allower(args.Resources)
allowDepth := func(prefix int, depth int) bool {
if args.Depth == 0 {
// always allow if maxDepth is unset
return true
}
return (depth - prefix) <= args.Depth
}
allowedPaths := slice.Map(func(p string) []string { return strings.Split(p, ".") }, args.Paths)
allowPath := func(path []string) bool {
if len(allowedPaths) == 0 {
return allowDepth(0, len(path))
}
for _, prefix := range allowedPaths {
if IsPrefixOf(prefix, path) && allowDepth(len(prefix), len(path)) {
return true
}
}
return false
}
//table := NewPivotTable("?", args.KubeVersions)
for _, kubeVersion := range args.KubeVersions {
fmt.Printf("%s\n", kubeVersion)
spec := MustReadSwaggerSpecFromGithub(MustVersion(kubeVersion))
typesByKindByApiVersion := spec.ResolveStructure()
for _, resourceName := range slice.Sort(maps.Keys(typesByKindByApiVersion)) {
if !allowResource(resourceName) {
continue
}
typesByApiVersion := map[string]*ResolvedType{}
for apiVersion, resolvedType := range typesByKindByApiVersion[resourceName] {
if allowApiVersion(apiVersion) {
typesByApiVersion[apiVersion] = resolvedType
}
}
switch args.Format {
case "table":
for apiVersion, resolvedType := range typesByApiVersion {
fmt.Printf("%s %s:\n", apiVersion, resourceName)
fmt.Printf("%s\n\n", TableResource(resolvedType, allowPath))
}
case "condensed":
fmt.Printf("%s:\n", resourceName)
for apiVersion, resolvedType := range typesByApiVersion {
fmt.Printf("%s\n\n", CondensedResource(apiVersion, resolvedType, allowPath))
}
default:
panic(errors.Errorf("invalid output format: %s", args.Format))
}
}
}
}
func TableResource(resolvedType *ResolvedType, allowPath func([]string) bool) string {
tableString := &strings.Builder{}
table := tablewriter.NewWriter(tableString)
table.SetAutoWrapText(false)
table.SetRowLine(true)
table.SetAutoMergeCells(true)
table.SetColMinWidth(1, 100)
table.SetHeader([]string{"Path", "Type"})
for _, pair := range resolvedType.Paths([]string{}) {
path, vType := pair.Fst, pair.Snd
if allowPath(path) {
table.Append([]string{strings.Join(path, "."), vType})
}
}
table.Render()
return tableString.String()
}
func IsPrefixOf[A comparable](xs []A, ys []A) bool {
for i := 0; i < len(xs); i++ {
if i >= len(ys) || xs[i] != ys[i] {
return false
}
}
return true
}
func CondensedResource(apiVersion string, resolvedType *ResolvedType, allowPath func([]string) bool) string {
lines := []string{apiVersion + ":"}
for _, pair := range resolvedType.Paths([]string{}) {
path, vType := pair.Fst, pair.Snd
if len(path) > 0 && allowPath(path) {
prefix := strings.Repeat(" ", len(path)-1)
typeString := fmt.Sprintf("%s%s", prefix, path[len(path)-1])
lines = append(lines, fmt.Sprintf("%-60s %s", typeString, vType))
}
}
return strings.Join(lines, "\n")
}