Skip to content

Commit 8816b16

Browse files
akafazovOlegErshovCopilotnexus49
authored
feat: implement feature toggles API (#65)
* feat: feature toggles API On-behalf-of: @SAP [email protected] Signed-off-by: Angel Kafazov <[email protected]> * add getting-started cc On-behalf-of: @SAP [email protected] Signed-off-by: Angel Kafazov <[email protected]> * increase timeout On-behalf-of: @SAP [email protected] Signed-off-by: Angel Kafazov <[email protected]> * speed up flux deployments On-behalf-of: @SAP [email protected] Signed-off-by: Angel Kafazov <[email protected]> * refactor(tests): enhance logging and improve test setup for better clarity On-behalf-of: @SAP [email protected] Signed-off-by: Angel Kafazov <[email protected]> * test: implement Test_applyDirStructure with mock expectations and assertions On-behalf-of: @SAP [email protected] Signed-off-by: Angel Kafazov <[email protected]> * Update pkg/subroutines/featuretoggles.go Co-authored-by: Oleg Ershov <[email protected]> * add requested changes On-behalf-of: @SAP [email protected] Signed-off-by: Angel Kafazov <[email protected]> * Update pkg/subroutines/featuretoggles.go Co-authored-by: Copilot <[email protected]> * chore: some lint errors Signed-off-by: Bastian Echterhölter <[email protected]> On-behalf-of: @SAP <[email protected]> --------- Signed-off-by: Angel Kafazov <[email protected]> Signed-off-by: Bastian Echterhölter <[email protected]> Co-authored-by: Oleg Ershov <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Bastian Echterhölter <[email protected]>
1 parent 943a1be commit 8816b16

File tree

21 files changed

+652
-308
lines changed

21 files changed

+652
-308
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ spec:
151151

152152
Those values are passed 1-1 to the `platform-mesh-operator-components` chart, deployed by the "Deployment" subroutine.
153153

154+
### Feature toggles
155+
156+
Certain features can be toggled by the user via the API:
157+
158+
#### feature-enable-getting-started
159+
160+
```yaml
161+
spec:
162+
featureToggles:
163+
- name: "feature-enable-getting-started"
164+
parameters: {}
165+
```
166+
167+
This applies the needed ContentConfiguration for the Getting Started UI page.
168+
154169
## Subroutines
155170

156171
The platform-mesh-operator processes the PlatformMesh resource through several subroutines:

api/v1alpha1/platformmesh_types.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,18 @@ import (
2323

2424
// PlatformMeshSpec defines the desired state of PlatformMesh
2525
type PlatformMeshSpec struct {
26-
Exposure *ExposureConfig `json:"exposure,omitempty"`
27-
Kcp Kcp `json:"kcp,omitempty"`
28-
Values apiextensionsv1.JSON `json:"values,omitempty"`
29-
OCM *OCMConfig `json:"ocm,omitempty"`
26+
Exposure *ExposureConfig `json:"exposure,omitempty"`
27+
Kcp Kcp `json:"kcp,omitempty"`
28+
Values apiextensionsv1.JSON `json:"values,omitempty"`
29+
OCM *OCMConfig `json:"ocm,omitempty"`
30+
FeatureToggles []FeatureToggle `json:"featureToggles,omitempty"`
3031
}
32+
33+
type FeatureToggle struct {
34+
Name string `json:"name,omitempty"`
35+
Parameters map[string]string `json:"parameters,omitempty"`
36+
}
37+
3138
type OCMConfig struct {
3239
Repo *RepoConfig `json:"repo,omitempty"`
3340
Component *ComponentConfig `json:"component,omitempty"`

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/core.platform-mesh.io_platformmeshes.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ spec:
8080
protocol:
8181
type: string
8282
type: object
83+
featureToggles:
84+
items:
85+
properties:
86+
name:
87+
type: string
88+
parameters:
89+
additionalProperties:
90+
type: string
91+
type: object
92+
type: object
93+
type: array
8394
kcp:
8495
properties:
8596
extraDefaultAPIBindings:

config/rbac/role.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,23 @@ rules:
3030
- get
3131
- patch
3232
- update
33+
- apiGroups:
34+
- realm.keycloak.crossplane.io
35+
resources:
36+
- realms
37+
verbs:
38+
- get
39+
- list
40+
- watch
41+
- apiGroups:
42+
- realm.keycloak.crossplane.io
43+
resources:
44+
- realms/finalizers
45+
verbs:
46+
- update
47+
- apiGroups:
48+
- realm.keycloak.crossplane.io
49+
resources:
50+
- realms/status
51+
verbs:
52+
- get

internal/config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,8 @@ type OperatorConfig struct {
3030
BaseDomain string `mapstructure:"subroutines-patch-oidc-basedomain" default:"portal.dev.local:8443"`
3131
DomainCALookup bool `mapstructure:"subroutines-patch-oidc-domain-ca-lookup" default:"false"`
3232
} `mapstructure:",squash"`
33+
FeatureToggles struct {
34+
Enabled bool `mapstructure:"subroutines-feature-toggles-enabled" default:"false"`
35+
} `mapstructure:",squash"`
3336
} `mapstructure:",squash"`
3437
}

internal/controller/platformmesh_controller.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ func NewPlatformMeshReconciler(log *logger.Logger, mgr ctrl.Manager, cfg *config
8888
if cfg.Subroutines.ProviderSecret.Enabled {
8989
subs = append(subs, subroutines.NewProviderSecretSubroutine(mgr.GetClient(), &subroutines.Helper{}, subroutines.DefaultHelmGetter{}, kcpUrl))
9090
}
91+
if cfg.Subroutines.FeatureToggles.Enabled {
92+
subs = append(subs, subroutines.NewFeatureToggleSubroutine(mgr.GetClient(), &subroutines.Helper{}, cfg, kcpUrl))
93+
}
9194
return &PlatformMeshReconciler{
9295
lifecycle: controllerruntime.NewLifecycleManager(subs, operatorName,
9396
pmReconcilerName, mgr.GetClient(), log).WithConditionManagement(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
apiVersion: ui.platform-mesh.io/v1alpha1
2+
kind: ContentConfiguration
3+
metadata:
4+
generation: 1
5+
labels:
6+
ui.platform-mesh.io/entity: main
7+
name: main-home-overview-getting-started
8+
spec:
9+
inlineConfiguration:
10+
content: |-
11+
{
12+
"name": "getting-started",
13+
"creationTimestamp": "2022-09-26T11:39:17Z",
14+
"luigiConfigFragment": {
15+
"data": {
16+
"nodes": [
17+
{
18+
"entityType": "main.overview::compound",
19+
"url": "/assets/openmfp-portal-ui-wc.js#getting-started",
20+
"layoutConfig": {
21+
"column": "1 / -1"
22+
},
23+
"webcomponent": {
24+
"selfRegistered": true
25+
}
26+
}
27+
]
28+
}
29+
}
30+
}
31+
contentType: json

pkg/subroutines/deployment_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ func TestDeployTestSuite(t *testing.T) {
3737
func (s *DeployTestSuite) SetupTest() {
3838
s.clientMock = new(mocks.Client)
3939
s.helperMock = new(mocks.KcpHelper)
40-
s.log, _ = logger.New(logger.DefaultConfig())
40+
cfgLog := logger.DefaultConfig()
41+
cfgLog.Level = "debug"
42+
cfgLog.NoJSON = true
43+
cfgLog.Name = "DeployTestSuite"
44+
s.log, _ = logger.New(cfgLog)
4145

4246
cfg := pmconfig.CommonServiceConfig{}
4347
operatorCfg := config.OperatorConfig{

pkg/subroutines/featuretoggles.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package subroutines
2+
3+
import (
4+
"context"
5+
"path/filepath"
6+
7+
"github.com/platform-mesh/golang-commons/controller/lifecycle/runtimeobject"
8+
"github.com/platform-mesh/golang-commons/errors"
9+
"github.com/platform-mesh/golang-commons/logger"
10+
corev1alpha1 "github.com/platform-mesh/platform-mesh-operator/api/v1alpha1"
11+
"github.com/platform-mesh/platform-mesh-operator/internal/config"
12+
"k8s.io/client-go/rest"
13+
ctrl "sigs.k8s.io/controller-runtime"
14+
"sigs.k8s.io/controller-runtime/pkg/client"
15+
)
16+
17+
const FeatureToggleSubroutineName = "FeatureToggleSubroutine"
18+
19+
type KubeconfigBuilder interface {
20+
Build(ctx context.Context, client client.Client, kcpUrl string) (*rest.Config, error)
21+
}
22+
23+
type defaultKubeconfigBuilder struct{}
24+
25+
func (defaultKubeconfigBuilder) Build(ctx context.Context, client client.Client, kcpUrl string) (*rest.Config, error) {
26+
return buildKubeconfig(ctx, client, kcpUrl)
27+
}
28+
29+
type FeatureToggleSubroutine struct {
30+
client client.Client
31+
workspaceDirectory string
32+
kcpUrl string
33+
kubeconfigBuilder KubeconfigBuilder
34+
kcpHelper KcpHelper
35+
}
36+
37+
func NewFeatureToggleSubroutine(client client.Client, helper KcpHelper, operatorCfg *config.OperatorConfig, kcpUrl string) *FeatureToggleSubroutine {
38+
return &FeatureToggleSubroutine{
39+
client: client,
40+
workspaceDirectory: filepath.Join(operatorCfg.WorkspaceDir, "/manifests/features/"),
41+
kcpUrl: kcpUrl,
42+
kubeconfigBuilder: defaultKubeconfigBuilder{},
43+
kcpHelper: helper,
44+
}
45+
}
46+
47+
func (r *FeatureToggleSubroutine) GetName() string {
48+
return FeatureToggleSubroutineName
49+
}
50+
51+
func (r *FeatureToggleSubroutine) Finalize(_ context.Context, _ runtimeobject.RuntimeObject) (ctrl.Result, errors.OperatorError) {
52+
return ctrl.Result{}, nil
53+
}
54+
55+
func (r *FeatureToggleSubroutine) Finalizers() []string { // coverage-ignore
56+
return []string{}
57+
}
58+
59+
func (r *FeatureToggleSubroutine) Process(ctx context.Context, runtimeObj runtimeobject.RuntimeObject) (ctrl.Result, errors.OperatorError) {
60+
log := logger.LoadLoggerFromContext(ctx).ChildLogger("subroutine", r.GetName())
61+
62+
inst := runtimeObj.(*corev1alpha1.PlatformMesh)
63+
for _, ft := range inst.Spec.FeatureToggles {
64+
switch ft.Name {
65+
case "feature-enable-getting-started":
66+
// Implement the logic to enable the getting started feature
67+
log.Info().Msg("Getting started feature enabled")
68+
return r.FeatureGettingStarted(ctx, inst)
69+
default:
70+
log.Warn().Str("featureToggle", ft.Name).Msg("Unknown feature toggle")
71+
}
72+
}
73+
74+
return ctrl.Result{}, nil
75+
}
76+
77+
func (r *FeatureToggleSubroutine) FeatureGettingStarted(ctx context.Context, inst *corev1alpha1.PlatformMesh) (ctrl.Result, errors.OperatorError) {
78+
log := logger.LoadLoggerFromContext(ctx).ChildLogger("subroutine", r.GetName())
79+
80+
// Implement the logic to enable the getting started feature
81+
log.Info().Msg("Getting started feature enabled")
82+
83+
// Build kcp kubeconfig
84+
cfg, err := buildKubeconfig(ctx, r.client, r.kcpUrl)
85+
if err != nil {
86+
log.Error().Err(err).Msg("Failed to build kubeconfig")
87+
return ctrl.Result{}, errors.NewOperatorError(errors.Wrap(err, "Failed to build kubeconfig"), true, false)
88+
}
89+
90+
dir := r.workspaceDirectory + "/feature-enable-getting-started"
91+
92+
err = ApplyDirStructure(ctx, dir, "root", cfg, make(map[string]string), inst, r.kcpHelper)
93+
if err != nil {
94+
log.Err(err).Msg("Failed to apply dir structure")
95+
return ctrl.Result{}, errors.NewOperatorError(errors.Wrap(err, "Failed to apply dir structure"), true, false)
96+
}
97+
98+
return ctrl.Result{}, nil
99+
}

0 commit comments

Comments
 (0)