@@ -20,17 +20,17 @@ import (
20
20
"errors"
21
21
"fmt"
22
22
"path"
23
- "path/filepath"
24
- "strings"
25
23
"text/template"
26
24
"time"
27
25
28
26
"github.com/operator-framework/api/pkg/operators/v1alpha1"
29
27
log "github.com/sirupsen/logrus"
30
28
corev1 "k8s.io/api/core/v1"
29
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
31
30
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32
31
"k8s.io/apimachinery/pkg/types"
33
32
"k8s.io/apimachinery/pkg/util/wait"
33
+ "k8s.io/client-go/util/retry"
34
34
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
35
35
36
36
"github.com/operator-framework/operator-sdk/internal/olm/operator"
@@ -44,6 +44,9 @@ const (
44
44
45
45
defaultContainerName = "registry-grpc"
46
46
defaultContainerPortName = "grpc"
47
+
48
+ defaultConfigMapName = "operator-sdk-run-bundle-config"
49
+ defaultConfigMapKey = "extraFBC"
47
50
)
48
51
49
52
// FBCRegistryPod holds resources necessary for creation of a registry pod in FBC scenarios.
@@ -64,35 +67,32 @@ type FBCRegistryPod struct { //nolint:maligned
64
67
// FBCContent represents the contents of the FBC file (string YAML).
65
68
FBCContent string
66
69
67
- // FBCDir is the name of the FBC directory name where the FBC resides in.
68
- FBCDir string
69
-
70
- // FBCFile represents the FBC filename that has all the contents to be served through the registry pod.
71
- FBCFile string
70
+ // FBCIndexRootDir is the FBC directory that exists under root of an FBC container image.
71
+ // This directory has the File-Based Catalog representation of a catalog index.
72
+ FBCIndexRootDir string
72
73
73
74
cfg * operator.Configuration
74
75
}
75
76
76
77
// init initializes the FBCRegistryPod struct.
77
- func (f * FBCRegistryPod ) init (cfg * operator.Configuration ) error {
78
+ func (f * FBCRegistryPod ) init (cfg * operator.Configuration , cs * v1alpha1. CatalogSource ) error {
78
79
if f .GRPCPort == 0 {
79
80
f .GRPCPort = defaultGRPCPort
80
81
}
81
82
83
+ if f .FBCIndexRootDir == "" {
84
+ f .FBCIndexRootDir = "/configs"
85
+ }
86
+
82
87
f .cfg = cfg
83
88
84
89
// validate the FBCRegistryPod struct and ensure required fields are set
85
90
if err := f .validate (); err != nil {
86
91
return fmt .Errorf ("invalid FBC registry pod: %v" , err )
87
92
}
88
93
89
- bundleImage := f .BundleItems [len (f .BundleItems )- 1 ].ImageTag
90
- trimmedbundleImage := strings .Split (bundleImage , ":" )[0 ]
91
- f .FBCDir = fmt .Sprintf ("%s-index" , filepath .Join ("/tmp" , strings .Split (trimmedbundleImage , "/" )[2 ]))
92
- f .FBCFile = filepath .Join (f .FBCDir , strings .Split (bundleImage , ":" )[1 ])
93
-
94
94
// podForBundleRegistry() to make the pod definition
95
- pod , err := f .podForBundleRegistry ()
95
+ pod , err := f .podForBundleRegistry (cs )
96
96
if err != nil {
97
97
return fmt .Errorf ("error building registry pod definition: %v" , err )
98
98
}
@@ -105,7 +105,7 @@ func (f *FBCRegistryPod) init(cfg *operator.Configuration) error {
105
105
// sets the catalog source as the owner for the pod and verifies that
106
106
// the pod is running
107
107
func (f * FBCRegistryPod ) Create (ctx context.Context , cfg * operator.Configuration , cs * v1alpha1.CatalogSource ) (* corev1.Pod , error ) {
108
- if err := f .init (cfg ); err != nil {
108
+ if err := f .init (cfg , cs ); err != nil {
109
109
return nil , err
110
110
}
111
111
@@ -184,7 +184,7 @@ func getPodName(bundleImage string) string {
184
184
185
185
// podForBundleRegistry constructs and returns the registry pod definition
186
186
// and throws error when unable to build the pod definition successfully
187
- func (f * FBCRegistryPod ) podForBundleRegistry () (* corev1.Pod , error ) {
187
+ func (f * FBCRegistryPod ) podForBundleRegistry (cs * v1alpha1. CatalogSource ) (* corev1.Pod , error ) {
188
188
// rp was already validated so len(f.BundleItems) must be greater than 0.
189
189
bundleImage := f .BundleItems [len (f .BundleItems )- 1 ].ImageTag
190
190
@@ -194,13 +194,38 @@ func (f *FBCRegistryPod) podForBundleRegistry() (*corev1.Pod, error) {
194
194
return nil , err
195
195
}
196
196
197
+ // create ConfigMap if it does not exist,
198
+ // if it exists, then update it with new content.
199
+ cm , err := f .createConfigMap (cs )
200
+ if err != nil {
201
+ return nil , fmt .Errorf ("configMap error: %w" , err )
202
+ }
203
+
197
204
// make the pod definition
198
205
f .pod = & corev1.Pod {
199
206
ObjectMeta : metav1.ObjectMeta {
200
207
Name : getPodName (bundleImage ),
201
208
Namespace : f .cfg .Namespace ,
202
209
},
203
210
Spec : corev1.PodSpec {
211
+ Volumes : []corev1.Volume {
212
+ {
213
+ Name : k8sutil .TrimDNS1123Label (cm .Name + "-volume" ),
214
+ VolumeSource : corev1.VolumeSource {
215
+ ConfigMap : & corev1.ConfigMapVolumeSource {
216
+ Items : []corev1.KeyToPath {
217
+ {
218
+ Key : defaultConfigMapKey ,
219
+ Path : path .Join (defaultConfigMapName , fmt .Sprintf ("%s.yaml" , defaultConfigMapKey )),
220
+ },
221
+ },
222
+ LocalObjectReference : corev1.LocalObjectReference {
223
+ Name : cm .Name ,
224
+ },
225
+ },
226
+ },
227
+ },
228
+ },
204
229
Containers : []corev1.Container {
205
230
{
206
231
Name : defaultContainerName ,
@@ -213,6 +238,13 @@ func (f *FBCRegistryPod) podForBundleRegistry() (*corev1.Pod, error) {
213
238
Ports : []corev1.ContainerPort {
214
239
{Name : defaultContainerPortName , ContainerPort : f .GRPCPort },
215
240
},
241
+ VolumeMounts : []corev1.VolumeMount {
242
+ {
243
+ Name : k8sutil .TrimDNS1123Label (cm .Name + "-volume" ),
244
+ MountPath : path .Join (f .FBCIndexRootDir , cm .Name ),
245
+ SubPath : cm .Name ,
246
+ },
247
+ },
216
248
},
217
249
},
218
250
},
@@ -221,23 +253,63 @@ func (f *FBCRegistryPod) podForBundleRegistry() (*corev1.Pod, error) {
221
253
return f .pod , nil
222
254
}
223
255
224
- const fbcCmdTemplate = `mkdir -p {{ .FBCDir }} && \
225
- echo '{{ .FBCContent }}' >> {{ .FBCFile }} && \
226
- opm serve {{ .FBCDir }} -p {{ .GRPCPort }}
227
- `
256
+ // container creation command for FBC type images.
257
+ const fbcCmdTemplate = `opm serve {{ .FBCIndexRootDir}} -p {{ .GRPCPort }}`
258
+
259
+ // createConfigMap creates a ConfigMap if it does not exist and if it does, then update it with new content.
260
+ // Also, sets the owner reference by making CatalogSource the owner of ConfigMap object for cleanup purposes.
261
+ func (f * FBCRegistryPod ) createConfigMap (cs * v1alpha1.CatalogSource ) (* corev1.ConfigMap , error ) {
262
+ // new ConfigMap
263
+ cm := & corev1.ConfigMap {
264
+ TypeMeta : metav1.TypeMeta {
265
+ APIVersion : corev1 .SchemeGroupVersion .String (),
266
+ Kind : "ConfigMap" ,
267
+ },
268
+ ObjectMeta : metav1.ObjectMeta {
269
+ Name : defaultConfigMapName ,
270
+ Namespace : f .cfg .Namespace ,
271
+ },
272
+ Data : map [string ]string {
273
+ defaultConfigMapKey : f .FBCContent ,
274
+ },
275
+ }
276
+
277
+ // set owner reference by making catalog source the owner of ConfigMap object
278
+ if err := controllerutil .SetOwnerReference (cs , cm , f .cfg .Scheme ); err != nil {
279
+ return nil , fmt .Errorf ("set configmap %q owner reference: %v" , cm .GetName (), err )
280
+ }
281
+
282
+ cmKey := types.NamespacedName {
283
+ Namespace : f .cfg .Namespace ,
284
+ Name : defaultConfigMapName ,
285
+ }
286
+
287
+ // create a ConfigMap if it does not exist;
288
+ // update it with new data if it already exists.
289
+ if err := retry .RetryOnConflict (retry .DefaultBackoff , func () error {
290
+ err := f .cfg .Client .Get (context .TODO (), cmKey , cm )
291
+ if apierrors .IsNotFound (err ) {
292
+ if err := f .cfg .Client .Create (context .TODO (), cm ); err != nil {
293
+ return fmt .Errorf ("error creating ConfigMap: %w" , err )
294
+ }
295
+ }
296
+ // update ConfigMap with new FBCContent
297
+ cm .Data = map [string ]string {defaultConfigMapKey : f .FBCContent }
298
+ return f .cfg .Client .Update (context .TODO (), cm )
299
+ }); err != nil {
300
+ return nil , fmt .Errorf ("error updating ConfigMap: %w" , err )
301
+ }
302
+
303
+ return cm , nil
304
+
305
+ }
228
306
229
307
// getContainerCmd uses templating to construct the container command
230
308
// and throws error if unable to parse and execute the container command
231
309
func (f * FBCRegistryPod ) getContainerCmd () (string , error ) {
232
- var t * template.Template
233
- // create a custom dirname template function
234
- funcMap := template.FuncMap {
235
- "dirname" : path .Dir ,
236
- }
237
-
238
310
// add the custom dirname template function to the
239
311
// template's FuncMap and parse the cmdTemplate
240
- t = template .Must (template .New ("cmd" ). Funcs ( funcMap ).Parse (fbcCmdTemplate ))
312
+ t : = template .Must (template .New ("cmd" ).Parse (fbcCmdTemplate ))
241
313
242
314
// execute the command by applying the parsed template to command
243
315
// and write command output to out
0 commit comments