1
2
3
4 package accumulator
5
6 import (
7 "encoding/json"
8 "strings"
9
10 "k8s.io/kube-openapi/pkg/validation/spec"
11 "sigs.k8s.io/kustomize/api/ifc"
12 "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
13 "sigs.k8s.io/kustomize/api/types"
14 "sigs.k8s.io/kustomize/kyaml/errors"
15 "sigs.k8s.io/kustomize/kyaml/filesys"
16 "sigs.k8s.io/kustomize/kyaml/resid"
17 "sigs.k8s.io/yaml"
18 )
19
20
21
22
23 type OpenAPIDefinition struct {
24 Schema spec.Schema
25 Dependencies []string
26 }
27
28 type myProperties = map[string]spec.Schema
29 type nameToApiMap map[string]OpenAPIDefinition
30
31
32 func LoadConfigFromCRDs(
33 ldr ifc.Loader, paths []string) (*builtinconfig.TransformerConfig, error) {
34 tc := builtinconfig.MakeEmptyConfig()
35 for _, path := range paths {
36 content, err := ldr.Load(path)
37 if err != nil {
38 return nil, err
39 }
40 m, err := makeNameToApiMap(content)
41 if err != nil {
42 return nil, errors.WrapPrefixf(err, "unable to parse open API definition from '%s'", path)
43 }
44 otherTc, err := makeConfigFromApiMap(m)
45 if err != nil {
46 return nil, err
47 }
48 tc, err = tc.Merge(otherTc)
49 if err != nil {
50 return nil, err
51 }
52 }
53 return tc, nil
54 }
55
56 func makeNameToApiMap(content []byte) (result nameToApiMap, err error) {
57 if content[0] == '{' {
58 err = json.Unmarshal(content, &result)
59 } else {
60 err = yaml.Unmarshal(content, &result)
61 }
62 return
63 }
64
65 func makeConfigFromApiMap(m nameToApiMap) (*builtinconfig.TransformerConfig, error) {
66 result := builtinconfig.MakeEmptyConfig()
67 for name, api := range m {
68 if !looksLikeAk8sType(api.Schema.SchemaProps.Properties) {
69 continue
70 }
71 tc := builtinconfig.MakeEmptyConfig()
72 err := loadCrdIntoConfig(
73 tc, makeGvkFromTypeName(name), m, name, []string{})
74 if err != nil {
75 return result, err
76 }
77 result, err = result.Merge(tc)
78 if err != nil {
79 return result, err
80 }
81 }
82 return result, nil
83 }
84
85
86
87
88 func makeGvkFromTypeName(n string) resid.Gvk {
89 names := strings.Split(n, filesys.SelfDir)
90 kind := names[len(names)-1]
91 return resid.Gvk{Kind: kind}
92 }
93
94 func looksLikeAk8sType(properties myProperties) bool {
95 _, ok := properties["kind"]
96 if !ok {
97 return false
98 }
99 _, ok = properties["apiVersion"]
100 if !ok {
101 return false
102 }
103 _, ok = properties["metadata"]
104 return ok
105 }
106
107 const (
108
109 xAnnotation = "x-kubernetes-annotation"
110
111
112 xLabelSelector = "x-kubernetes-label-selector"
113
114
115 xIdentity = "x-kubernetes-identity"
116
117
118 xVersion = "x-kubernetes-object-ref-api-version"
119
120
121 xKind = "x-kubernetes-object-ref-kind"
122
123
124
125 xNameKey = "x-kubernetes-object-ref-name-key"
126 )
127
128
129 func loadCrdIntoConfig(
130 theConfig *builtinconfig.TransformerConfig, theGvk resid.Gvk, theMap nameToApiMap,
131 typeName string, path []string) (err error) {
132 api, ok := theMap[typeName]
133 if !ok {
134 return nil
135 }
136 for propName, property := range api.Schema.SchemaProps.Properties {
137 _, annotate := property.Extensions.GetString(xAnnotation)
138 if annotate {
139 err = theConfig.AddAnnotationFieldSpec(
140 makeFs(theGvk, append(path, propName)))
141 if err != nil {
142 return
143 }
144 }
145 _, label := property.Extensions.GetString(xLabelSelector)
146 if label {
147 err = theConfig.AddLabelFieldSpec(
148 makeFs(theGvk, append(path, propName)))
149 if err != nil {
150 return
151 }
152 }
153 _, identity := property.Extensions.GetString(xIdentity)
154 if identity {
155 err = theConfig.AddPrefixFieldSpec(
156 makeFs(theGvk, append(path, propName)))
157 if err != nil {
158 return
159 }
160 }
161 version, ok := property.Extensions.GetString(xVersion)
162 if ok {
163 kind, ok := property.Extensions.GetString(xKind)
164 if ok {
165 nameKey, ok := property.Extensions.GetString(xNameKey)
166 if !ok {
167 nameKey = "name"
168 }
169 err = theConfig.AddNamereferenceFieldSpec(
170 builtinconfig.NameBackReferences{
171 Gvk: resid.Gvk{Kind: kind, Version: version},
172 Referrers: []types.FieldSpec{
173 makeFs(theGvk, append(path, propName, nameKey))},
174 })
175 if err != nil {
176 return
177 }
178 }
179 }
180 if property.Ref.GetURL() != nil {
181 err = loadCrdIntoConfig(
182 theConfig, theGvk, theMap,
183 property.Ref.String(), append(path, propName))
184 if err != nil {
185 return
186 }
187 }
188 }
189 return nil
190 }
191
192 func makeFs(in resid.Gvk, path []string) types.FieldSpec {
193 return types.FieldSpec{
194 CreateIfNotPresent: false,
195 Gvk: in,
196 Path: strings.Join(path, "/"),
197 }
198 }
199
View as plain text