...
1
16
17 package config
18
19 import (
20 "fmt"
21 "reflect"
22 "strings"
23
24 "k8s.io/apimachinery/pkg/util/sets"
25 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
26 )
27
28 type navigationSteps struct {
29 steps []navigationStep
30 currentStepIndex int
31 }
32
33 type navigationStep struct {
34 stepValue string
35 stepType reflect.Type
36 }
37
38 func newNavigationSteps(path string) (*navigationSteps, error) {
39 steps := []navigationStep{}
40 individualParts := strings.Split(path, ".")
41
42 currType := reflect.TypeOf(clientcmdapi.Config{})
43 currPartIndex := 0
44 for currPartIndex < len(individualParts) {
45 switch currType.Kind() {
46 case reflect.Map:
47
48
49
50
51
52
53 mapValueType := currType.Elem().Elem()
54 mapValueOptions, err := getPotentialTypeValues(mapValueType)
55 if err != nil {
56 return nil, err
57 }
58 nextPart := findNameStep(individualParts[currPartIndex:], sets.StringKeySet(mapValueOptions))
59
60 steps = append(steps, navigationStep{nextPart, mapValueType})
61 currPartIndex += len(strings.Split(nextPart, "."))
62 currType = mapValueType
63
64 case reflect.Struct:
65 nextPart := individualParts[currPartIndex]
66
67 options, err := getPotentialTypeValues(currType)
68 if err != nil {
69 return nil, err
70 }
71 fieldType, exists := options[nextPart]
72 if !exists {
73 return nil, fmt.Errorf("unable to parse %v after %v at %v", path, steps, currType)
74 }
75
76 steps = append(steps, navigationStep{nextPart, fieldType})
77 currPartIndex += len(strings.Split(nextPart, "."))
78 currType = fieldType
79 default:
80 return nil, fmt.Errorf("unable to parse one or more field values of %v", path)
81 }
82 }
83
84 return &navigationSteps{steps, 0}, nil
85 }
86
87 func (s *navigationSteps) pop() navigationStep {
88 if s.moreStepsRemaining() {
89 s.currentStepIndex++
90 return s.steps[s.currentStepIndex-1]
91 }
92 return navigationStep{}
93 }
94
95 func (s *navigationSteps) moreStepsRemaining() bool {
96 return len(s.steps) > s.currentStepIndex
97 }
98
99
100
101 func findNameStep(parts []string, typeOptions sets.String) string {
102 if len(parts) == 0 {
103 return ""
104 }
105
106 numberOfPartsInStep := findKnownValue(parts[1:], typeOptions) + 1
107
108 if numberOfPartsInStep == 0 {
109 numberOfPartsInStep = len(parts)
110 }
111 nextParts := parts[0:numberOfPartsInStep]
112
113 return strings.Join(nextParts, ".")
114 }
115
116
117 func getPotentialTypeValues(typeValue reflect.Type) (map[string]reflect.Type, error) {
118 if typeValue.Kind() == reflect.Pointer {
119 typeValue = typeValue.Elem()
120 }
121
122 if typeValue.Kind() != reflect.Struct {
123 return nil, fmt.Errorf("%v is not of type struct", typeValue)
124 }
125
126 ret := make(map[string]reflect.Type)
127
128 for fieldIndex := 0; fieldIndex < typeValue.NumField(); fieldIndex++ {
129 fieldType := typeValue.Field(fieldIndex)
130 yamlTag := fieldType.Tag.Get("json")
131 yamlTagName := strings.Split(yamlTag, ",")[0]
132
133 ret[yamlTagName] = fieldType.Type
134 }
135
136 return ret, nil
137 }
138
139 func findKnownValue(parts []string, valueOptions sets.String) int {
140 for i := range parts {
141 if valueOptions.Has(parts[i]) {
142 return i
143 }
144 }
145
146 return -1
147 }
148
View as plain text