@@ -20,6 +20,7 @@ import (
20
20
"errors"
21
21
"fmt"
22
22
"path"
23
+ "strings"
23
24
"text/template"
24
25
"time"
25
26
@@ -46,8 +47,9 @@ const (
46
47
defaultContainerName = "registry-grpc"
47
48
defaultContainerPortName = "grpc"
48
49
49
- defaultConfigMapName = "operator-sdk-run-bundle-config"
50
- defaultConfigMapKey = "extraFBC"
50
+ defaultConfigMapKey = "extraFBC"
51
+
52
+ maxConfigMapSize = 1 * 1024 * 1024
51
53
)
52
54
53
55
// FBCRegistryPod holds resources necessary for creation of a registry pod in FBC scenarios.
@@ -76,6 +78,8 @@ type FBCRegistryPod struct { //nolint:maligned
76
78
// SecurityContext on the Pod
77
79
SecurityContext string
78
80
81
+ configMapName string
82
+
79
83
cfg * operator.Configuration
80
84
}
81
85
@@ -89,6 +93,10 @@ func (f *FBCRegistryPod) init(cfg *operator.Configuration, cs *v1alpha1.CatalogS
89
93
f .FBCIndexRootDir = fmt .Sprintf ("/%s-configs" , cs .Name )
90
94
}
91
95
96
+ if f .configMapName == "" {
97
+ f .configMapName = fmt .Sprintf ("%s-configmap" , cs .Name )
98
+ }
99
+
92
100
f .cfg = cfg
93
101
94
102
// validate the FBCRegistryPod struct and ensure required fields are set
@@ -210,11 +218,39 @@ func (f *FBCRegistryPod) podForBundleRegistry(cs *v1alpha1.CatalogSource) (*core
210
218
211
219
// create ConfigMap if it does not exist,
212
220
// if it exists, then update it with new content.
213
- cm , err := f .createConfigMap (cs )
221
+ cms , err := f .createConfigMaps (cs )
214
222
if err != nil {
215
223
return nil , fmt .Errorf ("configMap error: %w" , err )
216
224
}
217
225
226
+ volumes := []corev1.Volume {}
227
+ volumeMounts := []corev1.VolumeMount {}
228
+
229
+ for _ , cm := range cms {
230
+ volumes = append (volumes , corev1.Volume {
231
+ Name : k8sutil .TrimDNS1123Label (cm .Name + "-volume" ),
232
+ VolumeSource : corev1.VolumeSource {
233
+ ConfigMap : & corev1.ConfigMapVolumeSource {
234
+ Items : []corev1.KeyToPath {
235
+ {
236
+ Key : defaultConfigMapKey ,
237
+ Path : path .Join (cm .Name , fmt .Sprintf ("%s.yaml" , defaultConfigMapKey )),
238
+ },
239
+ },
240
+ LocalObjectReference : corev1.LocalObjectReference {
241
+ Name : cm .Name ,
242
+ },
243
+ },
244
+ },
245
+ })
246
+
247
+ volumeMounts = append (volumeMounts , corev1.VolumeMount {
248
+ Name : k8sutil .TrimDNS1123Label (cm .Name + "-volume" ),
249
+ MountPath : path .Join (f .FBCIndexRootDir , cm .Name ),
250
+ SubPath : cm .Name ,
251
+ })
252
+ }
253
+
218
254
// make the pod definition
219
255
f .pod = & corev1.Pod {
220
256
ObjectMeta : metav1.ObjectMeta {
@@ -256,24 +292,7 @@ func (f *FBCRegistryPod) podForBundleRegistry(cs *v1alpha1.CatalogSource) (*core
256
292
// Type: corev1.SeccompProfileTypeRuntimeDefault,
257
293
// },
258
294
// },
259
- Volumes : []corev1.Volume {
260
- {
261
- Name : k8sutil .TrimDNS1123Label (cm .Name + "-volume" ),
262
- VolumeSource : corev1.VolumeSource {
263
- ConfigMap : & corev1.ConfigMapVolumeSource {
264
- Items : []corev1.KeyToPath {
265
- {
266
- Key : defaultConfigMapKey ,
267
- Path : path .Join (defaultConfigMapName , fmt .Sprintf ("%s.yaml" , defaultConfigMapKey )),
268
- },
269
- },
270
- LocalObjectReference : corev1.LocalObjectReference {
271
- Name : cm .Name ,
272
- },
273
- },
274
- },
275
- },
276
- },
295
+ Volumes : volumes ,
277
296
Containers : []corev1.Container {
278
297
{
279
298
Name : defaultContainerName ,
@@ -286,13 +305,7 @@ func (f *FBCRegistryPod) podForBundleRegistry(cs *v1alpha1.CatalogSource) (*core
286
305
Ports : []corev1.ContainerPort {
287
306
{Name : defaultContainerPortName , ContainerPort : f .GRPCPort },
288
307
},
289
- VolumeMounts : []corev1.VolumeMount {
290
- {
291
- Name : k8sutil .TrimDNS1123Label (cm .Name + "-volume" ),
292
- MountPath : path .Join (f .FBCIndexRootDir , cm .Name ),
293
- SubPath : cm .Name ,
294
- },
295
- },
308
+ VolumeMounts : volumeMounts ,
296
309
SecurityContext : & corev1.SecurityContext {
297
310
Privileged : pointer .Bool (false ),
298
311
ReadOnlyRootFilesystem : pointer .Bool (false ),
@@ -315,50 +328,134 @@ const fbcCmdTemplate = `opm serve {{ .FBCIndexRootDir}} -p {{ .GRPCPort }}`
315
328
316
329
// createConfigMap creates a ConfigMap if it does not exist and if it does, then update it with new content.
317
330
// Also, sets the owner reference by making CatalogSource the owner of ConfigMap object for cleanup purposes.
318
- func (f * FBCRegistryPod ) createConfigMap (cs * v1alpha1.CatalogSource ) (* corev1.ConfigMap , error ) {
319
- // new ConfigMap
320
- cm := & corev1.ConfigMap {
331
+ func (f * FBCRegistryPod ) createConfigMaps (cs * v1alpha1.CatalogSource ) ([]* corev1.ConfigMap , error ) {
332
+ // By default just use the partitioning logic.
333
+ // If the entire FBC contents can fit in one ConfigMap it will.
334
+ cms := f .partitionedConfigMaps ()
335
+
336
+ // Loop through all the ConfigMaps and set the OwnerReference and try to create them
337
+ for _ , cm := range cms {
338
+ // set owner reference by making catalog source the owner of ConfigMap object
339
+ if err := controllerutil .SetOwnerReference (cs , cm , f .cfg .Scheme ); err != nil {
340
+ return nil , fmt .Errorf ("set configmap %q owner reference: %v" , cm .GetName (), err )
341
+ }
342
+
343
+ err := f .createOrUpdateConfigMap (cm )
344
+ if err != nil {
345
+ return nil , err
346
+ }
347
+ }
348
+
349
+ return cms , nil
350
+ }
351
+
352
+ // partitionedConfigMaps will create and return a list of *corev1.ConfigMap
353
+ // that represents all the ConfigMaps that will need to be created to
354
+ // properly have all the FBC contents rendered in the registry pod.
355
+ func (f * FBCRegistryPod ) partitionedConfigMaps () []* corev1.ConfigMap {
356
+ // Split on the YAML separator `---`
357
+ yamlDefs := strings .Split (f .FBCContent , "---" )[1 :]
358
+ configMaps := []* corev1.ConfigMap {}
359
+
360
+ // Keep the number of ConfigMaps that are created to a minimum by
361
+ // stuffing them as full as possible.
362
+ partitionCount := 1
363
+ cm := f .makeBaseConfigMap ()
364
+ // for each chunk of yaml see if it can be added to the ConfigMap partition
365
+ for _ , yamlDef := range yamlDefs {
366
+ // If the ConfigMap has data then lets attempt to add to it
367
+ if len (cm .Data ) != 0 {
368
+ // Create a copy to use to verify that adding the data doesn't
369
+ // exceed the max ConfigMap size of 1 MiB.
370
+ tempCm := cm .DeepCopy ()
371
+ tempCm .Data [defaultConfigMapKey ] = tempCm .Data [defaultConfigMapKey ] + "\n ---\n " + yamlDef
372
+
373
+ // if it would be too large adding the data then partition it.
374
+ if tempCm .Size () >= maxConfigMapSize {
375
+ // Set the ConfigMap name based on the partition it is
376
+ cm .SetName (fmt .Sprintf ("%s-partition-%d" , f .configMapName , partitionCount ))
377
+ // Increase the partition count
378
+ partitionCount ++
379
+ // Add the ConfigMap to the list of ConfigMaps
380
+ configMaps = append (configMaps , cm .DeepCopy ())
381
+
382
+ // Create a new ConfigMap
383
+ cm = f .makeBaseConfigMap ()
384
+ // Since adding this data would have made the previous
385
+ // ConfigMap too large, add it to this new one.
386
+ // No chunk of YAML from the bundle should cause
387
+ // the ConfigMap size to exceed 1 MiB and if
388
+ // somehow it does then there is a problem with the
389
+ // YAML itself. We can't reasonably break it up smaller
390
+ // since it is a single object.
391
+ cm .Data [defaultConfigMapKey ] = yamlDef
392
+ } else {
393
+ // if adding the data to the ConfigMap
394
+ // doesn't make the ConfigMap exceed the
395
+ // size limit then actually add it.
396
+ cm .Data = tempCm .Data
397
+ }
398
+ } else {
399
+ // If there is no data in the ConfigMap
400
+ // then this is the first pass. Since it is
401
+ // the first pass go ahead and add the data.
402
+ cm .Data [defaultConfigMapKey ] = yamlDef
403
+ }
404
+ }
405
+
406
+ // if there aren't as many ConfigMaps as partitions AND the unadded ConfigMap has data
407
+ // then add it to the list of ConfigMaps. This is so we don't miss adding a ConfigMap
408
+ // after the above loop completes.
409
+ if len (configMaps ) != partitionCount && len (cm .Data ) != 0 {
410
+ cm .SetName (fmt .Sprintf ("%s-partition-%d" , f .configMapName , partitionCount ))
411
+ configMaps = append (configMaps , cm .DeepCopy ())
412
+ }
413
+
414
+ return configMaps
415
+ }
416
+
417
+ // makeBaseConfigMap will return the base *corev1.ConfigMap
418
+ // definition that is used by various functions when creating a ConfigMap.
419
+ func (f * FBCRegistryPod ) makeBaseConfigMap () * corev1.ConfigMap {
420
+ return & corev1.ConfigMap {
321
421
TypeMeta : metav1.TypeMeta {
322
422
APIVersion : corev1 .SchemeGroupVersion .String (),
323
423
Kind : "ConfigMap" ,
324
424
},
325
425
ObjectMeta : metav1.ObjectMeta {
326
- Name : defaultConfigMapName ,
327
426
Namespace : f .cfg .Namespace ,
328
427
},
329
- Data : map [string ]string {
330
- defaultConfigMapKey : f .FBCContent ,
331
- },
332
- }
333
-
334
- // set owner reference by making catalog source the owner of ConfigMap object
335
- if err := controllerutil .SetOwnerReference (cs , cm , f .cfg .Scheme ); err != nil {
336
- return nil , fmt .Errorf ("set configmap %q owner reference: %v" , cm .GetName (), err )
428
+ Data : map [string ]string {},
337
429
}
430
+ }
338
431
432
+ // createOrUpdateConfigMap will create a ConfigMap if it doesn't exist or
433
+ // update it if it already exists.
434
+ func (f * FBCRegistryPod ) createOrUpdateConfigMap (cm * corev1.ConfigMap ) error {
339
435
cmKey := types.NamespacedName {
340
- Namespace : f . cfg . Namespace ,
341
- Name : defaultConfigMapName ,
436
+ Namespace : cm . GetNamespace () ,
437
+ Name : cm . GetName () ,
342
438
}
343
439
344
440
// create a ConfigMap if it does not exist;
345
441
// update it with new data if it already exists.
346
442
if err := retry .RetryOnConflict (retry .DefaultBackoff , func () error {
347
- err := f .cfg .Client .Get (context .TODO (), cmKey , cm )
443
+ tempCm := & corev1.ConfigMap {}
444
+ err := f .cfg .Client .Get (context .TODO (), cmKey , tempCm )
348
445
if apierrors .IsNotFound (err ) {
349
446
if err := f .cfg .Client .Create (context .TODO (), cm ); err != nil {
350
447
return fmt .Errorf ("error creating ConfigMap: %w" , err )
351
448
}
449
+ return nil
352
450
}
353
451
// update ConfigMap with new FBCContent
354
- cm .Data = map [ string ] string { defaultConfigMapKey : f . FBCContent }
355
- return f .cfg .Client .Update (context .TODO (), cm )
452
+ tempCm .Data = cm . Data
453
+ return f .cfg .Client .Update (context .TODO (), tempCm )
356
454
}); err != nil {
357
- return nil , fmt .Errorf ("error updating ConfigMap: %w" , err )
455
+ return fmt .Errorf ("error updating ConfigMap: %w" , err )
358
456
}
359
457
360
- return cm , nil
361
-
458
+ return nil
362
459
}
363
460
364
461
// getContainerCmd uses templating to construct the container command
0 commit comments