1
1
package catalogsource
2
2
3
3
import (
4
- "bytes"
5
- "encoding/json"
6
4
"fmt"
7
- "reflect"
8
5
"regexp"
9
6
"strconv"
10
7
"strings"
11
8
"sync"
12
9
13
10
"github.com/blang/semver/v4"
14
-
15
- "github.com/operator-framework/api/pkg/operators/v1alpha1"
16
11
"github.com/sirupsen/logrus"
17
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
18
- "k8s.io/apimachinery/pkg/runtime/schema"
19
12
"k8s.io/apimachinery/pkg/version"
20
- "k8s.io/client-go/util/jsonpath"
13
+
14
+ "github.com/operator-framework/api/pkg/operators/v1alpha1"
21
15
)
22
16
23
17
const (
@@ -30,21 +24,6 @@ const (
30
24
capGrpKubeMinorV = "kubeminorv"
31
25
// capGrpKubePatchV is a capture group name for a kube patch version
32
26
capGrpKubePatchV = "kubepatchv"
33
- // capGrpGvk is a capture group name for a dynamic template that uses its own subgroups
34
- capGrpGvk = "gvk"
35
-
36
- // capSubgrpGroup is a sub capture group name used in a dynamic template
37
- capSubgrpGroup = "group"
38
- // capSubgrpVersion is a sub capture group name used in a dynamic template
39
- capSubgrpVersion = "version"
40
- // capSubgrpKind is a sub capture group name used in a dynamic template
41
- capSubgrpKind = "kind"
42
- // capSubgrpName is a sub capture group name used in a dynamic template
43
- capSubgrpName = "name"
44
- // capSubgrpNamespace is a sub capture group name used in a dynamic template
45
- capSubgrpNamespace = "namespace"
46
- // capSubgrpJsonpath is a sub capture group name used in a dynamic template
47
- capSubgrpJsonpath = "jsonpath"
48
27
49
28
// static templates
50
29
@@ -55,9 +34,6 @@ const (
55
34
// TemplKubePatchV is a template that represents the kube patch version
56
35
TemplKubePatchV = "{kube_patch_version}"
57
36
58
- // templGvk is a dynamic template that uses its own subgroups
59
- templGvk = "{group:(?P<group>.*?),version:(?P<version>.*?),kind:(?P<kind>.*?),name:(?P<name>.*?),namespace:(?P<namespace>.*?),jsonpath:(?P<jsonpath>{.*?})}"
60
-
61
37
// templateMissing represents a value that could not be obtained from the cluster
62
38
templateMissing = "missing"
63
39
@@ -72,17 +48,7 @@ const (
72
48
var templateNameToReplacementValuesMap = map [string ]string {}
73
49
74
50
// templateMutex is a package scoped mutex for synchronizing access to templateNameToReplacementValuesMap
75
- var templateMutex sync.Mutex
76
-
77
- // convertToKey is a function that creates a key for templateNameToReplacementValuesMap based on a GVK key and json path
78
- func convertToKey (key GVK_Key , jsonPath string ) string {
79
- return fmt .Sprintf ("{group:%s,version:%s,kind:%s,name:%s,namespace:%s,jsonpath:%s}" , key .Group , key .Version , key .Kind , key .name , key .namespace , jsonPath )
80
- }
81
-
82
- // gvkToJSONPathMap is a multimap (i.e. one key with multiple values) where each value is
83
- // zero or more JSON paths. In other words the user could specify multiple JSON path references
84
- // for the same kubernetes manifest
85
- var gvkToJSONPathMap = map [GVK_Key ][]string {}
51
+ var templateMutex sync.RWMutex
86
52
87
53
func init () {
88
54
// Handle known static templates
@@ -106,10 +72,9 @@ func initializeIfNeeded(templateKey string) {
106
72
// resetMaps is only useful for test cases
107
73
func resetMaps () {
108
74
templateMutex .Lock ()
109
- defer templateMutex .Unlock ()
110
-
111
75
templateNameToReplacementValuesMap = map [string ]string {}
112
- gvkToJSONPathMap = map [GVK_Key ][]string {}
76
+ templateMutex .Unlock ()
77
+
113
78
initializeIfNeeded (TemplKubeMajorV )
114
79
initializeIfNeeded (TemplKubeMinorV )
115
80
initializeIfNeeded (TemplKubePatchV )
@@ -139,13 +104,10 @@ var regexList = regexEntries{
139
104
{capGrpKubeMajorV , TemplKubeMajorV },
140
105
{capGrpKubeMinorV , TemplKubeMinorV },
141
106
{capGrpKubePatchV , TemplKubePatchV },
142
- {capGrpGvk , templGvk },
143
107
}
144
108
145
109
var regexImageTemplates = regexp .MustCompile (regexList .String ())
146
110
147
- var regexGVKTemplate = regexp .MustCompile (templGvk )
148
-
149
111
// ReplaceTemplates takes a catalog image reference containing templates (i.e. catalogImageTemplate)
150
112
// and attempts to replace the templates with actual values (if available).
151
113
// The return value processedCatalogImageTemplate represents the catalog image reference after processing.
@@ -155,8 +117,8 @@ var regexGVKTemplate = regexp.MustCompile(templGvk)
155
117
// fetched yet). Providing an empty catalogImageTemplate results in empty processedCatalogImageTemplate and
156
118
// zero length unresolvedTemplates
157
119
func ReplaceTemplates (catalogImageTemplate string ) (processedCatalogImageTemplate string , unresolvedTemplates []string ) {
158
- templateMutex .Lock ()
159
- defer templateMutex .Unlock ()
120
+ templateMutex .RLock ()
121
+ defer templateMutex .RUnlock ()
160
122
161
123
// init to empty slice
162
124
unresolvedTemplates = []string {}
@@ -200,161 +162,6 @@ func GetCatalogTemplateAnnotation(catalogSource *v1alpha1.CatalogSource) string
200
162
}
201
163
}
202
164
203
- // GVK_Key uniquely represents a Group/Version/Kind (with optional name/namespace)
204
- // and can be used as a key for retrieval of data associated with this key
205
- type GVK_Key struct {
206
- schema.GroupVersionKind
207
- name string
208
- namespace string
209
- }
210
-
211
- func InitializeCatalogSourceTemplates (catalogSource * v1alpha1.CatalogSource ) []GVK_Key {
212
-
213
- // capture a list of keys that were found in this catalog source
214
- foundGVKs := []GVK_Key {}
215
-
216
- // findNamedMatches will return a map whose key is the named capture group, and value is the value of the capture group
217
- findNamedMatches := func (str string ) map [string ]string {
218
- // Note: matches and names indices are "in sync"
219
- matches := regexGVKTemplate .FindStringSubmatch (str )
220
- names := regexGVKTemplate .SubexpNames ()
221
-
222
- results := map [string ]string {}
223
- for i , match := range matches {
224
- // only add named groups to the map
225
- if names [i ] != "" {
226
- results [names [i ]] = match
227
- }
228
- }
229
- return results
230
- }
231
-
232
- catalogImageTemplate := GetCatalogTemplateAnnotation (catalogSource )
233
- if catalogImageTemplate == "" {
234
- // bail out since there's nothing to do here
235
- return foundGVKs
236
- }
237
-
238
- /* Handle GVK templates */
239
-
240
- // get every GVK template available (if any)
241
- gvkTemplates := regexGVKTemplate .FindAllString (catalogImageTemplate , - 1 )
242
- // add each GVK template for later use, initializing to missing value
243
- for _ , gvkTemplate := range gvkTemplates {
244
- initializeIfNeeded (gvkTemplate )
245
-
246
- // get the sub groups
247
- subGroupMap := findNamedMatches (gvkTemplate )
248
-
249
- // create GVKTemplate to use as a key... add values from the subgroups as best we can, defaults to empty string for values not found
250
- key := GVK_Key {
251
- GroupVersionKind : schema.GroupVersionKind {Group : subGroupMap [capSubgrpGroup ], Version : subGroupMap [capSubgrpVersion ], Kind : subGroupMap [capSubgrpKind ]},
252
- name : subGroupMap [capSubgrpName ],
253
- namespace : subGroupMap [capSubgrpNamespace ],
254
- }
255
- jsonPath := subGroupMap [capSubgrpJsonpath ]
256
-
257
- // see if we've already added this key (don't add duplicates)
258
- gvkPresent := false
259
- for _ , existingGVK := range foundGVKs {
260
- if reflect .DeepEqual (existingGVK , key ) {
261
- gvkPresent = true
262
- }
263
- }
264
- if ! gvkPresent {
265
- foundGVKs = append (foundGVKs , key )
266
- }
267
-
268
- // add this entry to the map and append the jsonPath to the array
269
- if existingJsonPaths , ok := gvkToJSONPathMap [key ]; ok {
270
- // map already has this key, now find out if we've already added this path
271
- foundEntry := false
272
- for _ , existingJsonPath := range existingJsonPaths {
273
- if jsonPath == existingJsonPath {
274
- foundEntry = true
275
- }
276
- }
277
- // if we did not find a jsonpath entry then add it now
278
- if ! foundEntry {
279
- gvkToJSONPathMap [key ] = append (gvkToJSONPathMap [key ], jsonPath )
280
- }
281
- } else {
282
- gvkToJSONPathMap [key ] = append (gvkToJSONPathMap [key ], jsonPath )
283
- }
284
-
285
- }
286
-
287
- return foundGVKs
288
- }
289
-
290
- func UpdateGVKValue (u * unstructured.Unstructured , logger * logrus.Logger ) {
291
- templateMutex .Lock ()
292
- defer templateMutex .Unlock ()
293
-
294
- // reconstitute the key
295
- key := GVK_Key {
296
- GroupVersionKind : u .GetObjectKind ().GroupVersionKind (),
297
- name : u .GetName (),
298
- namespace : u .GetNamespace (),
299
- }
300
-
301
- // find the JSON paths
302
- if jsonPaths , ok := gvkToJSONPathMap [key ]; ok {
303
-
304
- // convert the unstructured object into JSON as bytes
305
- jsonBytes , err := u .MarshalJSON ()
306
- if err != nil {
307
- logger .WithError (err ).Warn ("unable to convert kube manifest to JSON" )
308
- }
309
-
310
- // pass the JSON as bytes through the go json library so its in the right format
311
- var processedJSON interface {}
312
- err = json .Unmarshal (jsonBytes , & processedJSON )
313
- if err != nil {
314
- logger .WithError (err ).Warn ("unable to convert kube manifest json data into usable form" )
315
- }
316
-
317
- for _ , jsonPath := range jsonPaths {
318
-
319
- gvkLogger := logger .WithFields (logrus.Fields {
320
- "gvk" : u .GetObjectKind ().GroupVersionKind ().String (),
321
- "jsonPath" : jsonPath ,
322
- })
323
- // create the json path parser
324
- jsonPathParser := jsonpath .New ("GVK path parser" )
325
-
326
- // parse the json path template
327
- err = jsonPathParser .Parse (jsonPath )
328
- if err != nil {
329
- gvkLogger .WithError (err ).Warn ("unable to parse json path template" )
330
- continue
331
- }
332
-
333
- // execute the parser using the JSON data writing the results into a buffer
334
- buf := new (bytes.Buffer )
335
- err = jsonPathParser .Execute (buf , processedJSON )
336
- if err != nil {
337
- gvkLogger .WithError (err ).Warn ("unable to execute json path parsing" )
338
- continue
339
- }
340
-
341
- templateMapKey := convertToKey (key , jsonPath )
342
- templateMapValue := buf .String ()
343
-
344
- if jsonPath == templateMapValue {
345
- // the jsonpath is exactly the same as the templateMapValue, this means
346
- // that the jsonpath was probably invalid, so don't update
347
- gvkLogger .Debugf ("jsonpath %q is likely invalid (maybe curly braces are missing?)" , jsonPath )
348
- continue
349
- }
350
- // reconstruct the key for the template replacement map and add
351
- // whatever we got from the json path execution
352
- templateNameToReplacementValuesMap [templateMapKey ] = templateMapValue
353
- gvkLogger .Debugf ("updated templateNameToReplacementValuesMap: key=%q value%q" , templateMapKey , templateMapValue )
354
- }
355
- }
356
- }
357
-
358
165
func UpdateKubeVersion (serverVersion * version.Info , logger * logrus.Logger ) {
359
166
templateMutex .Lock ()
360
167
defer templateMutex .Unlock ()
0 commit comments