@@ -18,24 +18,30 @@ package config
18
18
import (
19
19
"context"
20
20
"fmt"
21
+ "reflect"
22
+ "sort"
21
23
"strings"
22
24
"sync"
23
25
26
+ dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
27
+ "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
28
+ controller "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
24
29
"github.com/devfile/devworkspace-operator/pkg/config/proxy"
30
+ "github.com/devfile/devworkspace-operator/pkg/constants"
31
+ "github.com/devfile/devworkspace-operator/pkg/infrastructure"
25
32
routeV1 "github.com/openshift/api/route/v1"
26
33
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
27
34
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
35
"k8s.io/apimachinery/pkg/types"
29
36
ctrl "sigs.k8s.io/controller-runtime"
30
37
crclient "sigs.k8s.io/controller-runtime/pkg/client"
31
-
32
- controller "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"
33
- "github.com/devfile/devworkspace-operator/pkg/infrastructure"
34
38
)
35
39
36
40
const (
37
- OperatorConfigName = "devworkspace-operator-config"
38
- openShiftTestRouteName = "devworkspace-controller-test-route"
41
+ OperatorConfigName = "devworkspace-operator-config"
42
+ openShiftTestRouteName = "devworkspace-controller-test-route"
43
+ ExternalConfigName = "external-config-name"
44
+ ExternalConfigNamespace = "external-config-namespace"
39
45
)
40
46
41
47
var (
@@ -55,6 +61,53 @@ func SetConfigForTesting(config *controller.OperatorConfiguration) {
55
61
updatePublicConfig ()
56
62
}
57
63
64
+ func ApplyExternalDWOCConfig (workspace * dw.DevWorkspace , client crclient.Client ) (err error ) {
65
+
66
+ if ! workspace .Spec .Template .Attributes .Exists (constants .ExternalDevWorkspaceConfiguration ) {
67
+ return nil
68
+ }
69
+
70
+ ExternalDWOCMeta := v1alpha1.ExternalConfig {}
71
+
72
+ err = workspace .Spec .Template .Attributes .GetInto (constants .ExternalDevWorkspaceConfiguration , & ExternalDWOCMeta )
73
+ if err != nil {
74
+ return fmt .Errorf ("failed to read attribute %s in DevWorkspace attributes: %w" , constants .ExternalDevWorkspaceConfiguration , err )
75
+ }
76
+
77
+ if ExternalDWOCMeta .Name == "" {
78
+ return fmt .Errorf ("'name' must be set for attribute %s in DevWorkspace attributes" , constants .ExternalDevWorkspaceConfiguration )
79
+ }
80
+
81
+ if ExternalDWOCMeta .Namespace == "" {
82
+ return fmt .Errorf ("'namespace' must be set for attribute %s in DevWorkspace attributes" , constants .ExternalDevWorkspaceConfiguration )
83
+ }
84
+
85
+ externalDWOC := & controller.DevWorkspaceOperatorConfig {}
86
+ namespacedName := types.NamespacedName {
87
+ Name : ExternalDWOCMeta .Name ,
88
+ Namespace : ExternalDWOCMeta .Namespace ,
89
+ }
90
+
91
+ err = client .Get (context .TODO (), namespacedName , externalDWOC )
92
+ if err != nil {
93
+ return fmt .Errorf ("could not fetch external DWOC with name '%s' in namespace '%s': %w" , ExternalDWOCMeta .Name , ExternalDWOCMeta .Namespace , err )
94
+ }
95
+
96
+ // TODO: It might be better to just always merge rather than suffering performance hit in configsAlreadyMerged() (or maintaing configsAlreadyMerged if we don't use reflect.DeepEqual)
97
+ if ! configsAlreadyMerged (externalDWOC .Config , internalConfig ) {
98
+ mergeInteralConfigWithExternal (externalDWOC .Config )
99
+ }
100
+
101
+ return nil
102
+ }
103
+
104
+ func mergeInteralConfigWithExternal (externalConfig * controller.OperatorConfiguration ) {
105
+ configMutex .Lock ()
106
+ defer configMutex .Unlock ()
107
+ mergeConfig (externalConfig , internalConfig )
108
+ updatePublicConfig ()
109
+ }
110
+
58
111
func SetupControllerConfig (client crclient.Client ) error {
59
112
if internalConfig != nil {
60
113
return fmt .Errorf ("internal controller configuration is already set up" )
@@ -185,6 +238,147 @@ func discoverRouteSuffix(client crclient.Client) (string, error) {
185
238
return host , nil
186
239
}
187
240
241
+ // TODO: Improve variable names?
242
+ // Returns true if 'from' has already been merged with 'to'.
243
+ // The two configs are considered merged if all fields that are set in 'from' have the same value in 'to'
244
+ func configsAlreadyMerged (from , to * controller.OperatorConfiguration ) bool {
245
+ if from == nil {
246
+ return true
247
+ }
248
+ if from .EnableExperimentalFeatures != nil {
249
+
250
+ if to .EnableExperimentalFeatures == nil {
251
+ return false
252
+ }
253
+
254
+ if * to .EnableExperimentalFeatures != * from .EnableExperimentalFeatures {
255
+ return false
256
+ }
257
+ }
258
+ if from .Routing != nil {
259
+ if to .Routing == nil {
260
+ return false
261
+ }
262
+ if from .Routing .DefaultRoutingClass != "" && to .Routing .DefaultRoutingClass != from .Routing .DefaultRoutingClass {
263
+ return false
264
+ }
265
+ if from .Routing .ClusterHostSuffix != "" && to .Routing .ClusterHostSuffix != from .Routing .ClusterHostSuffix {
266
+ return false
267
+ }
268
+ if from .Routing .ProxyConfig != nil {
269
+ if to .Routing .ProxyConfig == nil {
270
+ return false
271
+ }
272
+
273
+ if from .Routing .ProxyConfig .HttpProxy != "" && to .Routing .ProxyConfig .HttpProxy != from .Routing .ProxyConfig .HttpProxy {
274
+ return false
275
+ }
276
+
277
+ if from .Routing .ProxyConfig .HttpsProxy != "" && to .Routing .ProxyConfig .HttpsProxy != from .Routing .ProxyConfig .HttpsProxy {
278
+ return false
279
+ }
280
+
281
+ if from .Routing .ProxyConfig .NoProxy != "" && to .Routing .ProxyConfig .NoProxy != from .Routing .ProxyConfig .NoProxy {
282
+ return false
283
+ }
284
+ }
285
+ }
286
+ if from .Workspace != nil {
287
+ if to .Workspace == nil {
288
+ return false
289
+ }
290
+ if from .Workspace .StorageClassName != nil {
291
+ if to .Workspace .StorageClassName == nil {
292
+ return false
293
+ }
294
+
295
+ if * to .Workspace .StorageClassName != * from .Workspace .StorageClassName {
296
+ return false
297
+ }
298
+ }
299
+ if from .Workspace .PVCName != "" && to .Workspace .PVCName != from .Workspace .PVCName {
300
+ return false
301
+ }
302
+ if from .Workspace .ImagePullPolicy != "" && to .Workspace .ImagePullPolicy != from .Workspace .ImagePullPolicy {
303
+ return false
304
+ }
305
+ if from .Workspace .IdleTimeout != "" && to .Workspace .IdleTimeout != from .Workspace .IdleTimeout {
306
+ return false
307
+ }
308
+ if from .Workspace .ProgressTimeout != "" && to .Workspace .ProgressTimeout != from .Workspace .ProgressTimeout {
309
+ return false
310
+ }
311
+ if from .Workspace .IgnoredUnrecoverableEvents != nil && to .Workspace .IgnoredUnrecoverableEvents != nil {
312
+
313
+ if len (from .Workspace .IgnoredUnrecoverableEvents ) != len (to .Workspace .IgnoredUnrecoverableEvents ) {
314
+ return false
315
+ }
316
+
317
+ sort .Strings (to .Workspace .IgnoredUnrecoverableEvents )
318
+ sort .Strings (from .Workspace .IgnoredUnrecoverableEvents )
319
+
320
+ for i := range from .Workspace .IgnoredUnrecoverableEvents {
321
+ if from .Workspace .IgnoredUnrecoverableEvents [i ] != to .Workspace .IgnoredUnrecoverableEvents [i ] {
322
+ return false
323
+ }
324
+ }
325
+ }
326
+ if from .Workspace .CleanupOnStop != nil {
327
+ if to .Workspace .CleanupOnStop == nil {
328
+ return false
329
+ }
330
+
331
+ if * to .Workspace .CleanupOnStop != * from .Workspace .CleanupOnStop {
332
+ return false
333
+ }
334
+ }
335
+ if from .Workspace .PodSecurityContext != nil {
336
+ if to .Workspace .PodSecurityContext == nil {
337
+ return false
338
+ }
339
+
340
+ // TODO: Using reflect.DeepEqual could potentially really degrade performance
341
+ if ! reflect .DeepEqual (from .Workspace .PodSecurityContext , to .Workspace .PodSecurityContext ) {
342
+ return false
343
+ }
344
+ }
345
+ if from .Workspace .DefaultStorageSize != nil {
346
+ if to .Workspace .DefaultStorageSize == nil {
347
+ return false
348
+ }
349
+ if from .Workspace .DefaultStorageSize .Common != nil {
350
+ if to .Workspace .DefaultStorageSize .Common == nil {
351
+ return false
352
+ }
353
+ if from .Workspace .DefaultStorageSize .Common .Cmp (* to .Workspace .DefaultStorageSize .Common ) != 0 {
354
+ return false
355
+ }
356
+
357
+ }
358
+ if from .Workspace .DefaultStorageSize .PerWorkspace != nil {
359
+ if to .Workspace .DefaultStorageSize .PerWorkspace == nil {
360
+ return false
361
+ }
362
+ if from .Workspace .DefaultStorageSize .PerWorkspace .Cmp (* to .Workspace .DefaultStorageSize .PerWorkspace ) != 0 {
363
+ return false
364
+ }
365
+ }
366
+
367
+ }
368
+ if from .Workspace .DefaultTemplate != nil {
369
+ if to .Workspace .DefaultTemplate == nil {
370
+ return false
371
+ }
372
+
373
+ // TODO: Using reflect.DeepEqual could potentially really degrade performance
374
+ if ! reflect .DeepEqual (from .Workspace .DefaultTemplate , to .Workspace .DefaultTemplate ) {
375
+ return false
376
+ }
377
+ }
378
+ }
379
+ return true
380
+ }
381
+
188
382
func mergeConfig (from , to * controller.OperatorConfiguration ) {
189
383
if to == nil {
190
384
to = & controller.OperatorConfiguration {}
0 commit comments