@@ -20,6 +20,7 @@ import (
2020 "errors"
2121 "fmt"
2222 "path"
23+ "strings"
2324 "text/template"
2425 "time"
2526
@@ -46,8 +47,9 @@ const (
4647 defaultContainerName = "registry-grpc"
4748 defaultContainerPortName = "grpc"
4849
49- defaultConfigMapName = "operator-sdk-run-bundle-config"
50- defaultConfigMapKey = "extraFBC"
50+ defaultConfigMapKey = "extraFBC"
51+
52+ maxConfigMapSize = 1 * 1024 * 1024
5153)
5254
5355// FBCRegistryPod holds resources necessary for creation of a registry pod in FBC scenarios.
@@ -76,6 +78,8 @@ type FBCRegistryPod struct { //nolint:maligned
7678 // SecurityContext on the Pod
7779 SecurityContext string
7880
81+ configMapName string
82+
7983 cfg * operator.Configuration
8084}
8185
@@ -89,6 +93,10 @@ func (f *FBCRegistryPod) init(cfg *operator.Configuration, cs *v1alpha1.CatalogS
8993 f .FBCIndexRootDir = fmt .Sprintf ("/%s-configs" , cs .Name )
9094 }
9195
96+ if f .configMapName == "" {
97+ f .configMapName = fmt .Sprintf ("%s-configmap" , cs .Name )
98+ }
99+
92100 f .cfg = cfg
93101
94102 // validate the FBCRegistryPod struct and ensure required fields are set
@@ -210,11 +218,39 @@ func (f *FBCRegistryPod) podForBundleRegistry(cs *v1alpha1.CatalogSource) (*core
210218
211219 // create ConfigMap if it does not exist,
212220 // if it exists, then update it with new content.
213- cm , err := f .createConfigMap (cs )
221+ cms , err := f .createConfigMaps (cs )
214222 if err != nil {
215223 return nil , fmt .Errorf ("configMap error: %w" , err )
216224 }
217225
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+
218254 // make the pod definition
219255 f .pod = & corev1.Pod {
220256 ObjectMeta : metav1.ObjectMeta {
@@ -256,24 +292,7 @@ func (f *FBCRegistryPod) podForBundleRegistry(cs *v1alpha1.CatalogSource) (*core
256292 // Type: corev1.SeccompProfileTypeRuntimeDefault,
257293 // },
258294 // },
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 ,
277296 Containers : []corev1.Container {
278297 {
279298 Name : defaultContainerName ,
@@ -286,13 +305,7 @@ func (f *FBCRegistryPod) podForBundleRegistry(cs *v1alpha1.CatalogSource) (*core
286305 Ports : []corev1.ContainerPort {
287306 {Name : defaultContainerPortName , ContainerPort : f .GRPCPort },
288307 },
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 ,
296309 SecurityContext : & corev1.SecurityContext {
297310 Privileged : pointer .Bool (false ),
298311 ReadOnlyRootFilesystem : pointer .Bool (false ),
@@ -315,50 +328,134 @@ const fbcCmdTemplate = `opm serve {{ .FBCIndexRootDir}} -p {{ .GRPCPort }}`
315328
316329// createConfigMap creates a ConfigMap if it does not exist and if it does, then update it with new content.
317330// 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 {
321421 TypeMeta : metav1.TypeMeta {
322422 APIVersion : corev1 .SchemeGroupVersion .String (),
323423 Kind : "ConfigMap" ,
324424 },
325425 ObjectMeta : metav1.ObjectMeta {
326- Name : defaultConfigMapName ,
327426 Namespace : f .cfg .Namespace ,
328427 },
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 {},
337429 }
430+ }
338431
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 {
339435 cmKey := types.NamespacedName {
340- Namespace : f . cfg . Namespace ,
341- Name : defaultConfigMapName ,
436+ Namespace : cm . GetNamespace () ,
437+ Name : cm . GetName () ,
342438 }
343439
344440 // create a ConfigMap if it does not exist;
345441 // update it with new data if it already exists.
346442 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 )
348445 if apierrors .IsNotFound (err ) {
349446 if err := f .cfg .Client .Create (context .TODO (), cm ); err != nil {
350447 return fmt .Errorf ("error creating ConfigMap: %w" , err )
351448 }
449+ return nil
352450 }
353451 // 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 )
356454 }); err != nil {
357- return nil , fmt .Errorf ("error updating ConfigMap: %w" , err )
455+ return fmt .Errorf ("error updating ConfigMap: %w" , err )
358456 }
359457
360- return cm , nil
361-
458+ return nil
362459}
363460
364461// getContainerCmd uses templating to construct the container command
0 commit comments