From 87e866eeea342485f3d44169495e9abdb9fd22d5 Mon Sep 17 00:00:00 2001 From: bdumpp <144795224+bdumpp@users.noreply.github.com> Date: Wed, 10 Jul 2024 09:40:41 +0200 Subject: [PATCH] :sparkles: Systems' read-only state is configurable (#322) go 1.22.0 -> 1.22.5 --- CONTRIBUTING.md | 3 +- api/config/v2alpha2/projectconfig_types.go | 3 + config/default/config.yaml | 1 + docs/configuration.md | 4 + go.mod | 4 +- .../controller/styra/system_controller.go | 7 +- .../controller/controller_suite_test.go | 1 + .../controller/system_controller_test.go | 348 ++++++++++++------ 8 files changed, 261 insertions(+), 110 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 07eaba2..1f3bedb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -96,8 +96,7 @@ available targets. The Makefile has targets for building and deploying the controller to a Kubernetes cluster. -1. Set desired docker image name using the IMG environment variable: `export - IMG=docker-image-name`. This will be used across make targets that refer to +1. Set desired docker image name using the IMG environment variable: `export IMG=docker-image-name`. This will be used across make targets that refer to image names. 2. Build the controller docker image: `make docker-build` 3. Push the controller docker image: `make docker-push` diff --git a/api/config/v2alpha2/projectconfig_types.go b/api/config/v2alpha2/projectconfig_types.go index 37f6ec5..890bab4 100644 --- a/api/config/v2alpha2/projectconfig_types.go +++ b/api/config/v2alpha2/projectconfig_types.go @@ -38,6 +38,9 @@ type ProjectConfig struct { // protection if it is not set on the resource. DeletionProtectionDefault bool `json:"deletionProtectionDefault"` + // ReadOnly sets the default values of ReadOnly for systems + ReadOnly bool `json:"readOnly"` + // DisableCRDWebhooks disables the CRD webhooks on the controller. If running // multiple controllers in the same cluster, only one will need to have it's // webhooks enabled. diff --git a/config/default/config.yaml b/config/default/config.yaml index 4dc7eb7..5d2b793 100644 --- a/config/default/config.yaml +++ b/config/default/config.yaml @@ -3,6 +3,7 @@ kind: ProjectConfig #controllerClass: #deletionProtectionDefault: #disableCRDWebhooks: +readOnly: true enableMigrations: false #gitCredentials: logLevel: 0 diff --git a/docs/configuration.md b/docs/configuration.md index c26fb4b..f9eb7ef 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -3,6 +3,7 @@ This document describes the different configuration options for the Styra Contro * `controllerClass` * `deletionProtectionDefault` +* `readOnly` * `disableCRDWebhooks` * `enableMigrations` * `gitCredentials` @@ -94,6 +95,9 @@ The controller can be configured to add a prefix and a suffix to the Systems nam ## Delete Protection Custom Resources can have delete protection, means that they will not be deleted by the controller in Styra. The default can be configured by setting `deletionProtectionDefault`. +## Read Only +Styra Systems can be read-only, meaning they cannot be changed in the Styra GUI. This can be configured by setting `readOnly`. + ## EnableMigrations An annotation that allows configuring Systems in Kubernetes to link to a specific system in Styra. The ID that the system in Kubernetes should link to is configured by setting `styra-contoller/migration-id: [styra system id]` annotation on Kubernetes system resource. Should only be set while migrating. diff --git a/go.mod b/go.mod index c1da43b..71d75e7 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/bankdata/styra-controller -go 1.22.0 - -toolchain go1.22.1 +go 1.22.5 require ( github.com/ahmetb/gen-crd-api-reference-docs v0.3.0 diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go index 8a53dc0..4ceea23 100644 --- a/internal/controller/styra/system_controller.go +++ b/internal/controller/styra/system_controller.go @@ -1087,7 +1087,7 @@ func (r *SystemReconciler) specToSystemConfig(system *v1beta1.System) *styra.Sys cfg := &styra.SystemConfig{ Name: system.DisplayName(r.Config.SystemPrefix, r.Config.SystemSuffix), Type: "custom", - ReadOnly: true, + ReadOnly: r.Config.ReadOnly, } if len(system.Spec.DecisionMappings) > 0 { @@ -1156,6 +1156,11 @@ func (r *SystemReconciler) systemNeedsUpdate(log logr.Logger, system *v1beta1.Sy return true } + if cfg.ReadOnly != r.Config.ReadOnly { + log.Info("System needs update: read only is not equal") + return true + } + expectedModel := r.specToSystemConfig(system) if !reflect.DeepEqual(expectedModel.SourceControl, cfg.SourceControl) { log.Info("System needs update: source control is not equal") diff --git a/test/integration/controller/controller_suite_test.go b/test/integration/controller/controller_suite_test.go index ed388ba..2eacf4a 100644 --- a/test/integration/controller/controller_suite_test.go +++ b/test/integration/controller/controller_suite_test.go @@ -122,6 +122,7 @@ var _ = ginkgo.BeforeSuite(func() { IdentityProvider: "AzureAD Bankdata", JWTGroupsClaim: "groups", }, + ReadOnly: true, }, } diff --git a/test/integration/controller/system_controller_test.go b/test/integration/controller/system_controller_test.go index cbbc737..a1032d7 100644 --- a/test/integration/controller/system_controller_test.go +++ b/test/integration/controller/system_controller_test.go @@ -54,6 +54,12 @@ var _ = ginkgo.Describe("SystemReconciler.Reconcile", ginkgo.Label("integration" Spec: spec, } + cfg := &styra.SystemConfig{ + ID: "system_id", + Name: key.String(), + ReadOnly: true, + } + ctx := context.Background() ginkgo.By("Creating the system") @@ -64,21 +70,18 @@ var _ = ginkgo.Describe("SystemReconciler.Reconcile", ginkgo.Label("integration" }, nil).Once() styraClientMock.On("CreateSystem", mock.Anything, mock.Anything).Return(&styra.CreateSystemResponse{ - StatusCode: http.StatusOK, - SystemConfig: &styra.SystemConfig{ - ID: "default_test", - Name: key.String(), - }, + StatusCode: http.StatusOK, + SystemConfig: cfg, }, nil).Once() - styraClientMock.On("DeletePolicy", mock.Anything, "systems/default_test/rules").Return(&styra.DeletePolicyResponse{ + styraClientMock.On("DeletePolicy", mock.Anything, "systems/system_id/rules").Return(&styra.DeletePolicyResponse{ StatusCode: http.StatusOK, }, nil).Once() - styraClientMock.On("DeletePolicy", mock.Anything, "systems/default_test/test").Return(&styra.DeletePolicyResponse{ + styraClientMock.On("DeletePolicy", mock.Anything, "systems/system_id/test").Return(&styra.DeletePolicyResponse{ StatusCode: http.StatusOK, }, nil).Once() - styraClientMock.On("UpdateSystem", mock.Anything, "default_test", &styra.UpdateSystemRequest{ + styraClientMock.On("UpdateSystem", mock.Anything, "system_id", &styra.UpdateSystemRequest{ SystemConfig: &styra.SystemConfig{ Name: key.String(), Type: "custom", @@ -90,20 +93,20 @@ var _ = ginkgo.Describe("SystemReconciler.Reconcile", ginkgo.Label("integration" Name: key.String(), Type: "custom", ReadOnly: true, - ID: "default_test", + ID: "system_id", }, }, nil).Once() styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, }, nil).Once() styraClientMock.On("CreateRoleBinding", mock.Anything, &styra.CreateRoleBindingRequest{ ResourceFilter: &styra.ResourceFilter{ - ID: "default_test", + ID: "system_id", Kind: styra.RoleBindingKindSystem, }, RoleID: styra.RoleSystemViewer, @@ -112,59 +115,60 @@ var _ = ginkgo.Describe("SystemReconciler.Reconcile", ginkgo.Label("integration" StatusCode: http.StatusOK, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() // new reconcile as we create opatoken secret that we are watching - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return( + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return( &styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "default_test", - Name: key.String(), + ID: "system_id", + Name: key.String(), + ReadOnly: true, }, }, nil).Once() - styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() // new reconcile as we create opa configmap that we are watching - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return( + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return( &styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "default_test", - Name: key.String(), + ID: "system_id", + Name: key.String(), + ReadOnly: true, }, }, nil).Once() styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() @@ -203,11 +207,11 @@ var _ = ginkgo.Describe("SystemReconciler.Reconcile", ginkgo.Label("integration" bearer: token_path: /etc/opa/auth/token labels: - system-id: default_test + system-id: system_id system-type: custom discovery: name: discovery - prefix: /systems/default_test + prefix: /systems/system_id service: styra ` }, timeout, interval).Should(gomega.BeTrue()) @@ -260,7 +264,7 @@ discovery: Name: "default_local_plane", } - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return( + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return( &styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ @@ -272,22 +276,22 @@ discovery: styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() // new reconcile as we create opa configmap that we watch - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return( + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return( &styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ @@ -299,22 +303,22 @@ discovery: styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() // new reconcile as we create slp configmap that we watch - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return( + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return( &styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ @@ -326,15 +330,15 @@ discovery: styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() @@ -350,7 +354,7 @@ discovery: - name: styra url: http://default_local_plane/v1 labels: - system-id: default_test + system-id: system_id system-type: custom discovery: name: discovery @@ -371,11 +375,11 @@ discovery: bearer: token_path: /etc/slp/auth/token labels: - system-id: default_test + system-id: system_id system-type: custom discovery: name: discovery - resource: /systems/default_test/discovery + resource: /systems/system_id/discovery service: styra ` return fetchSuceeded && fetched.Data["slp.yaml"] == expectedConfigMapContent @@ -430,7 +434,7 @@ discovery: }, } - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return( + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return( &styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ @@ -440,7 +444,7 @@ discovery: }, }, nil).Once() - styraClientMock.On("UpdateSystem", mock.Anything, "default_test", &styra.UpdateSystemRequest{ + styraClientMock.On("UpdateSystem", mock.Anything, "system_id", &styra.UpdateSystemRequest{ SystemConfig: &styra.SystemConfig{ Name: key.String(), Type: "custom", @@ -464,7 +468,7 @@ discovery: }).Return(&styra.UpdateSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "default_test", + ID: "system_id", Name: key.String(), DecisionMappings: map[string]styra.DecisionMapping{ "": {}, @@ -486,15 +490,15 @@ discovery: styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() @@ -538,11 +542,12 @@ discovery: {Kind: styrav1beta1.SubjectKindUser, Name: "test2@test.com"}, } - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return(&styra.GetSystemResponse{ + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return(&styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "default_test", - Name: key.String(), + ID: "system_id", + Name: key.String(), + ReadOnly: true, DecisionMappings: map[string]styra.DecisionMapping{ "": {}, "test": { @@ -579,7 +584,7 @@ discovery: styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, @@ -594,9 +599,9 @@ discovery: StatusCode: http.StatusOK, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() @@ -648,11 +653,12 @@ discovery: {Kind: styrav1beta1.SubjectKindGroup, Name: "Group2"}, } - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return(&styra.GetSystemResponse{ + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return(&styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "default_test", - Name: key.String(), + ID: "system_id", + Name: key.String(), + ReadOnly: true, DecisionMappings: map[string]styra.DecisionMapping{ "": {}, "test": { @@ -681,7 +687,7 @@ discovery: styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer, @@ -708,9 +714,9 @@ discovery: StatusCode: http.StatusOK, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() @@ -754,11 +760,12 @@ discovery: toUpdate.Spec.Subjects = []styrav1beta1.Subject{} - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return(&styra.GetSystemResponse{ + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return(&styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "default_test", - Name: key.String(), + ID: "system_id", + Name: key.String(), + ReadOnly: true, DecisionMappings: map[string]styra.DecisionMapping{ "": {}, "test": { @@ -779,7 +786,7 @@ discovery: styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ Rolebindings: []*styra.RoleBindingConfig{ { @@ -827,9 +834,9 @@ discovery: StatusCode: http.StatusOK, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() @@ -870,12 +877,13 @@ discovery: toUpdate.Spec.Datasources = []styrav1beta1.Datasource{{Path: "test"}} - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return( + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return( &styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "default_test", - Name: key.String(), + ID: "system_id", + Name: key.String(), + ReadOnly: true, DecisionMappings: map[string]styra.DecisionMapping{ "": {}, "test": { @@ -896,15 +904,15 @@ discovery: styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() @@ -912,7 +920,7 @@ discovery: styraClientMock.On( "UpsertDatasource", mock.Anything, - fmt.Sprintf("systems/%s/%s", "default_test", "test"), + fmt.Sprintf("systems/%s/%s", "system_id", "test"), &styra.UpsertDatasourceRequest{Category: "rest"}, ).Return(&styra.UpsertDatasourceResponse{ StatusCode: http.StatusOK, @@ -922,8 +930,8 @@ discovery: "SystemDatasourceChanged", mock.Anything, mock.Anything, - "default_test", - "systems/default_test/test", + "system_id", + "systems/system_id/test", ).Return(nil).Once() gomega.Expect(k8sClient.Update(ctx, toUpdate)).To(gomega.Succeed()) @@ -987,11 +995,12 @@ discovery: }, } - styraClientMock.On("GetSystem", mock.Anything, "default_test").Return(&styra.GetSystemResponse{ + styraClientMock.On("GetSystem", mock.Anything, "system_id").Return(&styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "default_test", - Name: key.String(), + ID: "system_id", + Name: key.String(), + ReadOnly: true, DecisionMappings: map[string]styra.DecisionMapping{ "": {}, "test": { @@ -1012,21 +1021,21 @@ discovery: }, Datasources: []*styra.DatasourceConfig{ { - ID: "systems/default_test/test", + ID: "systems/system_id/test", Category: "rest", }, }, }, }, nil).Once() - styraClientMock.On("CreateUpdateSecret", mock.Anything, "systems/default_test/git", &styra.CreateUpdateSecretsRequest{ + styraClientMock.On("CreateUpdateSecret", mock.Anything, "systems/system_id/git", &styra.CreateUpdateSecretsRequest{ Name: "git-user", Secret: "git-password", }).Return(&styra.CreateUpdateSecretResponse{ StatusCode: http.StatusOK, }, nil).Once() - styraClientMock.On("UpdateSystem", mock.Anything, "default_test", &styra.UpdateSystemRequest{ + styraClientMock.On("UpdateSystem", mock.Anything, "system_id", &styra.UpdateSystemRequest{ SystemConfig: &styra.SystemConfig{ Name: key.String(), ReadOnly: true, @@ -1051,14 +1060,14 @@ discovery: }, SourceControl: &styra.SourceControlConfig{ Origin: styra.GitRepoConfig{ - Credentials: "systems/default_test/git", + Credentials: "systems/system_id/git", }, }, }, }).Return(&styra.UpdateSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "default_test", + ID: "system_id", Name: key.String(), ReadOnly: true, Type: "custom", @@ -1082,12 +1091,12 @@ discovery: }, SourceControl: &styra.SourceControlConfig{ Origin: styra.GitRepoConfig{ - Credentials: "systems/default_test/git", + Credentials: "systems/system_id/git", }, }, Datasources: []*styra.DatasourceConfig{ { - ID: "systems/default_test/test", + ID: "systems/system_id/test", Category: "rest", }, }, @@ -1096,15 +1105,15 @@ discovery: styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "default_test", + ResourceID: "system_id", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "default_test").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "default_test", + SystemID: "system_id", Token: "opa-token-123", SystemType: "custom", }, nil).Once() @@ -1206,7 +1215,7 @@ discovery: ginkgo.By("Deleting the system") - styraClientMock.On("DeleteSystem", mock.Anything, "default_test").Return(&styra.DeleteSystemResponse{ + styraClientMock.On("DeleteSystem", mock.Anything, "system_id").Return(&styra.DeleteSystemResponse{ StatusCode: http.StatusOK, }, nil) @@ -1220,6 +1229,8 @@ discovery: resetMock(&styraClientMock.Mock) + ginkgo.By("Connecting to an existing Styra System by name") + key2 := types.NamespacedName{ Name: "test2", Namespace: "default", @@ -1233,45 +1244,45 @@ discovery: Spec: spec, } - ginkgo.By("Connecting to an existing Styra System") - - cfg := &styra.SystemConfig{ - ID: "system_id", - Name: key2.String(), + cfg2 := &styra.SystemConfig{ + ID: "system_id2", + Name: key2.String(), + ReadOnly: true, } styraClientMock.On("GetSystemByName", mock.Anything, key2.String()).Return(&styra.GetSystemResponse{ StatusCode: http.StatusOK, - SystemConfig: cfg, + SystemConfig: cfg2, }, nil).Once() styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "system_id", + ResourceID: "system_id2", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, }, nil).Once() - styraClientMock.On("GetOPAConfig", mock.Anything, "system_id").Return(styra.OPAConfig{ + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id2").Return(styra.OPAConfig{ HostURL: "styra-url-123", - SystemID: "system_id", + SystemID: "system_id2", Token: "opa-token-123", SystemType: "custom", }, nil) - styraClientMock.On("GetSystem", mock.Anything, "system_id").Return( + styraClientMock.On("GetSystem", mock.Anything, "system_id2").Return( &styra.GetSystemResponse{ StatusCode: http.StatusOK, SystemConfig: &styra.SystemConfig{ - ID: "system_id", - Name: key2.String(), + ID: "system_id2", + Name: key2.String(), + ReadOnly: true, }, }, nil) styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ ResourceKind: styra.RoleBindingKindSystem, - ResourceID: "system_id", + ResourceID: "system_id2", }).Return(&styra.ListRoleBindingsV2Response{ StatusCode: http.StatusOK, Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, @@ -1322,7 +1333,136 @@ discovery: return false } return finalizer.IsSet(fetched) && - fetched.Status.ID == "system_id" && + fetched.Status.ID == "system_id2" && + fetched.Status.Phase == styrav1beta1.SystemPhaseCreated && + fetched.Status.Ready + }, timeout, interval).Should(gomega.BeTrue()) + + resetMock(&styraClientMock.Mock) + + // Create new system, which was not ReadOnly, so it gets updated + + ginkgo.By("Changing ReadOnly flag in system") + + key3 := types.NamespacedName{ + Name: "test3", + Namespace: "default", + } + + // system v1beta1 + toCreate3 := &styrav1beta1.System{ + ObjectMeta: metav1.ObjectMeta{ + Name: key3.Name, + Namespace: key3.Namespace, + }, + Spec: spec, + } + + cfg3 := &styra.SystemConfig{ + ID: "system_id3", + Name: key3.String(), + ReadOnly: false, + } + + styraClientMock.On("GetSystemByName", mock.Anything, key3.String()).Return(&styra.GetSystemResponse{ + StatusCode: http.StatusOK, + SystemConfig: cfg3, + }, nil).Once() + + styraClientMock.On("UpdateSystem", mock.Anything, "system_id3", &styra.UpdateSystemRequest{ + SystemConfig: &styra.SystemConfig{ + Name: key3.String(), + ReadOnly: true, + Type: "custom", + }, + }).Return(&styra.UpdateSystemResponse{ + StatusCode: http.StatusOK, + SystemConfig: &styra.SystemConfig{ + ID: cfg3.ID, + Name: key3.String(), + ReadOnly: true, + }, + }, nil).Once() + + styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ + ResourceKind: styra.RoleBindingKindSystem, + ResourceID: "system_id3", + }).Return(&styra.ListRoleBindingsV2Response{ + StatusCode: http.StatusOK, + Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, + }, nil).Once() + + styraClientMock.On("GetOPAConfig", mock.Anything, "system_id3").Return(styra.OPAConfig{ + HostURL: "styra-url-123", + SystemID: "system_id3", + Token: "opa-token-123", + SystemType: "custom", + }, nil) + + styraClientMock.On("GetSystem", mock.Anything, "system_id3").Return( + &styra.GetSystemResponse{ + StatusCode: http.StatusOK, + SystemConfig: &styra.SystemConfig{ + ID: "system_id3", + Name: key3.String(), + ReadOnly: true, + }, + }, nil) + + styraClientMock.On("ListRoleBindingsV2", mock.Anything, &styra.ListRoleBindingsV2Params{ + ResourceKind: styra.RoleBindingKindSystem, + ResourceID: "system_id3", + }).Return(&styra.ListRoleBindingsV2Response{ + StatusCode: http.StatusOK, + Rolebindings: []*styra.RoleBindingConfig{{ID: "1", RoleID: styra.RoleSystemViewer}}, + }, nil) + + gomega.Expect(k8sClient.Create(ctx, toCreate3)).To(gomega.Succeed()) + gomega.Eventually(func() bool { + var ( + getSystem int + getSystemByName int + createSystem int + deletePolicy int + listRolebindingsV2 int + createRoleBinding int + getOPAConfig int + ) + for _, call := range styraClientMock.Calls { + switch call.Method { + case "GetSystem": + getSystem++ + case "CreateSystem": + createSystem++ + case "GetSystemByName": + getSystemByName++ + case "DeletePolicy": + deletePolicy++ + case "ListRoleBindingsV2": + listRolebindingsV2++ + case "CreateRoleBinding": + createRoleBinding++ + case "GetOPAConfig": + getOPAConfig++ + } + } + return getSystem == 2 && + getSystemByName == 1 && + createSystem == 0 && + deletePolicy == 0 && + listRolebindingsV2 == 3 && + createRoleBinding == 0 && + getOPAConfig == 3 + }, timeout, interval).Should(gomega.BeTrue()) + + gomega.Eventually(func() bool { + fetched := &styrav1beta1.System{} + + if err := k8sClient.Get(ctx, key3, fetched); err != nil { + return false + } + return finalizer.IsSet(fetched) && + fetched.Status.ID == "system_id3" && fetched.Status.Phase == styrav1beta1.SystemPhaseCreated && fetched.Status.Ready }, timeout, interval).Should(gomega.BeTrue())