@@ -19,6 +19,7 @@ package controllers
19
19
import (
20
20
"context"
21
21
"fmt"
22
+ "strconv"
22
23
23
24
registryv1alpha1 "github.com/devfile/registry-operator/api/v1alpha1"
24
25
"github.com/devfile/registry-operator/pkg/registry"
@@ -27,10 +28,14 @@ import (
27
28
corev1 "k8s.io/api/core/v1"
28
29
networkingv1 "k8s.io/api/networking/v1"
29
30
"k8s.io/apimachinery/pkg/api/errors"
31
+ "k8s.io/apimachinery/pkg/api/resource"
30
32
"k8s.io/apimachinery/pkg/types"
33
+ "k8s.io/apimachinery/pkg/util/intstr"
31
34
"sigs.k8s.io/controller-runtime/pkg/client"
32
35
)
33
36
37
+ const viewerContainerName = "registry-viewer"
38
+
34
39
// updateDeployment ensures that a devfile registry deployment exists on the cluster and is up to date with the custom resource
35
40
func (r * DevfileRegistryReconciler ) updateDeployment (ctx context.Context , cr * registryv1alpha1.DevfileRegistry , dep * appsv1.Deployment ) error {
36
41
// Check to see if the existing devfile registry deployment needs to be updated
@@ -73,6 +78,14 @@ func (r *DevfileRegistryReconciler) updateDeployment(ctx context.Context, cr *re
73
78
}
74
79
}
75
80
81
+ updated , err := r .updateDeploymentForHeadlessChange (cr , dep )
82
+ if err != nil {
83
+ return err
84
+ }
85
+ if updated {
86
+ needsUpdating = true
87
+ }
88
+
76
89
if registry .IsStorageEnabled (cr ) {
77
90
if dep .Spec .Template .Spec .Volumes [0 ].PersistentVolumeClaim == nil {
78
91
dep .Spec .Template .Spec .Volumes [0 ].VolumeSource = registry .GetDevfileRegistryVolumeSource (cr )
@@ -219,3 +232,174 @@ func (r *DevfileRegistryReconciler) deleteOldPVCIfNeeded(ctx context.Context, cr
219
232
}
220
233
return nil
221
234
}
235
+
236
+ // updateRegistryHeadlessEnv updates or adds the REGISTRY_HEADLESS environment variable
237
+ func updateRegistryHeadlessEnv (envVars []corev1.EnvVar , headless bool ) []corev1.EnvVar {
238
+ found := false
239
+ for i , env := range envVars {
240
+ if env .Name == "REGISTRY_HEADLESS" {
241
+ envVars [i ].Value = strconv .FormatBool (headless )
242
+ found = true
243
+ break
244
+ }
245
+ }
246
+ if ! found {
247
+ envVars = append (envVars , corev1.EnvVar {
248
+ Name : "REGISTRY_HEADLESS" ,
249
+ Value : strconv .FormatBool (headless ),
250
+ })
251
+ }
252
+ return envVars
253
+ }
254
+
255
+ // removeViewerContainer removes the registry-viewer container from the list of containers
256
+ func removeViewerContainer (containers []corev1.Container ) []corev1.Container {
257
+ var newContainers []corev1.Container
258
+ for _ , container := range containers {
259
+ if container .Name != viewerContainerName {
260
+ newContainers = append (newContainers , container )
261
+ }
262
+ }
263
+ return newContainers
264
+ }
265
+
266
+ // updateDeploymentForHeadlessChange updates the deployment based on headless configuration
267
+ func (r * DevfileRegistryReconciler ) updateDeploymentForHeadlessChange (cr * registryv1alpha1.DevfileRegistry , dep * appsv1.Deployment ) (bool , error ) {
268
+ updated := false
269
+ allowPrivilegeEscalation := false
270
+ runAsNonRoot := true
271
+ localHostname := "localhost"
272
+
273
+ if ! registry .IsHeadlessEnabled (cr ) {
274
+ // Check if viewer container already exists before adding
275
+ viewerExists := false
276
+ for _ , container := range dep .Spec .Template .Spec .Containers {
277
+ if container .Name == viewerContainerName {
278
+ viewerExists = true
279
+ break
280
+ }
281
+ }
282
+
283
+ if ! viewerExists {
284
+ // Configure StartupProbe
285
+ dep .Spec .Template .Spec .Containers [0 ].StartupProbe = & corev1.Probe {
286
+ ProbeHandler : corev1.ProbeHandler {
287
+ HTTPGet : & corev1.HTTPGetAction {
288
+ Path : "/viewer" ,
289
+ Port : intstr .FromInt (registry .RegistryViewerPort ),
290
+ Scheme : corev1 .URISchemeHTTP ,
291
+ },
292
+ },
293
+ InitialDelaySeconds : 30 ,
294
+ PeriodSeconds : 10 ,
295
+ TimeoutSeconds : 20 ,
296
+ }
297
+
298
+ // Append registry-viewer container
299
+ dep .Spec .Template .Spec .Containers = append (dep .Spec .Template .Spec .Containers , corev1.Container {
300
+ Image : registry .GetRegistryViewerImage (cr ),
301
+ ImagePullPolicy : registry .GetRegistryViewerImagePullPolicy (cr ),
302
+ Name : viewerContainerName ,
303
+ SecurityContext : & corev1.SecurityContext {
304
+ AllowPrivilegeEscalation : & allowPrivilegeEscalation ,
305
+ RunAsNonRoot : & runAsNonRoot ,
306
+ Capabilities : & corev1.Capabilities {
307
+ Drop : []corev1.Capability {"ALL" },
308
+ },
309
+ SeccompProfile : & corev1.SeccompProfile {
310
+ Type : "RuntimeDefault" ,
311
+ },
312
+ },
313
+ Resources : corev1.ResourceRequirements {
314
+ Requests : corev1.ResourceList {
315
+ corev1 .ResourceCPU : resource .MustParse ("250m" ),
316
+ corev1 .ResourceMemory : resource .MustParse ("64Mi" ),
317
+ },
318
+ Limits : corev1.ResourceList {
319
+ corev1 .ResourceCPU : resource .MustParse ("500m" ),
320
+ corev1 .ResourceMemory : resource .MustParse ("256Mi" ),
321
+ },
322
+ },
323
+ LivenessProbe : & corev1.Probe {
324
+ ProbeHandler : corev1.ProbeHandler {
325
+ HTTPGet : & corev1.HTTPGetAction {
326
+ Path : "/viewer" ,
327
+ Port : intstr .FromInt (registry .RegistryViewerPort ),
328
+ Scheme : corev1 .URISchemeHTTP ,
329
+ },
330
+ },
331
+ InitialDelaySeconds : 15 ,
332
+ PeriodSeconds : 10 ,
333
+ TimeoutSeconds : 20 ,
334
+ },
335
+ ReadinessProbe : & corev1.Probe {
336
+ ProbeHandler : corev1.ProbeHandler {
337
+ HTTPGet : & corev1.HTTPGetAction {
338
+ Path : "/viewer" ,
339
+ Port : intstr .FromInt (registry .RegistryViewerPort ),
340
+ Scheme : corev1 .URISchemeHTTP ,
341
+ },
342
+ },
343
+ InitialDelaySeconds : 15 ,
344
+ PeriodSeconds : 10 ,
345
+ TimeoutSeconds : 20 ,
346
+ },
347
+ Env : []corev1.EnvVar {
348
+ {
349
+ Name : "NEXT_PUBLIC_ANALYTICS_WRITE_KEY" ,
350
+ Value : cr .Spec .Telemetry .RegistryViewerWriteKey ,
351
+ },
352
+ {
353
+ Name : "DEVFILE_REGISTRIES" ,
354
+ Value : fmt .Sprintf (`[
355
+ {
356
+ "name": "%s",
357
+ "url": "http://%s",
358
+ "fqdn": "%s"
359
+ }
360
+ ]` , cr .ObjectMeta .Name , localHostname , cr .Status .URL ),
361
+ },
362
+ },
363
+ })
364
+ updated = true
365
+ }
366
+ } else {
367
+ // Check if REGISTRY_HEADLESS env var needs to be updated
368
+ headlessEnvNeedsUpdate := true
369
+ for _ , env := range dep .Spec .Template .Spec .Containers [0 ].Env {
370
+ if env .Name == "REGISTRY_HEADLESS" && env .Value == strconv .FormatBool (true ) {
371
+ headlessEnvNeedsUpdate = false
372
+ break
373
+ }
374
+ }
375
+
376
+ // Check if viewer container needs to be removed
377
+ viewerExists := false
378
+ for _ , container := range dep .Spec .Template .Spec .Containers {
379
+ if container .Name == viewerContainerName {
380
+ viewerExists = true
381
+ break
382
+ }
383
+ }
384
+
385
+ if headlessEnvNeedsUpdate || viewerExists {
386
+ // Set REGISTRY_HEADLESS environment variable
387
+ dep .Spec .Template .Spec .Containers [0 ].Env = updateRegistryHeadlessEnv (
388
+ dep .Spec .Template .Spec .Containers [0 ].Env ,
389
+ true ,
390
+ )
391
+
392
+ // Remove viewer container
393
+ dep .Spec .Template .Spec .Containers = removeViewerContainer (
394
+ dep .Spec .Template .Spec .Containers ,
395
+ )
396
+
397
+ // Clear startup probe
398
+ dep .Spec .Template .Spec .Containers [0 ].StartupProbe = nil
399
+
400
+ updated = true
401
+ }
402
+ }
403
+
404
+ return updated , nil
405
+ }
0 commit comments