From 985044e664e590cac1e3cb9ef61cb81d261851c1 Mon Sep 17 00:00:00 2001 From: ChrBlaesBD Date: Thu, 19 Feb 2026 10:31:30 +0100 Subject: [PATCH 01/14] added revision field in bundleCOnfig --- pkg/ocp/bundles.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/ocp/bundles.go b/pkg/ocp/bundles.go index 4e3b1e0e..159c51bb 100644 --- a/pkg/ocp/bundles.go +++ b/pkg/ocp/bundles.go @@ -36,6 +36,7 @@ type BundleConfig struct { Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` ObjectStorage ObjectStorage `json:"object_storage,omitempty" yaml:"object_storage,omitempty"` Requirements []Requirement `json:"requirements,omitempty" yaml:"requirements,omitempty"` + Revision string `json:"revision,omitempty" yaml:"revision,omitempty"` ExcludedFiles []string `json:"excluded_files,omitempty" yaml:"excluded_files,omitempty"` } From 63af3525dc8022214f34c3d96a7150a9bd015cc7 Mon Sep 17 00:00:00 2001 From: ChrBlaesBD Date: Mon, 23 Feb 2026 10:52:46 +0100 Subject: [PATCH 02/14] adding revision to putBundleResponse --- config/samples/styra_v1beta1_system.yaml | 10 ++++- .../controller/styra/system_controller.go | 19 +++++++- pkg/ocp/bundles.go | 11 +---- .../controller/system_controller_test.go | 43 +++++++++++++++++++ 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/config/samples/styra_v1beta1_system.yaml b/config/samples/styra_v1beta1_system.yaml index df397cfc..994c8a79 100644 --- a/config/samples/styra_v1beta1_system.yaml +++ b/config/samples/styra_v1beta1_system.yaml @@ -8,6 +8,7 @@ metadata: app.kubernetes.io/managed-by: kustomize app.kubernetes.io/created-by: styra-controller styra-controller/control-plane: opa-control-plane + styra-controller/class: dev name: system-sample spec: decisionMappings: @@ -20,5 +21,12 @@ spec: path: result.reasons datasources: - path: "test" - # TODO(user): Add fields here + sourceControl: + origin: + path: policy + reference: refs/heads/master + url: 'https://github.com/bd-b0100/pol-library-stress-tester' + + + \ No newline at end of file diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go index 4518b163..cd0d0946 100644 --- a/internal/controller/styra/system_controller.go +++ b/internal/controller/styra/system_controller.go @@ -18,6 +18,7 @@ package styra import ( "context" + "crypto/sha256" "encoding/json" "fmt" "net/http" @@ -420,7 +421,7 @@ func (r *SystemReconciler) ocpReconcile( system.SetCondition(v1beta1.ConditionTypeSystemSourceUpdated, metav1.ConditionTrue) reconcileSystemBundleStart := time.Now() - result, err = r.reconcileSystemBundle(ctx, uniqueName, requirements) + result, err = r.reconcileSystemBundle(ctx, system, uniqueName, requirements) r.Metrics.ReconcileSegmentTime. WithLabelValues("reconcileSystemBundleOcp"). Observe(time.Since(reconcileSystemBundleStart).Seconds()) @@ -801,6 +802,7 @@ func (r *SystemReconciler) reconcileS3Credentials( func (r *SystemReconciler) reconcileSystemBundle( ctx context.Context, + system *v1beta1.System, uniqueName string, requirements []ocp.Requirement) (ctrl.Result, error) { if r.Config.OPAControlPlaneConfig.BundleObjectStorage.S3 == nil { @@ -821,6 +823,7 @@ func (r *SystemReconciler) reconcileSystemBundle( Name: uniqueName, ObjectStorage: objectStorage, Requirements: requirements, + Revision: bundleRevision(system, requirements), } err := r.OCP.PutBundle(ctx, bundle) @@ -830,6 +833,20 @@ func (r *SystemReconciler) reconcileSystemBundle( return ctrl.Result{}, nil } +func bundleRevision(system *v1beta1.System, requirements []ocp.Requirement) string { + if system.Spec.SourceControl == nil || system.Spec.SourceControl.Origin.Commit == "" { + return "" + } + + requirementsJSON, err := json.Marshal(requirements) + if err != nil { + return fmt.Sprintf(`git:{"commit":"%s"}`, system.Spec.SourceControl.Origin.Commit) + } + + dataHash := sha256.Sum256(requirementsJSON) + return fmt.Sprintf(`git:{"commit":"%s"},data:%x`, system.Spec.SourceControl.Origin.Commit, dataHash) +} + func (r *SystemReconciler) reconcileSystemSource( ctx context.Context, log logr.Logger, diff --git a/pkg/ocp/bundles.go b/pkg/ocp/bundles.go index 159c51bb..136f6980 100644 --- a/pkg/ocp/bundles.go +++ b/pkg/ocp/bundles.go @@ -30,16 +30,6 @@ const ( endpointV1Bundles = "/v1/bundles" ) -// BundleConfig represents the configuration of a bundle in the OCP APIs. -type BundleConfig struct { - Name string `json:"-" yaml:"-"` - Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` - ObjectStorage ObjectStorage `json:"object_storage,omitempty" yaml:"object_storage,omitempty"` - Requirements []Requirement `json:"requirements,omitempty" yaml:"requirements,omitempty"` - Revision string `json:"revision,omitempty" yaml:"revision,omitempty"` - ExcludedFiles []string `json:"excluded_files,omitempty" yaml:"excluded_files,omitempty"` -} - // ObjectStorage represents the object storage configuration for a bundle. type ObjectStorage struct { AmazonS3 *AmazonS3 `json:"aws,omitempty" yaml:"aws,omitempty"` @@ -85,6 +75,7 @@ type PutBundleRequest struct { Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` ObjectStorage ObjectStorage `json:"object_storage,omitempty" yaml:"object_storage,omitempty"` Requirements []Requirement `json:"requirements,omitempty" yaml:"requirements,omitempty"` + Revision string `json:"revision,omitempty" yaml:"revision,omitempty"` ExcludedFiles []string `json:"excluded_files,omitempty" yaml:"excluded_files,omitempty"` } diff --git a/test/integration/controller/system_controller_test.go b/test/integration/controller/system_controller_test.go index 7313c567..1bebdfa5 100644 --- a/test/integration/controller/system_controller_test.go +++ b/test/integration/controller/system_controller_test.go @@ -18,6 +18,7 @@ package styra import ( "context" + "crypto/sha256" "encoding/json" "fmt" "net/http" @@ -44,6 +45,15 @@ import ( "github.com/bankdata/styra-controller/pkg/styra" ) +func expectedBundleRevision(commit string, requirements []ocp.Requirement) string { + requirementsJSON, err := json.Marshal(requirements) + if err != nil { + return fmt.Sprintf(`git:{\"commit\":\"%s\"}`, commit) + } + dataHash := sha256.Sum256(requirementsJSON) + return fmt.Sprintf(`git:{\"commit\":\"%s\"},data:%x`, commit, dataHash) +} + var _ = ginkgo.Describe("SystemReconciler.Reconcile", ginkgo.Label("integration"), func() { ginkgo.It("should reconcile", func() { sourceControl := styrav1beta1.SourceControl{ @@ -2628,6 +2638,17 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int Source: "default-ocp-system", }, }, + Revision: expectedBundleRevision("0123456789abcdef", []ocp.Requirement{ + { + Source: "library1", + }, + { + Source: "path-to-datasource", + }, + { + Source: "default-ocp-system", + }, + }), }).Return(nil).Once() // Called in reconcileS3Credentials @@ -2686,6 +2707,17 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int Source: "default-ocp-system", }, }, + Revision: expectedBundleRevision("0123456789abcdef", []ocp.Requirement{ + { + Source: "library1", + }, + { + Source: "path-to-datasource", + }, + { + Source: "default-ocp-system", + }, + }), }).Return(nil).Once() // Called in reconcileS3Credentials @@ -2738,6 +2770,17 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int Source: "default-ocp-system", }, }, + Revision: expectedBundleRevision("0123456789abcdef", []ocp.Requirement{ + { + Source: "library1", + }, + { + Source: "path-to-datasource", + }, + { + Source: "default-ocp-system", + }, + }), }).Return(nil).Once() // Called in reconcileS3Credentials From b7342b006f7320aab9f1ef2efc6cf00f0d491f84 Mon Sep 17 00:00:00 2001 From: ChrBlaesBD Date: Fri, 27 Feb 2026 09:21:43 +0100 Subject: [PATCH 03/14] exstended requrements with revision option --- config/samples/styra_v1beta1_system.yaml | 4 +- .../controller/styra/system_controller.go | 40 ++++++++++++++---- pkg/ocp/sources.go | 6 ++- .../controller/system_controller_test.go | 41 +++++++++++++++---- 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/config/samples/styra_v1beta1_system.yaml b/config/samples/styra_v1beta1_system.yaml index 994c8a79..690506db 100644 --- a/config/samples/styra_v1beta1_system.yaml +++ b/config/samples/styra_v1beta1_system.yaml @@ -20,12 +20,12 @@ spec: reason: path: result.reasons datasources: - - path: "test" + - path: "example" sourceControl: origin: path: policy reference: refs/heads/master - url: 'https://github.com/bd-b0100/pol-library-stress-tester' + url: 'https://github.com/something/some-repo.git' diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go index cd0d0946..e03fe5f8 100644 --- a/internal/controller/styra/system_controller.go +++ b/internal/controller/styra/system_controller.go @@ -18,7 +18,6 @@ package styra import ( "context" - "crypto/sha256" "encoding/json" "fmt" "net/http" @@ -799,7 +798,7 @@ func (r *SystemReconciler) reconcileS3Credentials( return s3Credentials, ctrl.Result{}, nil } - + func (r *SystemReconciler) reconcileSystemBundle( ctx context.Context, system *v1beta1.System, @@ -834,17 +833,42 @@ func (r *SystemReconciler) reconcileSystemBundle( } func bundleRevision(system *v1beta1.System, requirements []ocp.Requirement) string { - if system.Spec.SourceControl == nil || system.Spec.SourceControl.Origin.Commit == "" { + if system.Spec.SourceControl == nil { + return "" + } + if len(requirements) == 0 { return "" } - requirementsJSON, err := json.Marshal(requirements) - if err != nil { - return fmt.Sprintf(`git:{"commit":"%s"}`, system.Spec.SourceControl.Origin.Commit) + parts := make([]string, 0, len(requirements)+1) + systemRequirement := requirements[len(requirements)-1] + parts = append(parts, + fmt.Sprintf(`git:{input.sources["%s"].%s}`, systemRequirement.Source, requirementRevisionField(systemRequirement)), + ) + + for _, requirement := range requirements[:len(requirements)-1] { + parts = append(parts, + fmt.Sprintf(`%s:{input.sources["%s"].%s}`, + requirement.Source, + requirement.Source, + requirementRevisionField(requirement), + ), + ) + } + + return fmt.Sprintf(`$"%s"`, strings.Join(parts, ",")) +} + +func requirementRevisionField(requirement ocp.Requirement) string { + if requirement.Revision_hash { + return "sql.hash" + } + + if requirement.Revision_commit { + return "git" } - dataHash := sha256.Sum256(requirementsJSON) - return fmt.Sprintf(`git:{"commit":"%s"},data:%x`, system.Spec.SourceControl.Origin.Commit, dataHash) + return "git" } func (r *SystemReconciler) reconcileSystemSource( diff --git a/pkg/ocp/sources.go b/pkg/ocp/sources.go index 351cfcdc..261b47f0 100644 --- a/pkg/ocp/sources.go +++ b/pkg/ocp/sources.go @@ -109,8 +109,10 @@ type Secret struct { // Requirement represents a requirement for a bundle and a source. type Requirement struct { - Source string `json:"source,omitempty" yaml:"source,omitempty"` - Git GitRequirement `json:"git,omitempty" yaml:"git,omitempty"` + Source string `json:"source,omitempty" yaml:"source,omitempty"` + Git GitRequirement `json:"git,omitempty" yaml:"git,omitempty"` + Revision_hash bool `json:"revision_hash,omitempty" yaml:"revision_hash,omitempty"` + Revision_commit bool `json:"revision_commit,omitempty" yaml:"revision_commit,omitempty"` } // GitRequirement represents Git requirement. diff --git a/test/integration/controller/system_controller_test.go b/test/integration/controller/system_controller_test.go index 1bebdfa5..fcdb6eec 100644 --- a/test/integration/controller/system_controller_test.go +++ b/test/integration/controller/system_controller_test.go @@ -18,11 +18,11 @@ package styra import ( "context" - "crypto/sha256" "encoding/json" "fmt" "net/http" "reflect" + "strings" "time" ginkgo "github.com/onsi/ginkgo/v2" @@ -45,13 +45,40 @@ import ( "github.com/bankdata/styra-controller/pkg/styra" ) -func expectedBundleRevision(commit string, requirements []ocp.Requirement) string { - requirementsJSON, err := json.Marshal(requirements) - if err != nil { - return fmt.Sprintf(`git:{\"commit\":\"%s\"}`, commit) +func expectedBundleRevision(_ string, requirements []ocp.Requirement) string { + if len(requirements) == 0 { + return "" } - dataHash := sha256.Sum256(requirementsJSON) - return fmt.Sprintf(`git:{\"commit\":\"%s\"},data:%x`, commit, dataHash) + + parts := make([]string, 0, len(requirements)+1) + systemRequirement := requirements[len(requirements)-1] + parts = append(parts, + fmt.Sprintf(`git:{input.sources["%s"].%s}`, systemRequirement.Source, expectedRequirementRevisionField(systemRequirement)), + ) + + for _, requirement := range requirements[:len(requirements)-1] { + parts = append(parts, + fmt.Sprintf(`%s:{input.sources["%s"].%s}`, + requirement.Source, + requirement.Source, + expectedRequirementRevisionField(requirement), + ), + ) + } + + return fmt.Sprintf(`$"%s"`, strings.Join(parts, ",")) +} + +func expectedRequirementRevisionField(requirement ocp.Requirement) string { + if requirement.Revision_hash { + return "sql.hash" + } + + if requirement.Revision_commit { + return "git" + } + + return "git" } var _ = ginkgo.Describe("SystemReconciler.Reconcile", ginkgo.Label("integration"), func() { From 52548e208350b5d3e0e06e9c25dc8721abd01f64 Mon Sep 17 00:00:00 2001 From: ChrBlaesBD Date: Mon, 2 Mar 2026 13:57:35 +0100 Subject: [PATCH 04/14] for jacob 0203 --- api/config/v2alpha2/projectconfig_types.go | 8 +++- api/config/v2alpha2/zz_generated.deepcopy.go | 17 +++++++- .../controller/styra/system_controller.go | 41 +++++++++++-------- pkg/ocp/sources.go | 34 +++++++++++---- pkg/styra/client.go | 17 +++++++- pkg/styra/workspace_test.go | 20 +++++++++ .../controller/system_controller_test.go | 29 ++++++++----- 7 files changed, 125 insertions(+), 41 deletions(-) diff --git a/api/config/v2alpha2/projectconfig_types.go b/api/config/v2alpha2/projectconfig_types.go index 260b0897..ceb8839d 100644 --- a/api/config/v2alpha2/projectconfig_types.go +++ b/api/config/v2alpha2/projectconfig_types.go @@ -216,7 +216,7 @@ type OPAControlPlaneConfig struct { // DefaultRequirements is a list of requirements that will be added to all // systems/bundles created by the controller in the OCP, in addition to any requirements/datasources // specified on the System resource. - DefaultRequirements []string `json:"defaultRequirements,omitempty"` + DefaultRequirements []DefaultRequirement `json:"defaultRequirements,omitempty"` // SystemDatasourceChanged is the URL to be called when a system datasource has changed. SystemDatasourceChanged string `json:"systemDatasourceChanged,omitempty"` @@ -227,6 +227,12 @@ type OPAControlPlaneConfig struct { DecisionAPIConfig *DecisionAPIConfig `json:"decisionAPIConfig,omitempty"` } +type DefaultRequirement struct { + Name string `json:"name"` + GitSource bool `json:"gitSource,omitempty"` + DataSource bool `json:"dataSource,omitempty"` +} + // UserCredentialHandler defines the structure of possible user credential handlers type UserCredentialHandler struct { S3 *S3Handler `json:"s3,omitempty" yaml:"s3,omitempty"` diff --git a/api/config/v2alpha2/zz_generated.deepcopy.go b/api/config/v2alpha2/zz_generated.deepcopy.go index eef69be5..8007aa7e 100644 --- a/api/config/v2alpha2/zz_generated.deepcopy.go +++ b/api/config/v2alpha2/zz_generated.deepcopy.go @@ -91,6 +91,21 @@ func (in *DecisionLogReporting) DeepCopy() *DecisionLogReporting { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultRequirement) DeepCopyInto(out *DefaultRequirement) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultRequirement. +func (in *DefaultRequirement) DeepCopy() *DefaultRequirement { + if in == nil { + return nil + } + out := new(DefaultRequirement) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExporterConfig) DeepCopyInto(out *ExporterConfig) { *out = *in @@ -313,7 +328,7 @@ func (in *OPAControlPlaneConfig) DeepCopyInto(out *OPAControlPlaneConfig) { } if in.DefaultRequirements != nil { in, out := &in.DefaultRequirements, &out.DefaultRequirements - *out = make([]string, len(*in)) + *out = make([]DefaultRequirement, len(*in)) copy(*out, *in) } if in.DecisionAPIConfig != nil { diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go index e03fe5f8..42457146 100644 --- a/internal/controller/styra/system_controller.go +++ b/internal/controller/styra/system_controller.go @@ -400,7 +400,7 @@ func (r *SystemReconciler) ocpReconcile( } } - requirements = append(requirements, ocp.NewRequirement(datasource.Path)) + requirements = append(requirements, ocp.NewRequirement(datasource.Path, "data")) } system.SetCondition(v1beta1.ConditionTypeRequirementsUpdated, metav1.ConditionTrue) @@ -416,7 +416,7 @@ func (r *SystemReconciler) ocpReconcile( WithEvent(v1beta1.EventErrorUpdateSource). WithSystemCondition(v1beta1.ConditionTypeSystemSourceUpdated) } - requirements = append(requirements, ocp.NewRequirement(uniqueName)) + requirements = append(requirements, ocp.NewRequirement(uniqueName, "git")) system.SetCondition(v1beta1.ConditionTypeSystemSourceUpdated, metav1.ConditionTrue) reconcileSystemBundleStart := time.Now() @@ -798,7 +798,7 @@ func (r *SystemReconciler) reconcileS3Credentials( return s3Credentials, ctrl.Result{}, nil } - + func (r *SystemReconciler) reconcileSystemBundle( ctx context.Context, system *v1beta1.System, @@ -841,17 +841,12 @@ func bundleRevision(system *v1beta1.System, requirements []ocp.Requirement) stri } parts := make([]string, 0, len(requirements)+1) - systemRequirement := requirements[len(requirements)-1] - parts = append(parts, - fmt.Sprintf(`git:{input.sources["%s"].%s}`, systemRequirement.Source, requirementRevisionField(systemRequirement)), - ) - for _, requirement := range requirements[:len(requirements)-1] { + for _, requirement := range requirements { parts = append(parts, - fmt.Sprintf(`%s:{input.sources["%s"].%s}`, - requirement.Source, + fmt.Sprintf(`%s:%s`, requirement.Source, - requirementRevisionField(requirement), + requirementRevisionExpression(requirement), ), ) } @@ -859,16 +854,26 @@ func bundleRevision(system *v1beta1.System, requirements []ocp.Requirement) stri return fmt.Sprintf(`$"%s"`, strings.Join(parts, ",")) } -func requirementRevisionField(requirement ocp.Requirement) string { - if requirement.Revision_hash { - return "sql.hash" +func requirementRevisionExpression(requirement ocp.Requirement) string { + if requirement.RevisionHash && requirement.RevisionCommit { + return fmt.Sprintf( + `{input.sources["%s"].git}-{input.sources["%s"].sql}`, + requirement.Source, + requirement.Source, + ) } - if requirement.Revision_commit { - return "git" + if requirement.RevisionHash { + return fmt.Sprintf( + `{input.sources["%s"].sql}`, + requirement.Source, + ) } - return "git" + return fmt.Sprintf( + `{input.sources["%s"].git}`, + requirement.Source, + ) } func (r *SystemReconciler) reconcileSystemSource( @@ -2267,7 +2272,7 @@ func (r *SystemReconciler) CreateDefaultRequirements(ctx context.Context, log lo if r.Config.EnableOPAControlPlaneReconciliation || r.Config.EnableOPAControlPlaneReconciliationTestData { log.Info("Creating ocp default requirements") for _, defaultRequirement := range r.Config.OPAControlPlaneConfig.DefaultRequirements { - _, err := r.createSourceIfNotExists(ctx, log, v1beta1.Datasource{Path: defaultRequirement}) + _, err := r.createSourceIfNotExists(ctx, log, v1beta1.Datasource{Path: defaultRequirement.Name}) if err != nil { return err } diff --git a/pkg/ocp/sources.go b/pkg/ocp/sources.go index 261b47f0..1a839eec 100644 --- a/pkg/ocp/sources.go +++ b/pkg/ocp/sources.go @@ -23,6 +23,7 @@ import ( "io" "net/http" + configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" "github.com/bankdata/styra-controller/pkg/httperror" "github.com/pkg/errors" ) @@ -109,10 +110,10 @@ type Secret struct { // Requirement represents a requirement for a bundle and a source. type Requirement struct { - Source string `json:"source,omitempty" yaml:"source,omitempty"` - Git GitRequirement `json:"git,omitempty" yaml:"git,omitempty"` - Revision_hash bool `json:"revision_hash,omitempty" yaml:"revision_hash,omitempty"` - Revision_commit bool `json:"revision_commit,omitempty" yaml:"revision_commit,omitempty"` + Source string `json:"source,omitempty" yaml:"source,omitempty"` + Git GitRequirement `json:"git,omitempty" yaml:"git,omitempty"` + RevisionHash bool `json:"revision_hash,omitempty" yaml:"revision_hash,omitempty"` + RevisionCommit bool `json:"revision_commit,omitempty" yaml:"revision_commit,omitempty"` } // GitRequirement represents Git requirement. @@ -121,17 +122,32 @@ type GitRequirement struct { } // NewRequirement creates a new Requirement for a bundle. -func NewRequirement(source string) Requirement { - return Requirement{ +func NewRequirement(source string, sourceType string) Requirement { + requirement := Requirement{ Source: source, } + + if sourceType == "git" { + requirement.RevisionCommit = true + } else if sourceType == "data" { + requirement.RevisionHash = true + } + + return requirement } -// ToRequirements converts a list of sources to a list of Requirements. -func ToRequirements(sources []string) []Requirement { +// ToRequirements converts the default requirements to a list of bundle Requirements. +func ToRequirements(sources []configv2alpha2.DefaultRequirement) []Requirement { requirements := make([]Requirement, len(sources)) for i, source := range sources { - requirements[i] = NewRequirement(source) + requirement := NewRequirement(source.Name, "") + if source.GitSource { + requirement.RevisionCommit = true + } + if source.DataSource { + requirement.RevisionHash = true + } + requirements[i] = requirement } return requirements } diff --git a/pkg/styra/client.go b/pkg/styra/client.go index d424f224..ac57ed1e 100644 --- a/pkg/styra/client.go +++ b/pkg/styra/client.go @@ -128,8 +128,21 @@ func (c *Client) newRequest( var b bytes.Buffer if body != nil { - if err := json.NewEncoder(&b).Encode(body); err != nil { - return nil, errors.Wrap(err, "could not encode body") + switch payload := body.(type) { + case json.RawMessage: + if err := json.Compact(&b, payload); err != nil { + return nil, errors.Wrap(err, "could not compact raw JSON body") + } + case *json.RawMessage: + if payload != nil { + if err := json.Compact(&b, *payload); err != nil { + return nil, errors.Wrap(err, "could not compact raw JSON body") + } + } + default: + if err := json.NewEncoder(&b).Encode(body); err != nil { + return nil, errors.Wrap(err, "could not encode body") + } } } diff --git a/pkg/styra/workspace_test.go b/pkg/styra/workspace_test.go index 561398c4..c9b2274c 100644 --- a/pkg/styra/workspace_test.go +++ b/pkg/styra/workspace_test.go @@ -119,4 +119,24 @@ var _ = ginkgo.Describe("UpdateWorkspace", func() { expectStyraErr: true, }), ) + + ginkgo.It("sends compact raw JSON payload", func() { + request := json.RawMessage(`{"commit": "e7e1128fb3bde5ce46c3b7b6c62000fbfc463c3c"}`) + + c := newTestClient(func(r *http.Request) *http.Response { + bs, err := io.ReadAll(r.Body) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + gomega.Expect(string(bs)).To(gomega.Equal(`{"commit":"e7e1128fb3bde5ce46c3b7b6c62000fbfc463c3c"}`)) + + return &http.Response{ + Header: make(http.Header), + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewBufferString(`{"request_id": "id"}`)), + } + }) + + res, err := c.UpdateWorkspaceRaw(context.Background(), request) + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + gomega.Expect(res.StatusCode).To(gomega.Equal(http.StatusOK)) + }) }) diff --git a/test/integration/controller/system_controller_test.go b/test/integration/controller/system_controller_test.go index fcdb6eec..4d2ca18a 100644 --- a/test/integration/controller/system_controller_test.go +++ b/test/integration/controller/system_controller_test.go @@ -53,15 +53,14 @@ func expectedBundleRevision(_ string, requirements []ocp.Requirement) string { parts := make([]string, 0, len(requirements)+1) systemRequirement := requirements[len(requirements)-1] parts = append(parts, - fmt.Sprintf(`git:{input.sources["%s"].%s}`, systemRequirement.Source, expectedRequirementRevisionField(systemRequirement)), + fmt.Sprintf(`git:%s`, expectedRequirementRevisionExpression(systemRequirement)), ) for _, requirement := range requirements[:len(requirements)-1] { parts = append(parts, - fmt.Sprintf(`%s:{input.sources["%s"].%s}`, + fmt.Sprintf(`%s:%s`, requirement.Source, - requirement.Source, - expectedRequirementRevisionField(requirement), + expectedRequirementRevisionExpression(requirement), ), ) } @@ -69,16 +68,26 @@ func expectedBundleRevision(_ string, requirements []ocp.Requirement) string { return fmt.Sprintf(`$"%s"`, strings.Join(parts, ",")) } -func expectedRequirementRevisionField(requirement ocp.Requirement) string { - if requirement.Revision_hash { - return "sql.hash" +func expectedRequirementRevisionExpression(requirement ocp.Requirement) string { + if requirement.RevisionHash && requirement.RevisionCommit { + return fmt.Sprintf( + `{object.get(object.get(object.get(input.sources, "%s", {}), "git", {}), "commit", "")}-{object.get(object.get(object.get(input.sources, "%s", {}), "sql", {}), "hash", "")}`, + requirement.Source, + requirement.Source, + ) } - if requirement.Revision_commit { - return "git" + if requirement.RevisionHash { + return fmt.Sprintf( + `{object.get(object.get(object.get(input.sources, "%s", {}), "sql", {}), "hash", "")}`, + requirement.Source, + ) } - return "git" + return fmt.Sprintf( + `{object.get(object.get(object.get(input.sources, "%s", {}), "git", {}), "commit", "")}`, + requirement.Source, + ) } var _ = ginkgo.Describe("SystemReconciler.Reconcile", ginkgo.Label("integration"), func() { From b04beb9226469e0f85af537ff052efab35ea7dfa Mon Sep 17 00:00:00 2001 From: ChrBlaesBD Date: Tue, 3 Mar 2026 09:59:48 +0100 Subject: [PATCH 05/14] s --- internal/controller/styra/system_controller.go | 6 +++--- .../controller/styra/system_controller_test.go | 16 ++++++++++++++++ .../controller/system_controller_test.go | 6 ++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go index 42457146..117445b6 100644 --- a/internal/controller/styra/system_controller.go +++ b/internal/controller/styra/system_controller.go @@ -857,7 +857,7 @@ func bundleRevision(system *v1beta1.System, requirements []ocp.Requirement) stri func requirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash && requirement.RevisionCommit { return fmt.Sprintf( - `{input.sources["%s"].git}-{input.sources["%s"].sql}`, + `{input.sources[\"%s\"].git.commit}-{input.sources[\"%s\"].sql.hash}`, requirement.Source, requirement.Source, ) @@ -865,13 +865,13 @@ func requirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash { return fmt.Sprintf( - `{input.sources["%s"].sql}`, + `{input.sources[\"%s\"].sql.hash}`, requirement.Source, ) } return fmt.Sprintf( - `{input.sources["%s"].git}`, + `{input.sources[\"%s\"].git.commit}`, requirement.Source, ) } diff --git a/internal/controller/styra/system_controller_test.go b/internal/controller/styra/system_controller_test.go index 77b81b4b..717b333e 100644 --- a/internal/controller/styra/system_controller_test.go +++ b/internal/controller/styra/system_controller_test.go @@ -22,6 +22,7 @@ import ( configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" styrav1beta1 "github.com/bankdata/styra-controller/api/styra/v1beta1" + "github.com/bankdata/styra-controller/pkg/ocp" "github.com/bankdata/styra-controller/pkg/styra" ) @@ -131,3 +132,18 @@ var _ = ginkgo.DescribeTable("isURLValid", ginkgo.Entry("invalid url", "google.com", false), ginkgo.Entry("invalid url", "google", false), ) + +var _ = ginkgo.DescribeTable("requirementRevisionExpression", + func(requirement ocp.Requirement, expected string) { + gomega.Ω(requirementRevisionExpression(requirement)).To(gomega.Equal(expected)) + }, + ginkgo.Entry("git revision", ocp.Requirement{Source: "git", RevisionCommit: true}, + `{object.get(object.get(object.get(input.sources, "git", {}), "git", {}), "commit", "")}`, + ), + ginkgo.Entry("data revision", ocp.Requirement{Source: "data", RevisionHash: true}, + `{object.get(object.get(object.get(input.sources, "data", {}), "sql", {}), "hash", object.get(object.get(object.get(input.sources, "data", {}), "data", {}), "hash", ""))}`, + ), + ginkgo.Entry("git and data revision", ocp.Requirement{Source: "policy", RevisionCommit: true, RevisionHash: true}, + `{object.get(object.get(object.get(input.sources, "policy", {}), "git", {}), "commit", "")}-{object.get(object.get(object.get(input.sources, "policy", {}), "sql", {}), "hash", object.get(object.get(object.get(input.sources, "policy", {}), "data", {}), "hash", ""))}`, + ), +) diff --git a/test/integration/controller/system_controller_test.go b/test/integration/controller/system_controller_test.go index 4d2ca18a..e44bf816 100644 --- a/test/integration/controller/system_controller_test.go +++ b/test/integration/controller/system_controller_test.go @@ -71,7 +71,8 @@ func expectedBundleRevision(_ string, requirements []ocp.Requirement) string { func expectedRequirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash && requirement.RevisionCommit { return fmt.Sprintf( - `{object.get(object.get(object.get(input.sources, "%s", {}), "git", {}), "commit", "")}-{object.get(object.get(object.get(input.sources, "%s", {}), "sql", {}), "hash", "")}`, + `{object.get(object.get(object.get(input.sources, "%s", {}), "git", {}), "commit", "")}-{object.get(object.get(object.get(input.sources, "%s", {}), "sql", {}), "hash", object.get(object.get(object.get(input.sources, "%s", {}), "data", {}), "hash", ""))}`, + requirement.Source, requirement.Source, requirement.Source, ) @@ -79,7 +80,8 @@ func expectedRequirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash { return fmt.Sprintf( - `{object.get(object.get(object.get(input.sources, "%s", {}), "sql", {}), "hash", "")}`, + `{object.get(object.get(object.get(input.sources, "%s", {}), "sql", {}), "hash", object.get(object.get(object.get(input.sources, "%s", {}), "data", {}), "hash", ""))}`, + requirement.Source, requirement.Source, ) } From 0239d85a8b678183b44ca6046f0819bd7dc7ebf6 Mon Sep 17 00:00:00 2001 From: ChrBlaesBD Date: Tue, 3 Mar 2026 11:38:41 +0100 Subject: [PATCH 06/14] for jacob- works --- internal/controller/styra/system_controller.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go index 117445b6..9661546a 100644 --- a/internal/controller/styra/system_controller.go +++ b/internal/controller/styra/system_controller.go @@ -857,7 +857,7 @@ func bundleRevision(system *v1beta1.System, requirements []ocp.Requirement) stri func requirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash && requirement.RevisionCommit { return fmt.Sprintf( - `{input.sources[\"%s\"].git.commit}-{input.sources[\"%s\"].sql.hash}`, + "commit:{input.sources[\"%s\"].git.commit}-data:{input.sources[\"%s\"].sql.hash}", requirement.Source, requirement.Source, ) @@ -865,13 +865,13 @@ func requirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash { return fmt.Sprintf( - `{input.sources[\"%s\"].sql.hash}`, + "data:{input.sources[\"%s\"].sql.hash}", requirement.Source, ) } return fmt.Sprintf( - `{input.sources[\"%s\"].git.commit}`, + "commit:{input.sources[\"%s\"].git.commit}", requirement.Source, ) } From 46d85c52a4076326e37e84ee82e514412ecc60ad Mon Sep 17 00:00:00 2001 From: ChrBlaesBD Date: Wed, 4 Mar 2026 09:45:46 +0100 Subject: [PATCH 07/14] works --- config/default/config.yaml | 3 +++ .../controller/styra/system_controller_test.go | 6 +++--- .../controller/system_controller_test.go | 14 ++++---------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/config/default/config.yaml b/config/default/config.yaml index efc6d78b..df9caa67 100644 --- a/config/default/config.yaml +++ b/config/default/config.yaml @@ -36,6 +36,9 @@ opaControlPlaneConfig: ocpConfigSecretName: minio # Name of secret in ocp config defaultRequirements: - library1 + defaultRequirementsWithRevision: + - name: library2 + gitSource: true # used by controller to create users and policies userCredentialHandler: diff --git a/internal/controller/styra/system_controller_test.go b/internal/controller/styra/system_controller_test.go index 717b333e..03a4d429 100644 --- a/internal/controller/styra/system_controller_test.go +++ b/internal/controller/styra/system_controller_test.go @@ -138,12 +138,12 @@ var _ = ginkgo.DescribeTable("requirementRevisionExpression", gomega.Ω(requirementRevisionExpression(requirement)).To(gomega.Equal(expected)) }, ginkgo.Entry("git revision", ocp.Requirement{Source: "git", RevisionCommit: true}, - `{object.get(object.get(object.get(input.sources, "git", {}), "git", {}), "commit", "")}`, + `commit:{input.sources["git"].git.commit}`, ), ginkgo.Entry("data revision", ocp.Requirement{Source: "data", RevisionHash: true}, - `{object.get(object.get(object.get(input.sources, "data", {}), "sql", {}), "hash", object.get(object.get(object.get(input.sources, "data", {}), "data", {}), "hash", ""))}`, + `data:{input.sources["data"].sql.hash}`, ), ginkgo.Entry("git and data revision", ocp.Requirement{Source: "policy", RevisionCommit: true, RevisionHash: true}, - `{object.get(object.get(object.get(input.sources, "policy", {}), "git", {}), "commit", "")}-{object.get(object.get(object.get(input.sources, "policy", {}), "sql", {}), "hash", object.get(object.get(object.get(input.sources, "policy", {}), "data", {}), "hash", ""))}`, + `commit:{input.sources["policy"].git.commit}-data:{input.sources["policy"].sql.hash}`, ), ) diff --git a/test/integration/controller/system_controller_test.go b/test/integration/controller/system_controller_test.go index e44bf816..282661cd 100644 --- a/test/integration/controller/system_controller_test.go +++ b/test/integration/controller/system_controller_test.go @@ -51,12 +51,8 @@ func expectedBundleRevision(_ string, requirements []ocp.Requirement) string { } parts := make([]string, 0, len(requirements)+1) - systemRequirement := requirements[len(requirements)-1] - parts = append(parts, - fmt.Sprintf(`git:%s`, expectedRequirementRevisionExpression(systemRequirement)), - ) - for _, requirement := range requirements[:len(requirements)-1] { + for _, requirement := range requirements { parts = append(parts, fmt.Sprintf(`%s:%s`, requirement.Source, @@ -71,8 +67,7 @@ func expectedBundleRevision(_ string, requirements []ocp.Requirement) string { func expectedRequirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash && requirement.RevisionCommit { return fmt.Sprintf( - `{object.get(object.get(object.get(input.sources, "%s", {}), "git", {}), "commit", "")}-{object.get(object.get(object.get(input.sources, "%s", {}), "sql", {}), "hash", object.get(object.get(object.get(input.sources, "%s", {}), "data", {}), "hash", ""))}`, - requirement.Source, + `commit:{input.sources["%s"].git.commit}-data:{input.sources["%s"].sql.hash}`, requirement.Source, requirement.Source, ) @@ -80,14 +75,13 @@ func expectedRequirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash { return fmt.Sprintf( - `{object.get(object.get(object.get(input.sources, "%s", {}), "sql", {}), "hash", object.get(object.get(object.get(input.sources, "%s", {}), "data", {}), "hash", ""))}`, - requirement.Source, + `data:{input.sources["%s"].sql.hash}`, requirement.Source, ) } return fmt.Sprintf( - `{object.get(object.get(object.get(input.sources, "%s", {}), "git", {}), "commit", "")}`, + `commit:{input.sources["%s"].git.commit}`, requirement.Source, ) } From dba291ae87d2dc1eca3c59f611cc4fb8932d0bf3 Mon Sep 17 00:00:00 2001 From: ChrBlaesBD Date: Fri, 6 Mar 2026 10:00:53 +0100 Subject: [PATCH 08/14] for jacbo --- api/config/v2alpha2/projectconfig_types.go | 34 +- api/config/v2alpha2/zz_generated.deepcopy.go | 17 +- api/config/v2alpha3/groupversion_info.go | 37 ++ api/config/v2alpha3/projectconfig_types.go | 460 +++++++++++++ api/config/v2alpha3/zz_generated.deepcopy.go | 643 +++++++++++++++++++ internal/config/config.go | 49 ++ 6 files changed, 1217 insertions(+), 23 deletions(-) create mode 100644 api/config/v2alpha3/groupversion_info.go create mode 100644 api/config/v2alpha3/projectconfig_types.go create mode 100644 api/config/v2alpha3/zz_generated.deepcopy.go diff --git a/api/config/v2alpha2/projectconfig_types.go b/api/config/v2alpha2/projectconfig_types.go index ceb8839d..c4c18270 100644 --- a/api/config/v2alpha2/projectconfig_types.go +++ b/api/config/v2alpha2/projectconfig_types.go @@ -20,6 +20,7 @@ import ( "sort" "strings" + "github.com/bankdata/styra-controller/api/config/v2alpha3" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -216,7 +217,7 @@ type OPAControlPlaneConfig struct { // DefaultRequirements is a list of requirements that will be added to all // systems/bundles created by the controller in the OCP, in addition to any requirements/datasources // specified on the System resource. - DefaultRequirements []DefaultRequirement `json:"defaultRequirements,omitempty"` + DefaultRequirements []string `json:"defaultRequirements,omitempty"` // SystemDatasourceChanged is the URL to be called when a system datasource has changed. SystemDatasourceChanged string `json:"systemDatasourceChanged,omitempty"` @@ -227,12 +228,6 @@ type OPAControlPlaneConfig struct { DecisionAPIConfig *DecisionAPIConfig `json:"decisionAPIConfig,omitempty"` } -type DefaultRequirement struct { - Name string `json:"name"` - GitSource bool `json:"gitSource,omitempty"` - DataSource bool `json:"dataSource,omitempty"` -} - // UserCredentialHandler defines the structure of possible user credential handlers type UserCredentialHandler struct { S3 *S3Handler `json:"s3,omitempty" yaml:"s3,omitempty"` @@ -455,6 +450,31 @@ func (c *ProjectConfig) OPARestartEnabled() bool { return c.PodRestart.OPARestart.Enabled } +// ToV2Alpha2 returns this ProjectConfig converted to a v2alpha2.ProjectConfig +func (c *ProjectConfig) ToV2Alpha2() *v2alpha3.ProjectConfig { + v2cfg := &v2alpha3.ProjectConfig{ + ControllerClass: c.ControllerClass, + DeletionProtectionDefault: c.DeletionProtectionDefault, + DisableCRDWebhooks: c.DisableCRDWebhooks, + EnableMigrations: c.EnableMigrations, + LogLevel: c.LogLevel, + Styra: v2alpha3.StyraConfig{ + Token: c.Styra.Token, + Address: c.Styra.Address, + }, + SystemPrefix: c.SystemPrefix, + SystemSuffix: c.SystemSuffix, + SystemUserRoles: c.SystemUserRoles, + } + + for _, field in {c.OPAControlPlaneConfig.DefaultRequirements{ + + } + v2cfg.OPAControlPlaneConfig.DefaultRequirements = + + return v2cfg +} + func init() { SchemeBuilder.Register(&ProjectConfig{}) } diff --git a/api/config/v2alpha2/zz_generated.deepcopy.go b/api/config/v2alpha2/zz_generated.deepcopy.go index 8007aa7e..eef69be5 100644 --- a/api/config/v2alpha2/zz_generated.deepcopy.go +++ b/api/config/v2alpha2/zz_generated.deepcopy.go @@ -91,21 +91,6 @@ func (in *DecisionLogReporting) DeepCopy() *DecisionLogReporting { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DefaultRequirement) DeepCopyInto(out *DefaultRequirement) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultRequirement. -func (in *DefaultRequirement) DeepCopy() *DefaultRequirement { - if in == nil { - return nil - } - out := new(DefaultRequirement) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExporterConfig) DeepCopyInto(out *ExporterConfig) { *out = *in @@ -328,7 +313,7 @@ func (in *OPAControlPlaneConfig) DeepCopyInto(out *OPAControlPlaneConfig) { } if in.DefaultRequirements != nil { in, out := &in.DefaultRequirements, &out.DefaultRequirements - *out = make([]DefaultRequirement, len(*in)) + *out = make([]string, len(*in)) copy(*out, *in) } if in.DecisionAPIConfig != nil { diff --git a/api/config/v2alpha3/groupversion_info.go b/api/config/v2alpha3/groupversion_info.go new file mode 100644 index 00000000..310674e0 --- /dev/null +++ b/api/config/v2alpha3/groupversion_info.go @@ -0,0 +1,37 @@ +/* +Copyright (C) 2025 Bankdata (bankdata@bankdata.dk) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v2alpha3 contains API Schema definitions for the config v2alpha3 API group +// +kubebuilder:object:generate=true +// +kubebuilder:skip +// +groupName=config.bankdata.dk +package v2alpha3 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "config.bankdata.dk", Version: "v2alpha3"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/api/config/v2alpha3/projectconfig_types.go b/api/config/v2alpha3/projectconfig_types.go new file mode 100644 index 00000000..48c9427f --- /dev/null +++ b/api/config/v2alpha3/projectconfig_types.go @@ -0,0 +1,460 @@ +/* +Copyright (C) 2025 Bankdata (bankdata@bankdata.dk) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2alpha3 + +import ( + "sort" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +//+kubebuilder:object:root=true + +// ProjectConfig is the Schema for the projectconfigs API +type ProjectConfig struct { + metav1.TypeMeta `json:",inline"` + + // ControllerClass sets a controller class for this controller. This allows + // the provided CRDs to target a specific controller. This is useful when + // running multiple controllers in the same cluster. + ControllerClass string `json:"controllerClass"` + + // DeletionProtectionDefault sets the default to use with regards to deletion + // protection if it is not set on the resource. + DeletionProtectionDefault bool `json:"deletionProtectionDefault"` + + // ReadOnly sets the value of ReadOnly for systems + //+kubebuilder:deprecatedversion:warning="ReadOnly field is deprecated, only used in Styra" + // Deprecated: ReadOnly field is deprecated, only used in Styra. + // This field will be removed in a future version. + ReadOnly bool `json:"readOnly"` + + // EnableDeltaBundlesDefault sets the default of whether systems have delta-bundles or not + //+kubebuilder:deprecatedversion:warning="EnableDeltaBundlesDefault field is deprecated, only used in Styra" + // Deprecated: EnableDeltaBundlesDefault field is deprecated, only used in Styra. + // This field will be removed in a future version. + EnableDeltaBundlesDefault *bool `json:"enableDeltaBundlesDefault,omitempty"` + + // 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. + DisableCRDWebhooks bool `json:"disableCRDWebhooks"` + + // EnableMigrations enables the system migration annotation. This should be + // kept disabled unless migrations need to be done. + EnableMigrations bool `json:"enableMigrations"` + + // DatasourceIgnorePatterns is a list of regex patterns, that allow datasources in styra + // to be ignored based on their datasource id. + //+kubebuilder:deprecatedversion:warning="DatasourceIgnorePatterns field is deprecated, only used in Styra" + // Deprecated: DatasourceIgnorePatterns field is deprecated, only used in Styra. + // This field will be removed in a future version. + DatasourceIgnorePatterns []string `json:"datasourceIgnorePatterns,omitempty"` + + // GitCredentials holds a list of git credential configurations. The + // RepoPrefix of the GitCredential will be matched angainst repository URL in + // order to determine which credential to use. The GitCredential with the + // longest matching RepoPrefix will be selected. + //+kubebuilder:deprecatedversion:warning="GitCredentials field is deprecated, only used in Styra" + // Deprecated: GitCredentials field is deprecated, only used in Styra. + // This field will be removed in a future version. + GitCredentials []*GitCredential `json:"gitCredentials"` + + // LogLevel sets the logging level of the controller. A higher number gives + // more verbosity. A number higher than 0 should only be used for debugging + // purposes. + LogLevel int `json:"logLevel"` + + LeaderElection *LeaderElectionConfig `json:"leaderElection"` + + NotificationWebhooks *NotificationWebhooksConfig `json:"notificationWebhooks,omitempty"` + + Sentry *SentryConfig `json:"sentry"` + + //+kubebuilder:deprecatedversion:warning="SSO field is deprecated, only used in Styra" + // Deprecated: SSO field is deprecated, only used in Styra. This field will be removed in a future version. + SSO *SSOConfig `json:"sso"` + + //+kubebuilder:deprecatedversion:warning="Styra field is deprecated, use OPAControlPlaneConfig instead" + // Deprecated: Use OPAControlPlaneConfig instead. This field will be removed in a future version. + Styra StyraConfig `json:"styra"` + + OPA OPAConfig `json:"opa,omitempty"` + + // SystemPrefix is a prefix for all the systems that the controller creates + // in Styra DAS. This is useful in order to be able to identify what + // controller created a system in a shared Styra DAS instance. + SystemPrefix string `json:"systemPrefix"` + + // SystemSuffix is a suffix for all the systems that the controller creates + // in Styra DAS. This is useful in order to be able to identify what + // controller created a system in a shared Styra DAS instance. + SystemSuffix string `json:"systemSuffix"` + + // SystemUserRoles is a list of Styra DAS system level roles which the subjects of + // a system will be granted. + //+kubebuilder:deprecatedversion:warning="SystemUserRoles field is deprecated, only used in Styra" + // Deprecated: SystemUserRoles field is deprecated, only used in Styra. + // This field will be removed in a future version. + SystemUserRoles []string `json:"systemUserRoles"` + + //+kubebuilder:deprecatedversion:warning="DecisionsExporter field is deprecated, only used in Styra" + // Deprecated: DecisionsExporter field is deprecated, only used in Styra. + // This field will be removed in a future version. + DecisionsExporter *ExporterConfig `json:"decisionsExporter,omitempty"` + + //+kubebuilder:deprecatedversion:warning="ActivityExporter field is deprecated, only used in Styra" + // Deprecated: ActivityExporter field is deprecated, only used in Styra. + // This field will be removed in a future version. + ActivityExporter *ExporterConfig `json:"activityExporter,omitempty"` + + PodRestart *PodRestartConfig `json:"podRestart,omitempty"` + + // OPAControlPlaneConfig contains configuration for connecting to the + // OPA Control Plane APIs. If this is not set, the controller will not + // attempt to connect to the OPA Control Plane APIs. + OPAControlPlaneConfig *OPAControlPlaneConfig `json:"opaControlPlaneConfig,omitempty"` + + // UserCredentialHandler contains configuration for the controller to handle user credentials, e.g. in S3 + UserCredentialHandler *UserCredentialHandler `json:"userCredentialHandler,omitempty"` + + // EnableStyraReconciliation is a flag that sets whether the controller should use Styra + // A Migration flag to enable/disable Styra DAS reconciliation for all systems and libraries. + //+kubebuilder:deprecatedversion:warning="EnableStyraReconciliation field is deprecated. + // Only used in migration versions Styra->OCP" + // Deprecated: EnableStyraReconciliation field is deprecated, only used in migration versions Styra->OCP. + // This field will be removed in a future version. + EnableStyraReconciliation bool `json:"enableStyraReconciliation,omitempty"` + + // EnableOPAControlPlaneReconciliation is a flag that sets whether the controller should use OPAControlPlane + // A Migration flag to enable/disable OPA Control Plane reconciliation for all systems and libraries. + //+kubebuilder:deprecatedversion:warning="EnableOPAControlPlaneReconciliation field is deprecated, + // only used in migration versions Styra->OCP" + // Deprecated: EnableOPAControlPlaneReconciliation field is deprecated, only used in migration versions Styra->OCP. + // This field will be removed in a future version. + EnableOPAControlPlaneReconciliation bool `json:"enableOPAControlPlaneReconciliation,omitempty"` + + // EnableOPAControlPlaneReconciliationTestData is a flag that sets whether the controller should create + // OPAControlPlane test data. + // A Migration flag to allow adding test data to OPA Control Plane and not to modify k8s data + //+kubebuilder:deprecatedversion:warning="EnableOPAControlPlaneReconciliationTestData field is deprecated. + // Only used in migration versions Styra->OCP" + // Deprecated: EnableOPAControlPlaneReconciliationTestData field is deprecated. + // Only used in migration versions Styra->OCP. This field will be removed in a future version. + EnableOPAControlPlaneReconciliationTestData bool `json:"enableOPAControlPlaneReconciliationTestData,omitempty"` +} + +// PodRestartConfig contains configuration for restarting OPA and SLP pods +type PodRestartConfig struct { + SLPRestart *SLPRestartConfig `json:"slpRestart,omitempty"` + OPARestart *OPARestartConfig `json:"opaRestart,omitempty"` +} + +// SLPRestartConfig contains configuration for restarting SLP pods +type SLPRestartConfig struct { + Enabled bool `json:"enabled"` + DeploymentType string `json:"deploymentType"` // DeploymentType only currently supports "StatefulSet"" +} + +// OPARestartConfig contains configuration for restarting OPA pods -- This is not yet implemented +type OPARestartConfig struct { + Enabled bool `json:"enabled"` + DeploymentType string `json:"deploymentType"` +} + +// LeaderElectionConfig contains configuration for leader election +type LeaderElectionConfig struct { + LeaseDuration metav1.Duration `json:"leaseDuration"` + RenewDeadline metav1.Duration `json:"renewDeadline"` + RetryPeriod metav1.Duration `json:"retryPeriod"` +} + +// StyraConfig contains configuration for connecting to the Styra DAS apis +type StyraConfig struct { + // Address is the URL for the Styra DAS API server. + Address string `json:"address"` + + // Token is a Styra DAS API token. These can be created in the Styra DAS GUI + // or through the API. The token should have the `WorkspaceAdministrator` role. + Token string `json:"token"` + + // Alternative to the "token" whice define the Styra DAS API token directly in the config file, + // this "tokenSecretPath" will use a token from a secret (only if "token" is not set) + TokenSecretPath string `json:"tokenSecretPath"` +} + +// OPAControlPlaneConfig defines the config for the OPA Control Plane. +type OPAControlPlaneConfig struct { + // Address is the URL for the OPA Control Plane API server. + Address string `json:"address"` + + // Token is a OPA Control Plane API token. + Token string `json:"token"` + + // GitCredentials is the name of a secret used by the OPA Control Plane Git integration. + GitCredentials []*GitCredentials `json:"gitCredentials,omitempty"` + + // BundleObjectStorage is the object storage configuration to use for bundles. + // Currently only supports aws + BundleObjectStorage *BundleObjectStorage `json:"bundleObjectStorage,omitempty"` + + // DefaultRequirements is a list of requirements that will be added to all + // systems/bundles created by the controller in the OCP, in addition to any requirements/datasources + // specified on the System resource. + DefaultRequirements []DefaultRequirement `json:"defaultRequirements,omitempty"` + + // SystemDatasourceChanged is the URL to be called when a system datasource has changed. + SystemDatasourceChanged string `json:"systemDatasourceChanged,omitempty"` + // LibraryDatasourceChanged is the URL to be called when a library datasource has changed. + LibraryDatasourceChanged string `json:"libraryDatasourceChanged,omitempty"` + + // DecisionAPIConfig contains configuration for which api OPAs should use to and how + DecisionAPIConfig *DecisionAPIConfig `json:"decisionAPIConfig,omitempty"` +} + +type DefaultRequirement struct { + Name string `json:"name"` + GitSource bool `json:"gitSource,omitempty"` + DataSource bool `json:"dataSource,omitempty"` +} + +// UserCredentialHandler defines the structure of possible user credential handlers +type UserCredentialHandler struct { + S3 *S3Handler `json:"s3,omitempty" yaml:"s3,omitempty"` +} + +// S3Handler defines the structure for S3 handler configuration. +type S3Handler struct { + Bucket string `json:"bucket" yaml:"bucket"` + URL string `json:"url" yaml:"url"` + Region string `json:"region" yaml:"region"` + AccessKeyID string `json:"accessKeyID" yaml:"accessKeyID"` + SecretAccessKey string `json:"secretAccessKey" yaml:"secretAccessKey"` +} + +// BundleObjectStorage defines the structure for object storage configuration used by bundles +type BundleObjectStorage struct { + S3 *S3ObjectStorage `json:"s3,omitempty" yaml:"s3,omitempty"` +} + +// S3ObjectStorage defines the structure for S3 object storage configuration. +type S3ObjectStorage struct { + Bucket string `json:"bucket"` + URL string `json:"url,omitempty"` + Region string `json:"region"` + OCPConfigSecretName string `json:"ocpConfigSecretName"` +} + +// GitCredentials contains configuration for git credentials used by the OPA Control Plane. +type GitCredentials struct { + ID string `json:"id"` + + // RepoPrefix specifies a repo URL prefix. eg. if RepoPrefix is set to + // `https://github.com/bankdata`, then this credentials would apply for any + // repository under the bankdata github org. + RepoPrefix string `json:"repoPrefix"` +} + +// OPAConfig contains default configuration for the opa config generated by the styra-controller +type OPAConfig struct { + DecisionLogs DecisionLog `json:"decisionLogs,omitempty" yaml:"decisionLogs,omitempty"` + Metrics MetricsConfig `json:"metrics,omitempty" yaml:"metrics,omitempty"` + PersistBundle bool `json:"persist_bundle,omitempty" yaml:"persist_bundle,omitempty"` + PersistBundleDirectory string `json:"persist_bundle_directory,omitempty" yaml:"persist_bundle_directory,omitempty"` //nolint:lll + BundleServer *OPABundleServer `json:"bundleServer,omitempty" yaml:"bundleServer,omitempty"` +} + +// OPABundleServer contains configuration for the OPA bundle server +type OPABundleServer struct { + URL string `json:"url,omitempty" yaml:"url,omitempty"` + Path string `json:"path,omitempty" yaml:"path,omitempty"` +} + +// MetricsConfig contains configuration for OPA metrics +type MetricsConfig struct { + Prometheus PrometheusMetricsConfig `json:"prometheus,omitempty" yaml:"prometheus,omitempty"` +} + +// PrometheusMetricsConfig contains configuration for Prometheus metrics +type PrometheusMetricsConfig struct { + HTTP HTTPMetricsConfig `json:"http,omitempty" yaml:"http,omitempty"` +} + +// HTTPMetricsConfig contains configuration for HTTP metrics +type HTTPMetricsConfig struct { + Buckets []float64 `json:"buckets,omitempty" yaml:"buckets,omitempty"` +} + +// DecisionLog contains configuration for the decision logs +type DecisionLog struct { + RequestContext RequestContext `json:"requestContext,omitempty"` +} + +// DecisionAPIConfig contains configuration for decision log dispatch +type DecisionAPIConfig struct { + ServiceURL string `json:"serviceUrl,omitempty"` + Reporting DecisionLogReporting `json:"reporting,omitempty"` +} + +// DecisionLogReporting contains configuration for decision log reporting +type DecisionLogReporting struct { + MaxDelaySeconds int `json:"maxDelaySeconds,omitempty" yaml:"maxDelaySeconds,omitempty"` + MinDelaySeconds int `json:"minDelaySeconds,omitempty" yaml:"minDelaySeconds,omitempty"` + UploadSizeLimitBytes int `json:"uploadSizeLimitBytes,omitempty" yaml:"uploadSizeLimitBytes,omitempty"` +} + +// RequestContext contains configuration for the RequestContext in the decision logs +type RequestContext struct { + HTTP HTTP `json:"http"` +} + +// HTTP contains configuration for the HTTP config in the RequestContext +type HTTP struct { + // http headers that will be added to the decision logs + Headers []string `json:"headers"` +} + +// SentryConfig contains configuration for how errors should be reported to +// sentry. +type SentryConfig struct { + // Debug enables Sentry client debugging. + Debug bool `json:"debug"` + + // DSN is the Sentry project DSN. + DSN string `json:"dsn"` + + // Environment sets the environment of the events sent to Sentry. + Environment string `json:"environment"` + + // HTTPSProxy sets an HTTP proxy server for sentry to use. + HTTPSProxy string `json:"httpsProxy"` +} + +// NotificationWebhooksConfig contains configuration for how to call the notification +// webhook. +type NotificationWebhooksConfig struct { + // Address is the URL to be called when the controller should do a webhook + // notification. Currently the only supported notification is that a + // datasource configuration has changed. + + //+kubebuilder:deprecatedversion:warning="SystemDatasourceChanged field is deprecated, only used in Styra" + // Deprecated: SystemDatasourceChanged field is deprecated, only used in Styra. + // This field will be removed in a future version. + SystemDatasourceChanged string `json:"systemDatasourceChanged,omitempty"` + //+kubebuilder:deprecatedversion:warning="LibraryDatasourceChanged field is deprecated, only used in Styra" + // Deprecated: LibraryDatasourceChanged field is deprecated, only used in Styra. + // This field will be removed in a future version. + LibraryDatasourceChanged string `json:"libraryDatasourceChanged,omitempty"` +} + +// SSOConfig contains configuration for how to use SSO tokens for determining +// what groups a user belongs to. This can be used to grant members of a +// certain group access to systems. +type SSOConfig struct { + // IdentityProvider is the ID of a configured Styra DAS identity provider. + IdentityProvider string `json:"identityProvider"` + + // JWTGroupsClaim is the json path to a claim in issued JWTs which contain a + // list of groups that the user belongs to. + JWTGroupsClaim string `json:"jwtGroupsClaim"` +} + +// GitCredential represents a set of credentials to be used for repositories +// that match the RepoPrefix. +type GitCredential struct { + // User is a http basic auth username used for git. + User string `json:"user"` + + // Password is a http basic auth password used for git. + Password string `json:"password"` + + // RepoPrefix specifies a repo URL prefix. eg. if RepoPrefix is set to + // `https://github.com/bankdata`, then this credentials would apply for any + // repository under the bankdata github org. + RepoPrefix string `json:"repoPrefix"` +} + +// ExporterConfigType defines the type of exporter config +type ExporterConfigType string + +const ( + // ExporterConfigTypeDecisions is the type for decisions exporter config + ExporterConfigTypeDecisions ExporterConfigType = "DecisionsExporter" + // ExporterConfigTypeActivity is the type for activity exporter config + ExporterConfigTypeActivity ExporterConfigType = "ActivityExporter" +) + +// ExporterConfig contains configuration for exports +type ExporterConfig struct { + Enabled bool `json:"enabled,omitempty"` + Interval string `json:"interval,omitempty"` + Kafka *KafkaConfig `json:"kafka,omitempty"` +} + +// KafkaConfig contains configuration for exporting decisions to Kafka +type KafkaConfig struct { + Brokers []string `json:"brokers"` + Topic string `json:"topic"` + RequiredAcks string `json:"requiredAcks"` + TLS *TLSConfig `json:"tls,omitempty"` +} + +// TLSConfig contains TLS configuration for Kafka decisions export. +type TLSConfig struct { + ClientCertificateName string `json:"clientCertificateName"` + ClientCertificate string `json:"clientCertificate"` + ClientKey string `json:"clientKey"` + RootCA string `json:"rootCA"` + InsecureSkipVerify bool `json:"insecureSkipVerify"` +} + +// GetGitCredentialForRepo determines which default GitCredential to use for checking out the +// policy repository based on the URL to the policy repository. +func (c *ProjectConfig) GetGitCredentialForRepo(repo string) *GitCredential { + sort.Slice(c.GitCredentials, func(i, j int) bool { + return len(c.GitCredentials[i].RepoPrefix) > len(c.GitCredentials[j].RepoPrefix) + }) + + for _, gitCredential := range c.GitCredentials { + if strings.HasPrefix(repo, gitCredential.RepoPrefix) { + return gitCredential + } + } + + return nil +} + +// SLPRestartEnabled returns true if the OPA restart is enabled +func (c *ProjectConfig) SLPRestartEnabled() bool { + if c.PodRestart == nil || c.PodRestart.SLPRestart == nil { + return false + } + return c.PodRestart.SLPRestart.Enabled +} + +// OPARestartEnabled returns true if the OPA restart is enabled +func (c *ProjectConfig) OPARestartEnabled() bool { + if c.PodRestart == nil || c.PodRestart.OPARestart == nil { + return false + } + return c.PodRestart.OPARestart.Enabled +} + +func init() { + SchemeBuilder.Register(&ProjectConfig{}) +} diff --git a/api/config/v2alpha3/zz_generated.deepcopy.go b/api/config/v2alpha3/zz_generated.deepcopy.go new file mode 100644 index 00000000..5d389230 --- /dev/null +++ b/api/config/v2alpha3/zz_generated.deepcopy.go @@ -0,0 +1,643 @@ +//go:build !ignore_autogenerated + +/* +Copyright (C) 2025 Bankdata (bankdata@bankdata.dk) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v2alpha3 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BundleObjectStorage) DeepCopyInto(out *BundleObjectStorage) { + *out = *in + if in.S3 != nil { + in, out := &in.S3, &out.S3 + *out = new(S3ObjectStorage) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BundleObjectStorage. +func (in *BundleObjectStorage) DeepCopy() *BundleObjectStorage { + if in == nil { + return nil + } + out := new(BundleObjectStorage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DecisionAPIConfig) DeepCopyInto(out *DecisionAPIConfig) { + *out = *in + out.Reporting = in.Reporting +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecisionAPIConfig. +func (in *DecisionAPIConfig) DeepCopy() *DecisionAPIConfig { + if in == nil { + return nil + } + out := new(DecisionAPIConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DecisionLog) DeepCopyInto(out *DecisionLog) { + *out = *in + in.RequestContext.DeepCopyInto(&out.RequestContext) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecisionLog. +func (in *DecisionLog) DeepCopy() *DecisionLog { + if in == nil { + return nil + } + out := new(DecisionLog) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DecisionLogReporting) DeepCopyInto(out *DecisionLogReporting) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DecisionLogReporting. +func (in *DecisionLogReporting) DeepCopy() *DecisionLogReporting { + if in == nil { + return nil + } + out := new(DecisionLogReporting) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DefaultRequirement) DeepCopyInto(out *DefaultRequirement) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DefaultRequirement. +func (in *DefaultRequirement) DeepCopy() *DefaultRequirement { + if in == nil { + return nil + } + out := new(DefaultRequirement) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExporterConfig) DeepCopyInto(out *ExporterConfig) { + *out = *in + if in.Kafka != nil { + in, out := &in.Kafka, &out.Kafka + *out = new(KafkaConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterConfig. +func (in *ExporterConfig) DeepCopy() *ExporterConfig { + if in == nil { + return nil + } + out := new(ExporterConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitCredential) DeepCopyInto(out *GitCredential) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitCredential. +func (in *GitCredential) DeepCopy() *GitCredential { + if in == nil { + return nil + } + out := new(GitCredential) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitCredentials) DeepCopyInto(out *GitCredentials) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitCredentials. +func (in *GitCredentials) DeepCopy() *GitCredentials { + if in == nil { + return nil + } + out := new(GitCredentials) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTP) DeepCopyInto(out *HTTP) { + *out = *in + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTP. +func (in *HTTP) DeepCopy() *HTTP { + if in == nil { + return nil + } + out := new(HTTP) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPMetricsConfig) DeepCopyInto(out *HTTPMetricsConfig) { + *out = *in + if in.Buckets != nil { + in, out := &in.Buckets, &out.Buckets + *out = make([]float64, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMetricsConfig. +func (in *HTTPMetricsConfig) DeepCopy() *HTTPMetricsConfig { + if in == nil { + return nil + } + out := new(HTTPMetricsConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KafkaConfig) DeepCopyInto(out *KafkaConfig) { + *out = *in + if in.Brokers != nil { + in, out := &in.Brokers, &out.Brokers + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSConfig) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KafkaConfig. +func (in *KafkaConfig) DeepCopy() *KafkaConfig { + if in == nil { + return nil + } + out := new(KafkaConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LeaderElectionConfig) DeepCopyInto(out *LeaderElectionConfig) { + *out = *in + out.LeaseDuration = in.LeaseDuration + out.RenewDeadline = in.RenewDeadline + out.RetryPeriod = in.RetryPeriod +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LeaderElectionConfig. +func (in *LeaderElectionConfig) DeepCopy() *LeaderElectionConfig { + if in == nil { + return nil + } + out := new(LeaderElectionConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricsConfig) DeepCopyInto(out *MetricsConfig) { + *out = *in + in.Prometheus.DeepCopyInto(&out.Prometheus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsConfig. +func (in *MetricsConfig) DeepCopy() *MetricsConfig { + if in == nil { + return nil + } + out := new(MetricsConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NotificationWebhooksConfig) DeepCopyInto(out *NotificationWebhooksConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotificationWebhooksConfig. +func (in *NotificationWebhooksConfig) DeepCopy() *NotificationWebhooksConfig { + if in == nil { + return nil + } + out := new(NotificationWebhooksConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OPABundleServer) DeepCopyInto(out *OPABundleServer) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OPABundleServer. +func (in *OPABundleServer) DeepCopy() *OPABundleServer { + if in == nil { + return nil + } + out := new(OPABundleServer) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OPAConfig) DeepCopyInto(out *OPAConfig) { + *out = *in + in.DecisionLogs.DeepCopyInto(&out.DecisionLogs) + in.Metrics.DeepCopyInto(&out.Metrics) + if in.BundleServer != nil { + in, out := &in.BundleServer, &out.BundleServer + *out = new(OPABundleServer) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OPAConfig. +func (in *OPAConfig) DeepCopy() *OPAConfig { + if in == nil { + return nil + } + out := new(OPAConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OPAControlPlaneConfig) DeepCopyInto(out *OPAControlPlaneConfig) { + *out = *in + if in.GitCredentials != nil { + in, out := &in.GitCredentials, &out.GitCredentials + *out = make([]*GitCredentials, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(GitCredentials) + **out = **in + } + } + } + if in.BundleObjectStorage != nil { + in, out := &in.BundleObjectStorage, &out.BundleObjectStorage + *out = new(BundleObjectStorage) + (*in).DeepCopyInto(*out) + } + if in.DefaultRequirements != nil { + in, out := &in.DefaultRequirements, &out.DefaultRequirements + *out = make([]DefaultRequirement, len(*in)) + copy(*out, *in) + } + if in.DecisionAPIConfig != nil { + in, out := &in.DecisionAPIConfig, &out.DecisionAPIConfig + *out = new(DecisionAPIConfig) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OPAControlPlaneConfig. +func (in *OPAControlPlaneConfig) DeepCopy() *OPAControlPlaneConfig { + if in == nil { + return nil + } + out := new(OPAControlPlaneConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OPARestartConfig) DeepCopyInto(out *OPARestartConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OPARestartConfig. +func (in *OPARestartConfig) DeepCopy() *OPARestartConfig { + if in == nil { + return nil + } + out := new(OPARestartConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodRestartConfig) DeepCopyInto(out *PodRestartConfig) { + *out = *in + if in.SLPRestart != nil { + in, out := &in.SLPRestart, &out.SLPRestart + *out = new(SLPRestartConfig) + **out = **in + } + if in.OPARestart != nil { + in, out := &in.OPARestart, &out.OPARestart + *out = new(OPARestartConfig) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodRestartConfig. +func (in *PodRestartConfig) DeepCopy() *PodRestartConfig { + if in == nil { + return nil + } + out := new(PodRestartConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProjectConfig) DeepCopyInto(out *ProjectConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.EnableDeltaBundlesDefault != nil { + in, out := &in.EnableDeltaBundlesDefault, &out.EnableDeltaBundlesDefault + *out = new(bool) + **out = **in + } + if in.DatasourceIgnorePatterns != nil { + in, out := &in.DatasourceIgnorePatterns, &out.DatasourceIgnorePatterns + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.GitCredentials != nil { + in, out := &in.GitCredentials, &out.GitCredentials + *out = make([]*GitCredential, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(GitCredential) + **out = **in + } + } + } + if in.LeaderElection != nil { + in, out := &in.LeaderElection, &out.LeaderElection + *out = new(LeaderElectionConfig) + **out = **in + } + if in.NotificationWebhooks != nil { + in, out := &in.NotificationWebhooks, &out.NotificationWebhooks + *out = new(NotificationWebhooksConfig) + **out = **in + } + if in.Sentry != nil { + in, out := &in.Sentry, &out.Sentry + *out = new(SentryConfig) + **out = **in + } + if in.SSO != nil { + in, out := &in.SSO, &out.SSO + *out = new(SSOConfig) + **out = **in + } + out.Styra = in.Styra + in.OPA.DeepCopyInto(&out.OPA) + if in.SystemUserRoles != nil { + in, out := &in.SystemUserRoles, &out.SystemUserRoles + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.DecisionsExporter != nil { + in, out := &in.DecisionsExporter, &out.DecisionsExporter + *out = new(ExporterConfig) + (*in).DeepCopyInto(*out) + } + if in.ActivityExporter != nil { + in, out := &in.ActivityExporter, &out.ActivityExporter + *out = new(ExporterConfig) + (*in).DeepCopyInto(*out) + } + if in.PodRestart != nil { + in, out := &in.PodRestart, &out.PodRestart + *out = new(PodRestartConfig) + (*in).DeepCopyInto(*out) + } + if in.OPAControlPlaneConfig != nil { + in, out := &in.OPAControlPlaneConfig, &out.OPAControlPlaneConfig + *out = new(OPAControlPlaneConfig) + (*in).DeepCopyInto(*out) + } + if in.UserCredentialHandler != nil { + in, out := &in.UserCredentialHandler, &out.UserCredentialHandler + *out = new(UserCredentialHandler) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProjectConfig. +func (in *ProjectConfig) DeepCopy() *ProjectConfig { + if in == nil { + return nil + } + out := new(ProjectConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProjectConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrometheusMetricsConfig) DeepCopyInto(out *PrometheusMetricsConfig) { + *out = *in + in.HTTP.DeepCopyInto(&out.HTTP) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusMetricsConfig. +func (in *PrometheusMetricsConfig) DeepCopy() *PrometheusMetricsConfig { + if in == nil { + return nil + } + out := new(PrometheusMetricsConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RequestContext) DeepCopyInto(out *RequestContext) { + *out = *in + in.HTTP.DeepCopyInto(&out.HTTP) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestContext. +func (in *RequestContext) DeepCopy() *RequestContext { + if in == nil { + return nil + } + out := new(RequestContext) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S3Handler) DeepCopyInto(out *S3Handler) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S3Handler. +func (in *S3Handler) DeepCopy() *S3Handler { + if in == nil { + return nil + } + out := new(S3Handler) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S3ObjectStorage) DeepCopyInto(out *S3ObjectStorage) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S3ObjectStorage. +func (in *S3ObjectStorage) DeepCopy() *S3ObjectStorage { + if in == nil { + return nil + } + out := new(S3ObjectStorage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SLPRestartConfig) DeepCopyInto(out *SLPRestartConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SLPRestartConfig. +func (in *SLPRestartConfig) DeepCopy() *SLPRestartConfig { + if in == nil { + return nil + } + out := new(SLPRestartConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SSOConfig) DeepCopyInto(out *SSOConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SSOConfig. +func (in *SSOConfig) DeepCopy() *SSOConfig { + if in == nil { + return nil + } + out := new(SSOConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SentryConfig) DeepCopyInto(out *SentryConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SentryConfig. +func (in *SentryConfig) DeepCopy() *SentryConfig { + if in == nil { + return nil + } + out := new(SentryConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StyraConfig) DeepCopyInto(out *StyraConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StyraConfig. +func (in *StyraConfig) DeepCopy() *StyraConfig { + if in == nil { + return nil + } + out := new(StyraConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSConfig) DeepCopyInto(out *TLSConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSConfig. +func (in *TLSConfig) DeepCopy() *TLSConfig { + if in == nil { + return nil + } + out := new(TLSConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserCredentialHandler) DeepCopyInto(out *UserCredentialHandler) { + *out = *in + if in.S3 != nil { + in, out := &in.S3, &out.S3 + *out = new(S3Handler) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserCredentialHandler. +func (in *UserCredentialHandler) DeepCopy() *UserCredentialHandler { + if in == nil { + return nil + } + out := new(UserCredentialHandler) + in.DeepCopyInto(out) + return out +} diff --git a/internal/config/config.go b/internal/config/config.go index 33ae1007..ad6c952e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -22,6 +22,7 @@ import ( "regexp" "github.com/bankdata/styra-controller/api/config/v2alpha2" + "github.com/bankdata/styra-controller/api/config/v2alpha3" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -104,17 +105,65 @@ func deserialize(data []byte, scheme *runtime.Scheme) (*v2alpha2.ProjectConfig, cfg := &v2alpha2.ProjectConfig{} + switch gvk.Version { + case v2alpha3.GroupVersion.Version: + var v2cfg v2alpha3.ProjectConfig + if _, _, err := decoder.Decode(data, nil, &v2cfg); err != nil { + return nil, errors.Wrap(err, "could not decode into kind") + } + cfg = v2cfg.ToV2Alpha3() + case v2alpha2.GroupVersion.Version: + if _, _, err := decoder.Decode(data, nil, cfg); err != nil { + return nil, errors.Wrap(err, "could not decode into kind") + } + default: + return nil, errors.New("unsupported api version") + } + + return cfg, nil +} + +/*func deserialize(data []byte, scheme *runtime.Scheme) (*v2alpha2.ProjectConfig, error) { + decoder := serializer.NewCodecFactory(scheme).UniversalDeserializer() + _, gvk, err := decoder.Decode(data, nil, nil) + if err != nil { + return nil, errors.Wrap(err, "could not decode config") + } + + if gvk.Group != v2alpha2.GroupVersion.Group { + return nil, errors.New("unsupported api group") + } + + if gvk.Kind != "ProjectConfig" { + return nil, errors.New("unsupported api kind") + } + + cfg := &v2alpha2.ProjectConfig{} + switch gvk.Version { case v2alpha2.GroupVersion.Version: if _, _, err := decoder.Decode(data, nil, cfg); err != nil { return nil, errors.Wrap(err, "could not decode into kind") } + case v2alpha1.GroupVersion.Version: + var v2cfg v2alpha1.ProjectConfig + if _, _, err := decoder.Decode(data, nil, &v2cfg); err != nil { + return nil, errors.Wrap(err, "could not decode into kind") + } + cfg = v2cfg.ToV2Alpha2() + case v1.GroupVersion.Version: + var v1cfg v1.ProjectConfig + if _, _, err := decoder.Decode(data, nil, &v1cfg); err != nil { + return nil, errors.Wrap(err, "could not decode into kind") + } + cfg = v1cfg.ToV2Alpha1().ToV2Alpha2() default: return nil, errors.New("unsupported api version") } return cfg, nil } +*/ // MatchesIgnorePattern matches a specified ignore pattern, and excludes matches from being deleted func MatchesIgnorePattern(ignorePatterns []string, id string) (bool, error) { From b6014807a82953dfa9322b23bd9a6a83bd3d01e4 Mon Sep 17 00:00:00 2001 From: jgs_bankdata Date: Fri, 6 Mar 2026 10:57:34 +0100 Subject: [PATCH 09/14] done with converting to v2alpha3 - tests needs update --- api/config/v2alpha2/projectconfig_types.go | 222 +++++++++++++++++- cmd/main.go | 20 +- cmd/main_test.go | 18 +- internal/config/config.go | 56 +---- .../controller/styra/library_controller.go | 6 +- .../controller/styra/system_controller.go | 6 +- .../styra/system_controller_test.go | 4 +- internal/k8sconv/k8sconv.go | 8 +- internal/k8sconv/k8sconv_test.go | 66 +++--- pkg/ocp/opaconfig.go | 4 +- pkg/ocp/sources.go | 4 +- pkg/s3/client.go | 4 +- .../controller/controller_suite_test.go | 40 ++-- 13 files changed, 309 insertions(+), 149 deletions(-) diff --git a/api/config/v2alpha2/projectconfig_types.go b/api/config/v2alpha2/projectconfig_types.go index c4c18270..bc9d84ae 100644 --- a/api/config/v2alpha2/projectconfig_types.go +++ b/api/config/v2alpha2/projectconfig_types.go @@ -450,29 +450,231 @@ func (c *ProjectConfig) OPARestartEnabled() bool { return c.PodRestart.OPARestart.Enabled } -// ToV2Alpha2 returns this ProjectConfig converted to a v2alpha2.ProjectConfig -func (c *ProjectConfig) ToV2Alpha2() *v2alpha3.ProjectConfig { - v2cfg := &v2alpha3.ProjectConfig{ +// ToV2Alpha3 returns this ProjectConfig converted to a v2alpha3.ProjectConfig +func (c *ProjectConfig) ToV2Alpha3() *v2alpha3.ProjectConfig { + v2cfg3 := &v2alpha3.ProjectConfig{ ControllerClass: c.ControllerClass, DeletionProtectionDefault: c.DeletionProtectionDefault, + ReadOnly: c.ReadOnly, + EnableDeltaBundlesDefault: c.EnableDeltaBundlesDefault, DisableCRDWebhooks: c.DisableCRDWebhooks, EnableMigrations: c.EnableMigrations, + DatasourceIgnorePatterns: c.DatasourceIgnorePatterns, LogLevel: c.LogLevel, Styra: v2alpha3.StyraConfig{ Token: c.Styra.Token, Address: c.Styra.Address, }, - SystemPrefix: c.SystemPrefix, - SystemSuffix: c.SystemSuffix, - SystemUserRoles: c.SystemUserRoles, + SystemPrefix: c.SystemPrefix, + SystemSuffix: c.SystemSuffix, + SystemUserRoles: c.SystemUserRoles, + EnableStyraReconciliation: c.EnableStyraReconciliation, + EnableOPAControlPlaneReconciliation: c.EnableOPAControlPlaneReconciliation, + EnableOPAControlPlaneReconciliationTestData: c.EnableOPAControlPlaneReconciliationTestData, } - for _, field in {c.OPAControlPlaneConfig.DefaultRequirements{ - + if c.GitCredentials != nil { + v2cfg3.GitCredentials = make([]*v2alpha3.GitCredential, len(c.GitCredentials)) + for i, c := range c.GitCredentials { + v2cfg3.GitCredentials[i] = &v2alpha3.GitCredential{ + User: c.User, + Password: c.Password, + RepoPrefix: c.RepoPrefix, + } + } + } + + if c.LeaderElection != nil { + v2cfg3.LeaderElection = &v2alpha3.LeaderElectionConfig{ + LeaseDuration: c.LeaderElection.LeaseDuration, + RenewDeadline: c.LeaderElection.RenewDeadline, + RetryPeriod: c.LeaderElection.RetryPeriod, + } + } + + if c.NotificationWebhooks != nil { + v2cfg3.NotificationWebhooks = &v2alpha3.NotificationWebhooksConfig{ + SystemDatasourceChanged: c.NotificationWebhooks.SystemDatasourceChanged, + LibraryDatasourceChanged: c.NotificationWebhooks.LibraryDatasourceChanged, + } + } + + if c.Sentry != nil { + v2cfg3.Sentry = &v2alpha3.SentryConfig{ + DSN: c.Sentry.DSN, + Debug: c.Sentry.Debug, + Environment: c.Sentry.Environment, + HTTPSProxy: c.Sentry.HTTPSProxy, + } + } + + if c.SSO != nil { + v2cfg3.SSO = &v2alpha3.SSOConfig{ + IdentityProvider: c.SSO.IdentityProvider, + JWTGroupsClaim: c.SSO.JWTGroupsClaim, + } + } + + v2cfg3.OPA = v2alpha3.OPAConfig{ + DecisionLogs: v2alpha3.DecisionLog{ + RequestContext: v2alpha3.RequestContext{ + HTTP: v2alpha3.HTTP{ + Headers: c.OPA.DecisionLogs.RequestContext.HTTP.Headers, + }, + }, + }, + Metrics: v2alpha3.MetricsConfig{ + Prometheus: v2alpha3.PrometheusMetricsConfig{ + HTTP: v2alpha3.HTTPMetricsConfig{ + Buckets: c.OPA.Metrics.Prometheus.HTTP.Buckets, + }, + }, + }, + PersistBundle: c.OPA.PersistBundle, + PersistBundleDirectory: c.OPA.PersistBundleDirectory, + } + + if c.OPA.BundleServer != nil { + v2cfg3.OPA.BundleServer = &v2alpha3.OPABundleServer{ + URL: c.OPA.BundleServer.URL, + Path: c.OPA.BundleServer.Path, + } + } + + if c.DecisionsExporter != nil { + v2cfg3.DecisionsExporter = &v2alpha3.ExporterConfig{ + Enabled: c.DecisionsExporter.Enabled, + Interval: c.DecisionsExporter.Interval, + } + + if c.DecisionsExporter.Kafka != nil { + v2cfg3.DecisionsExporter.Kafka = &v2alpha3.KafkaConfig{ + Brokers: c.DecisionsExporter.Kafka.Brokers, + Topic: c.DecisionsExporter.Kafka.Topic, + RequiredAcks: c.DecisionsExporter.Kafka.RequiredAcks, + } + + if c.DecisionsExporter.Kafka.TLS != nil { + v2cfg3.DecisionsExporter.Kafka.TLS = &v2alpha3.TLSConfig{ + ClientCertificateName: c.DecisionsExporter.Kafka.TLS.ClientCertificateName, + ClientCertificate: c.DecisionsExporter.Kafka.TLS.ClientCertificate, + ClientKey: c.DecisionsExporter.Kafka.TLS.ClientKey, + RootCA: c.DecisionsExporter.Kafka.TLS.RootCA, + InsecureSkipVerify: c.DecisionsExporter.Kafka.TLS.InsecureSkipVerify, + } + } + } + } + + if c.ActivityExporter != nil { + v2cfg3.ActivityExporter = &v2alpha3.ExporterConfig{ + Enabled: c.ActivityExporter.Enabled, + Interval: c.ActivityExporter.Interval, + } + + if c.ActivityExporter.Kafka != nil { + v2cfg3.ActivityExporter.Kafka = &v2alpha3.KafkaConfig{ + Brokers: c.ActivityExporter.Kafka.Brokers, + Topic: c.ActivityExporter.Kafka.Topic, + RequiredAcks: c.ActivityExporter.Kafka.RequiredAcks, + } + + if c.ActivityExporter.Kafka.TLS != nil { + v2cfg3.ActivityExporter.Kafka.TLS = &v2alpha3.TLSConfig{ + ClientCertificateName: c.ActivityExporter.Kafka.TLS.ClientCertificateName, + ClientCertificate: c.ActivityExporter.Kafka.TLS.ClientCertificate, + ClientKey: c.ActivityExporter.Kafka.TLS.ClientKey, + RootCA: c.ActivityExporter.Kafka.TLS.RootCA, + InsecureSkipVerify: c.ActivityExporter.Kafka.TLS.InsecureSkipVerify, + } + } + } + } + + if c.PodRestart != nil { + v2cfg3.PodRestart = &v2alpha3.PodRestartConfig{} + + if c.PodRestart.SLPRestart != nil { + v2cfg3.PodRestart.SLPRestart = &v2alpha3.SLPRestartConfig{ + Enabled: c.PodRestart.SLPRestart.Enabled, + DeploymentType: c.PodRestart.SLPRestart.DeploymentType, + } + } + + if c.PodRestart.OPARestart != nil { + v2cfg3.PodRestart.OPARestart = &v2alpha3.OPARestartConfig{ + Enabled: c.PodRestart.OPARestart.Enabled, + DeploymentType: c.PodRestart.OPARestart.DeploymentType, + } + } + } + + if c.OPAControlPlaneConfig != nil { + v2cfg3.OPAControlPlaneConfig = &v2alpha3.OPAControlPlaneConfig{ + Address: c.OPAControlPlaneConfig.Address, + Token: c.OPAControlPlaneConfig.Token, + SystemDatasourceChanged: c.OPAControlPlaneConfig.SystemDatasourceChanged, + LibraryDatasourceChanged: c.OPAControlPlaneConfig.LibraryDatasourceChanged, + } + + if c.OPAControlPlaneConfig.GitCredentials != nil { + v2cfg3.OPAControlPlaneConfig.GitCredentials = make([]*v2alpha3.GitCredentials, len(c.OPAControlPlaneConfig.GitCredentials)) + for i, c := range c.OPAControlPlaneConfig.GitCredentials { + v2cfg3.OPAControlPlaneConfig.GitCredentials[i] = &v2alpha3.GitCredentials{ + ID: c.ID, + RepoPrefix: c.RepoPrefix, + } + } + } + + if c.OPAControlPlaneConfig.BundleObjectStorage != nil && c.OPAControlPlaneConfig.BundleObjectStorage.S3 != nil { + v2cfg3.OPAControlPlaneConfig.BundleObjectStorage = &v2alpha3.BundleObjectStorage{ + S3: &v2alpha3.S3ObjectStorage{ + Bucket: c.OPAControlPlaneConfig.BundleObjectStorage.S3.Bucket, + URL: c.OPAControlPlaneConfig.BundleObjectStorage.S3.URL, + Region: c.OPAControlPlaneConfig.BundleObjectStorage.S3.Region, + OCPConfigSecretName: c.OPAControlPlaneConfig.BundleObjectStorage.S3.OCPConfigSecretName, + }, + } + } + + v2cfg3.OPAControlPlaneConfig.DefaultRequirements = make([]v2alpha3.DefaultRequirement, len(c.OPAControlPlaneConfig.DefaultRequirements)) + + for i, requirement := range c.OPAControlPlaneConfig.DefaultRequirements { + v2cfg3.OPAControlPlaneConfig.DefaultRequirements[i] = v2alpha3.DefaultRequirement{ + Name: requirement, + GitSource: false, + DataSource: false, + } + } + + if c.OPAControlPlaneConfig.DecisionAPIConfig != nil { + v2cfg3.OPAControlPlaneConfig.DecisionAPIConfig = &v2alpha3.DecisionAPIConfig{ + ServiceURL: c.OPAControlPlaneConfig.DecisionAPIConfig.ServiceURL, + Reporting: v2alpha3.DecisionLogReporting{ + MaxDelaySeconds: c.OPAControlPlaneConfig.DecisionAPIConfig.Reporting.MaxDelaySeconds, + MinDelaySeconds: c.OPAControlPlaneConfig.DecisionAPIConfig.Reporting.MinDelaySeconds, + UploadSizeLimitBytes: c.OPAControlPlaneConfig.DecisionAPIConfig.Reporting.UploadSizeLimitBytes, + }, + } + } + } + + if c.UserCredentialHandler != nil && c.UserCredentialHandler.S3 != nil { + v2cfg3.UserCredentialHandler = &v2alpha3.UserCredentialHandler{ + S3: &v2alpha3.S3Handler{ + Bucket: c.UserCredentialHandler.S3.Bucket, + URL: c.UserCredentialHandler.S3.URL, + Region: c.UserCredentialHandler.S3.Region, + AccessKeyID: c.UserCredentialHandler.S3.AccessKeyID, + SecretAccessKey: c.UserCredentialHandler.S3.SecretAccessKey, + }, + } } - v2cfg.OPAControlPlaneConfig.DefaultRequirements = - return v2cfg + v2cfg3.DecisionsExporter = &v2alpha3.ExporterConfig{} + + return v2cfg3 } func init() { diff --git a/cmd/main.go b/cmd/main.go index fb4b3f8b..572da06c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -43,7 +43,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" styrav1alpha1 "github.com/bankdata/styra-controller/api/styra/v1alpha1" styrav1beta1 "github.com/bankdata/styra-controller/api/styra/v1beta1" "github.com/bankdata/styra-controller/internal/config" @@ -74,7 +74,7 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(styrav1alpha1.AddToScheme(scheme)) utilruntime.Must(styrav1beta1.AddToScheme(scheme)) - utilruntime.Must(configv2alpha2.AddToScheme(scheme)) + utilruntime.Must(configv2alpha3.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -194,13 +194,13 @@ func main() { styraClient = styra.New(styraHostURL, styraToken) if err := configureExporter( - styraClient, ctrlConfig.DecisionsExporter, configv2alpha2.ExporterConfigTypeDecisions); err != nil { - log.Error(err, fmt.Sprintf("unable to configure %s", configv2alpha2.ExporterConfigTypeDecisions)) + styraClient, ctrlConfig.DecisionsExporter, configv2alpha3.ExporterConfigTypeDecisions); err != nil { + log.Error(err, fmt.Sprintf("unable to configure %s", configv2alpha3.ExporterConfigTypeDecisions)) } if err := configureExporter( - styraClient, ctrlConfig.ActivityExporter, configv2alpha2.ExporterConfigTypeActivity); err != nil { - log.Error(err, fmt.Sprintf("unable to configure %s", configv2alpha2.ExporterConfigTypeActivity)) + styraClient, ctrlConfig.ActivityExporter, configv2alpha3.ExporterConfigTypeActivity); err != nil { + log.Error(err, fmt.Sprintf("unable to configure %s", configv2alpha3.ExporterConfigTypeActivity)) } } @@ -354,8 +354,8 @@ func exit(err error) { func configureExporter( styraClient styra.ClientInterface, - exporterConfig *configv2alpha2.ExporterConfig, - exporterType configv2alpha2.ExporterConfigType) error { + exporterConfig *configv2alpha3.ExporterConfig, + exporterType configv2alpha3.ExporterConfigType) error { if exporterConfig == nil { ctrl.Log.Info(fmt.Sprintf("no exporter configuration found for %s", exporterType)) return nil @@ -371,10 +371,10 @@ func configureExporter( return err } - if exporterType == configv2alpha2.ExporterConfigTypeActivity { + if exporterType == configv2alpha3.ExporterConfigTypeActivity { rawJSON := json.RawMessage("{\"activity_exporter\": null}") _, err = styraClient.UpdateWorkspaceRaw(context.Background(), rawJSON) - } else if exporterType == configv2alpha2.ExporterConfigTypeDecisions { + } else if exporterType == configv2alpha3.ExporterConfigTypeDecisions { rawJSON := json.RawMessage("{\"decisions_exporter\": null}") _, err = styraClient.UpdateWorkspaceRaw(context.Background(), rawJSON) } diff --git a/cmd/main_test.go b/cmd/main_test.go index efcbb2e1..e867126f 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -19,7 +19,7 @@ package main import ( "encoding/json" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" "github.com/bankdata/styra-controller/pkg/styra" styraclientmock "github.com/bankdata/styra-controller/pkg/styra/mocks" ginkgo "github.com/onsi/ginkgo/v2" @@ -41,11 +41,11 @@ var _ = ginkgo.Describe("ConfigureDecisionsExporter", func() { // Test cases for ConfigureExporter for Decisions ginkgo.DescribeTable("ConfigureDecisionsExporter", func(test test) { // Arrange - ctrlConfig := &configv2alpha2.ProjectConfig{ - DecisionsExporter: &configv2alpha2.ExporterConfig{ + ctrlConfig := &configv2alpha3.ProjectConfig{ + DecisionsExporter: &configv2alpha3.ExporterConfig{ Enabled: test.enabled, - Kafka: &configv2alpha2.KafkaConfig{ - TLS: &configv2alpha2.TLSConfig{ + Kafka: &configv2alpha3.KafkaConfig{ + TLS: &configv2alpha3.TLSConfig{ ClientCertificateName: "test-cert-name", ClientCertificate: "test-cert", ClientKey: "test-key", @@ -168,11 +168,11 @@ var _ = ginkgo.Describe("ConfigureActivityExporter", func() { // Test cases for ConfigureExporter for Activity ginkgo.DescribeTable("ConfigureActivityExporter", func(test test) { // Arrange - ctrlConfig := &configv2alpha2.ProjectConfig{ - ActivityExporter: &configv2alpha2.ExporterConfig{ + ctrlConfig := &configv2alpha3.ProjectConfig{ + ActivityExporter: &configv2alpha3.ExporterConfig{ Enabled: test.enabled, - Kafka: &configv2alpha2.KafkaConfig{ - TLS: &configv2alpha2.TLSConfig{ + Kafka: &configv2alpha3.KafkaConfig{ + TLS: &configv2alpha3.TLSConfig{ ClientCertificateName: "test-cert-name", ClientCertificate: "test-cert", ClientKey: "test-key", diff --git a/internal/config/config.go b/internal/config/config.go index ad6c952e..1389f4a7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -40,7 +40,7 @@ const ( // Load loads controller configuration from the given file using the types // registered in the scheme. -func Load(file string, scheme *runtime.Scheme) (*v2alpha2.ProjectConfig, error) { +func Load(file string, scheme *runtime.Scheme) (*v2alpha3.ProjectConfig, error) { bs, err := os.ReadFile(file) if err != nil { return nil, errors.Wrap(err, "could not read config file") @@ -49,7 +49,7 @@ func Load(file string, scheme *runtime.Scheme) (*v2alpha2.ProjectConfig, error) } // OptionsFromConfig creates a manager.Options based on a configuration file -func OptionsFromConfig(cfg *v2alpha2.ProjectConfig, scheme *runtime.Scheme) manager.Options { +func OptionsFromConfig(cfg *v2alpha3.ProjectConfig, scheme *runtime.Scheme) manager.Options { o := manager.Options{ Scheme: scheme, HealthProbeBindAddress: healthProbeBindAddress, @@ -72,7 +72,7 @@ func OptionsFromConfig(cfg *v2alpha2.ProjectConfig, scheme *runtime.Scheme) mana // TokenFromConfig returns the Styra DAS api token directly from "styra.token" // in the config or using the "styra.tokenSecretPath" to retrieve it fra a secret -func TokenFromConfig(cfg *v2alpha2.ProjectConfig) (string, error) { +func TokenFromConfig(cfg *v2alpha3.ProjectConfig) (string, error) { if cfg.Styra.Token != "" { return cfg.Styra.Token, nil } @@ -88,7 +88,7 @@ func TokenFromConfig(cfg *v2alpha2.ProjectConfig) (string, error) { return "", errors.New("No token or tokenSecretPath defined in the config") } -func deserialize(data []byte, scheme *runtime.Scheme) (*v2alpha2.ProjectConfig, error) { +func deserialize(data []byte, scheme *runtime.Scheme) (*v2alpha3.ProjectConfig, error) { decoder := serializer.NewCodecFactory(scheme).UniversalDeserializer() _, gvk, err := decoder.Decode(data, nil, nil) if err != nil { @@ -103,67 +103,25 @@ func deserialize(data []byte, scheme *runtime.Scheme) (*v2alpha2.ProjectConfig, return nil, errors.New("unsupported api kind") } - cfg := &v2alpha2.ProjectConfig{} + cfg := &v2alpha3.ProjectConfig{} switch gvk.Version { case v2alpha3.GroupVersion.Version: - var v2cfg v2alpha3.ProjectConfig - if _, _, err := decoder.Decode(data, nil, &v2cfg); err != nil { - return nil, errors.Wrap(err, "could not decode into kind") - } - cfg = v2cfg.ToV2Alpha3() - case v2alpha2.GroupVersion.Version: if _, _, err := decoder.Decode(data, nil, cfg); err != nil { return nil, errors.Wrap(err, "could not decode into kind") } - default: - return nil, errors.New("unsupported api version") - } - - return cfg, nil -} - -/*func deserialize(data []byte, scheme *runtime.Scheme) (*v2alpha2.ProjectConfig, error) { - decoder := serializer.NewCodecFactory(scheme).UniversalDeserializer() - _, gvk, err := decoder.Decode(data, nil, nil) - if err != nil { - return nil, errors.Wrap(err, "could not decode config") - } - - if gvk.Group != v2alpha2.GroupVersion.Group { - return nil, errors.New("unsupported api group") - } - - if gvk.Kind != "ProjectConfig" { - return nil, errors.New("unsupported api kind") - } - - cfg := &v2alpha2.ProjectConfig{} - - switch gvk.Version { case v2alpha2.GroupVersion.Version: - if _, _, err := decoder.Decode(data, nil, cfg); err != nil { - return nil, errors.Wrap(err, "could not decode into kind") - } - case v2alpha1.GroupVersion.Version: - var v2cfg v2alpha1.ProjectConfig + var v2cfg v2alpha2.ProjectConfig if _, _, err := decoder.Decode(data, nil, &v2cfg); err != nil { return nil, errors.Wrap(err, "could not decode into kind") } - cfg = v2cfg.ToV2Alpha2() - case v1.GroupVersion.Version: - var v1cfg v1.ProjectConfig - if _, _, err := decoder.Decode(data, nil, &v1cfg); err != nil { - return nil, errors.Wrap(err, "could not decode into kind") - } - cfg = v1cfg.ToV2Alpha1().ToV2Alpha2() + cfg = v2cfg.ToV2Alpha3() default: return nil, errors.New("unsupported api version") } return cfg, nil } -*/ // MatchesIgnorePattern matches a specified ignore pattern, and excludes matches from being deleted func MatchesIgnorePattern(ignorePatterns []string, id string) (bool, error) { diff --git a/internal/controller/styra/library_controller.go b/internal/controller/styra/library_controller.go index 2d25e8ff..eaff3052 100644 --- a/internal/controller/styra/library_controller.go +++ b/internal/controller/styra/library_controller.go @@ -41,7 +41,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" styrav1alpha1 "github.com/bankdata/styra-controller/api/styra/v1alpha1" "github.com/bankdata/styra-controller/pkg/ocp" ) @@ -52,7 +52,7 @@ type LibraryReconciler struct { Scheme *runtime.Scheme Styra styra.ClientInterface OCP ocp.ClientInterface - Config *configv2alpha2.ProjectConfig + Config *configv2alpha3.ProjectConfig WebhookClient webhook.Client } @@ -542,7 +542,7 @@ func (r *LibraryReconciler) updateRoleBindingSubjects( func createLibraryRolebindingSubjects( subjects []styrav1alpha1.LibrarySubject, - sso *configv2alpha2.SSOConfig, + sso *configv2alpha3.SSOConfig, ) []*styra.Subject { styraSubjectsByUserID := map[string]struct{}{} styraSubjectsByClaimValue := map[string]struct{}{} diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go index 9661546a..7c19acaa 100644 --- a/internal/controller/styra/system_controller.go +++ b/internal/controller/styra/system_controller.go @@ -48,7 +48,7 @@ import ( ctrlpred "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" "github.com/bankdata/styra-controller/api/styra/v1beta1" "github.com/bankdata/styra-controller/internal/config" ctrlerr "github.com/bankdata/styra-controller/internal/errors" @@ -83,7 +83,7 @@ type SystemReconciler struct { WebhookClient webhook.Client Recorder record.EventRecorder Metrics *SystemReconcilerMetrics - Config *configv2alpha2.ProjectConfig + Config *configv2alpha3.ProjectConfig } //+kubebuilder:rbac:groups=styra.bankdata.dk,resources=systems,verbs=get;list;watch;create;update;patch;delete @@ -1648,7 +1648,7 @@ func (r *SystemReconciler) reconcileSubjects( func createRolebindingSubjects( subjects []v1beta1.Subject, - sso *configv2alpha2.SSOConfig, + sso *configv2alpha3.SSOConfig, ) []*styra.Subject { styraSubjectsByUserID := map[string]struct{}{} styraSubjectsByClaimValue := map[string]struct{}{} diff --git a/internal/controller/styra/system_controller_test.go b/internal/controller/styra/system_controller_test.go index 03a4d429..78b9dfad 100644 --- a/internal/controller/styra/system_controller_test.go +++ b/internal/controller/styra/system_controller_test.go @@ -20,7 +20,7 @@ import ( ginkgo "github.com/onsi/ginkgo/v2" gomega "github.com/onsi/gomega" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" styrav1beta1 "github.com/bankdata/styra-controller/api/styra/v1beta1" "github.com/bankdata/styra-controller/pkg/ocp" "github.com/bankdata/styra-controller/pkg/styra" @@ -28,7 +28,7 @@ import ( var _ = ginkgo.DescribeTable("createRolebindingSubjects", func(subjects []styrav1beta1.Subject, expectedSubject []*styra.Subject) { - gomega.Ω(createRolebindingSubjects(subjects, &configv2alpha2.SSOConfig{ + gomega.Ω(createRolebindingSubjects(subjects, &configv2alpha3.SSOConfig{ IdentityProvider: "BDAD", JWTGroupsClaim: "groups", })).To(gomega.Equal(expectedSubject)) diff --git a/internal/k8sconv/k8sconv.go b/internal/k8sconv/k8sconv.go index 438b1244..b5a9bc39 100644 --- a/internal/k8sconv/k8sconv.go +++ b/internal/k8sconv/k8sconv.go @@ -26,7 +26,7 @@ import ( "gopkg.in/yaml.v2" corev1 "k8s.io/api/core/v1" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" "github.com/bankdata/styra-controller/pkg/ocp" "github.com/bankdata/styra-controller/pkg/styra" ) @@ -143,7 +143,7 @@ type HTTPMetricsConfig struct { // OPAConfToK8sOPAConfigMapforOCP merges the information given as input into a ConfigMap for OPA func OPAConfToK8sOPAConfigMapforOCP( opaconf ocp.OPAConfig, - opaDefaultConfig configv2alpha2.OPAConfig, + opaDefaultConfig configv2alpha3.OPAConfig, customConfig map[string]interface{}, _ logr.Logger, ) (corev1.ConfigMap, error) { @@ -233,7 +233,7 @@ func OPAConfToK8sOPAConfigMapforOCP( func OPAConfToK8sOPAConfigMap( opaconf styra.OPAConfig, slpURL string, - opaDefaultConfig configv2alpha2.OPAConfig, + opaDefaultConfig configv2alpha3.OPAConfig, customConfig map[string]interface{}, ) (corev1.ConfigMap, error) { @@ -356,7 +356,7 @@ func OPAConfToK8sSLPConfigMap(opaconf styra.OPAConfig) (corev1.ConfigMap, error) // directly to Styra and not via an SLP. func OPAConfToK8sOPAConfigMapNoSLP( opaconf styra.OPAConfig, - opaDefaultConfig configv2alpha2.OPAConfig, + opaDefaultConfig configv2alpha3.OPAConfig, customConfig map[string]interface{}, ) (corev1.ConfigMap, error) { diff --git a/internal/k8sconv/k8sconv_test.go b/internal/k8sconv/k8sconv_test.go index 02d8d22a..1e826298 100644 --- a/internal/k8sconv/k8sconv_test.go +++ b/internal/k8sconv/k8sconv_test.go @@ -23,7 +23,7 @@ import ( ginkgo "github.com/onsi/ginkgo/v2" gomega "github.com/onsi/gomega" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" "github.com/bankdata/styra-controller/internal/k8sconv" "github.com/bankdata/styra-controller/pkg/ocp" "github.com/bankdata/styra-controller/pkg/styra" @@ -33,7 +33,7 @@ import ( var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMap", func() { type test struct { - opaDefaultConfig configv2alpha2.OPAConfig + opaDefaultConfig configv2alpha3.OPAConfig opaconf styra.OPAConfig slpURL string expectedCMContent string @@ -58,10 +58,10 @@ var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMap", func() { }, ginkgo.Entry("success", test{ - opaDefaultConfig: configv2alpha2.OPAConfig{ - DecisionLogs: configv2alpha2.DecisionLog{ - RequestContext: configv2alpha2.RequestContext{ - HTTP: configv2alpha2.HTTP{ + opaDefaultConfig: configv2alpha3.OPAConfig{ + DecisionLogs: configv2alpha3.DecisionLog{ + RequestContext: configv2alpha3.RequestContext{ + HTTP: configv2alpha3.HTTP{ Headers: strings.Split("header1,header2", ","), }, }, @@ -148,7 +148,7 @@ discovery: var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapNoSLP", func() { type test struct { - opaDefaultConfig configv2alpha2.OPAConfig + opaDefaultConfig configv2alpha3.OPAConfig opaconf styra.OPAConfig expectedCMContent string } @@ -173,10 +173,10 @@ var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapNoSLP", func() { }, ginkgo.Entry("success", test{ - opaDefaultConfig: configv2alpha2.OPAConfig{ - DecisionLogs: configv2alpha2.DecisionLog{ - RequestContext: configv2alpha2.RequestContext{ - HTTP: configv2alpha2.HTTP{ + opaDefaultConfig: configv2alpha3.OPAConfig{ + DecisionLogs: configv2alpha3.DecisionLog{ + RequestContext: configv2alpha3.RequestContext{ + HTTP: configv2alpha3.HTTP{ Headers: strings.Split("header1,header2", ","), }, }, @@ -221,7 +221,7 @@ decision_logs: var _ = ginkgo.Describe("OPACustomConfToK8sWithSLP", func() { type test struct { - opaDefaultConfig configv2alpha2.OPAConfig + opaDefaultConfig configv2alpha3.OPAConfig opaconf styra.OPAConfig customConfig map[string]interface{} slpURL string @@ -247,10 +247,10 @@ var _ = ginkgo.Describe("OPACustomConfToK8sWithSLP", func() { }, ginkgo.Entry("success", test{ - opaDefaultConfig: configv2alpha2.OPAConfig{ - DecisionLogs: configv2alpha2.DecisionLog{ - RequestContext: configv2alpha2.RequestContext{ - HTTP: configv2alpha2.HTTP{ + opaDefaultConfig: configv2alpha3.OPAConfig{ + DecisionLogs: configv2alpha3.DecisionLog{ + RequestContext: configv2alpha3.RequestContext{ + HTTP: configv2alpha3.HTTP{ Headers: strings.Split("header1,header2", ","), }, }, @@ -296,7 +296,7 @@ distributed_tracing: var _ = ginkgo.Describe("OPACustomConfToK8sNoSLP", func() { type test struct { - opaDefaultConfig configv2alpha2.OPAConfig + opaDefaultConfig configv2alpha3.OPAConfig opaconf styra.OPAConfig customConfig map[string]interface{} expectedCMContent string @@ -321,10 +321,10 @@ var _ = ginkgo.Describe("OPACustomConfToK8sNoSLP", func() { }, ginkgo.Entry("success", test{ - opaDefaultConfig: configv2alpha2.OPAConfig{ - DecisionLogs: configv2alpha2.DecisionLog{ - RequestContext: configv2alpha2.RequestContext{ - HTTP: configv2alpha2.HTTP{ + opaDefaultConfig: configv2alpha3.OPAConfig{ + DecisionLogs: configv2alpha3.DecisionLog{ + RequestContext: configv2alpha3.RequestContext{ + HTTP: configv2alpha3.HTTP{ Headers: strings.Split("header1,header2", ","), }, }, @@ -379,7 +379,7 @@ distributed_tracing: var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapforOCP", func() { type test struct { - opaDefaultConfig configv2alpha2.OPAConfig + opaDefaultConfig configv2alpha3.OPAConfig opaconf ocp.OPAConfig customConfig map[string]interface{} expectedCMContent string @@ -407,10 +407,10 @@ var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapforOCP", func() { gomega.Expect(actualMap).To(gomega.Equal(expectedMap)) }, ginkgo.Entry("success", test{ - opaDefaultConfig: configv2alpha2.OPAConfig{ - DecisionLogs: configv2alpha2.DecisionLog{ - RequestContext: configv2alpha2.RequestContext{ - HTTP: configv2alpha2.HTTP{ + opaDefaultConfig: configv2alpha3.OPAConfig{ + DecisionLogs: configv2alpha3.DecisionLog{ + RequestContext: configv2alpha3.RequestContext{ + HTTP: configv2alpha3.HTTP{ Headers: strings.Split("header1,header2", ","), }, }, @@ -438,7 +438,7 @@ var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapforOCP", func() { }, }, }, - DecisionLogReporting: configv2alpha2.DecisionLogReporting{ + DecisionLogReporting: configv2alpha3.DecisionLogReporting{ UploadSizeLimitBytes: 1, MinDelaySeconds: 2, MaxDelaySeconds: 3, @@ -498,7 +498,7 @@ distributed_tracing: var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapforOCP", func() { type test struct { - opaDefaultConfig configv2alpha2.OPAConfig + opaDefaultConfig configv2alpha3.OPAConfig opaconf ocp.OPAConfig customConfig map[string]interface{} expectedCMContent string @@ -527,10 +527,10 @@ var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapforOCP", func() { }, ginkgo.Entry("success", test{ - opaDefaultConfig: configv2alpha2.OPAConfig{ - DecisionLogs: configv2alpha2.DecisionLog{ - RequestContext: configv2alpha2.RequestContext{ - HTTP: configv2alpha2.HTTP{ + opaDefaultConfig: configv2alpha3.OPAConfig{ + DecisionLogs: configv2alpha3.DecisionLog{ + RequestContext: configv2alpha3.RequestContext{ + HTTP: configv2alpha3.HTTP{ Headers: strings.Split("header1,header2", ","), }, }, @@ -557,7 +557,7 @@ var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapforOCP", func() { }, }, }, - DecisionLogReporting: configv2alpha2.DecisionLogReporting{ + DecisionLogReporting: configv2alpha3.DecisionLogReporting{ UploadSizeLimitBytes: 1048576, MinDelaySeconds: 1, MaxDelaySeconds: 30, diff --git a/pkg/ocp/opaconfig.go b/pkg/ocp/opaconfig.go index 2b228691..415f7cf3 100644 --- a/pkg/ocp/opaconfig.go +++ b/pkg/ocp/opaconfig.go @@ -17,7 +17,7 @@ limitations under the License. package ocp import ( - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" ) // OPAConfig stores the information going into the ConfigMap for the OPA @@ -27,7 +27,7 @@ type OPAConfig struct { UniqueName string Namespace string BundleResource string - DecisionLogReporting configv2alpha2.DecisionLogReporting + DecisionLogReporting configv2alpha3.DecisionLogReporting } // OPAServiceConfig defines a services added to the OPAs' config files. diff --git a/pkg/ocp/sources.go b/pkg/ocp/sources.go index 1a839eec..fc1d0b8e 100644 --- a/pkg/ocp/sources.go +++ b/pkg/ocp/sources.go @@ -23,7 +23,7 @@ import ( "io" "net/http" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" "github.com/bankdata/styra-controller/pkg/httperror" "github.com/pkg/errors" ) @@ -137,7 +137,7 @@ func NewRequirement(source string, sourceType string) Requirement { } // ToRequirements converts the default requirements to a list of bundle Requirements. -func ToRequirements(sources []configv2alpha2.DefaultRequirement) []Requirement { +func ToRequirements(sources []configv2alpha3.DefaultRequirement) []Requirement { requirements := make([]Requirement, len(sources)) for i, source := range sources { requirement := NewRequirement(source.Name, "") diff --git a/pkg/s3/client.go b/pkg/s3/client.go index 4c750820..4ae3a2a6 100644 --- a/pkg/s3/client.go +++ b/pkg/s3/client.go @@ -4,11 +4,11 @@ package s3 import ( "strings" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" ) // NewClient creates a new S3Client for MinIO -func NewClient(s3Handler configv2alpha2.S3Handler) (Client, error) { +func NewClient(s3Handler configv2alpha3.S3Handler) (Client, error) { config := Config{ AccessKeyID: s3Handler.AccessKeyID, SecretAccessKey: s3Handler.SecretAccessKey, diff --git a/test/integration/controller/controller_suite_test.go b/test/integration/controller/controller_suite_test.go index 7ed9900f..f030ae75 100644 --- a/test/integration/controller/controller_suite_test.go +++ b/test/integration/controller/controller_suite_test.go @@ -35,7 +35,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" + configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" styrav1alpha1 "github.com/bankdata/styra-controller/api/styra/v1alpha1" styrav1beta1 "github.com/bankdata/styra-controller/api/styra/v1beta1" styractrls "github.com/bankdata/styra-controller/internal/controller/styra" @@ -128,9 +128,9 @@ var _ = ginkgo.BeforeSuite(func() { S3: s3ClientMock, WebhookClient: webhookMock, Recorder: k8sManager.GetEventRecorderFor("system-controller"), - Config: &configv2alpha2.ProjectConfig{ + Config: &configv2alpha3.ProjectConfig{ SystemUserRoles: []string{string(styra.RoleSystemViewer)}, - SSO: &configv2alpha2.SSOConfig{ + SSO: &configv2alpha3.SSOConfig{ IdentityProvider: "AzureAD Bankdata", JWTGroupsClaim: "groups", }, @@ -139,33 +139,33 @@ var _ = ginkgo.BeforeSuite(func() { EnableDeltaBundlesDefault: ptr.Bool(false), EnableStyraReconciliation: true, EnableOPAControlPlaneReconciliation: true, - OPAControlPlaneConfig: &configv2alpha2.OPAControlPlaneConfig{ + OPAControlPlaneConfig: &configv2alpha3.OPAControlPlaneConfig{ Address: "ocp-url", Token: "ocp-token", - GitCredentials: []*configv2alpha2.GitCredentials{&configv2alpha2.GitCredentials{ + GitCredentials: []*configv2alpha3.GitCredentials{&configv2alpha3.GitCredentials{ ID: "github-credentials", RepoPrefix: "https://github", }}, DefaultRequirements: []string{"library1"}, - BundleObjectStorage: &configv2alpha2.BundleObjectStorage{ - S3: &configv2alpha2.S3ObjectStorage{ + BundleObjectStorage: &configv2alpha3.BundleObjectStorage{ + S3: &configv2alpha3.S3ObjectStorage{ Bucket: "test-bucket", Region: "eu-west-1", URL: "s3-url", OCPConfigSecretName: "s3-credentials", }, }, - DecisionAPIConfig: &configv2alpha2.DecisionAPIConfig{ + DecisionAPIConfig: &configv2alpha3.DecisionAPIConfig{ ServiceURL: "log-api-url", - Reporting: configv2alpha2.DecisionLogReporting{ + Reporting: configv2alpha3.DecisionLogReporting{ MaxDelaySeconds: 60, MinDelaySeconds: 5, UploadSizeLimitBytes: 1024, }, }, }, - UserCredentialHandler: &configv2alpha2.UserCredentialHandler{ - S3: &configv2alpha2.S3Handler{ + UserCredentialHandler: &configv2alpha3.UserCredentialHandler{ + S3: &configv2alpha3.S3Handler{ Bucket: "test-bucket", Region: "eu-west-1", URL: "s3-url", @@ -173,8 +173,8 @@ var _ = ginkgo.BeforeSuite(func() { SecretAccessKey: "secret-access-key", }, }, - OPA: configv2alpha2.OPAConfig{ - BundleServer: &configv2alpha2.OPABundleServer{ + OPA: configv2alpha3.OPAConfig{ + BundleServer: &configv2alpha3.OPABundleServer{ URL: "https://s3-url2", Path: "/test-bucket", }, @@ -210,13 +210,13 @@ var _ = ginkgo.BeforeSuite(func() { gomega.Expect(err).NotTo(gomega.HaveOccurred()) libraryReconciler := &styractrls.LibraryReconciler{ - Config: &configv2alpha2.ProjectConfig{ - SSO: &configv2alpha2.SSOConfig{ + Config: &configv2alpha3.ProjectConfig{ + SSO: &configv2alpha3.SSOConfig{ IdentityProvider: "AzureAD Bankdata", JWTGroupsClaim: "groups", }, DatasourceIgnorePatterns: []string{"^.*/ignore$"}, - GitCredentials: []*configv2alpha2.GitCredential{ + GitCredentials: []*configv2alpha3.GitCredential{ {User: "test-user", Password: "test-secret"}, }, EnableStyraReconciliation: true, @@ -257,18 +257,18 @@ var _ = ginkgo.BeforeSuite(func() { OCP: ocpClientMock, WebhookClient: webhookMock, Recorder: k8sManagerPodRestart.GetEventRecorderFor("system-controller"), - Config: &configv2alpha2.ProjectConfig{ + Config: &configv2alpha3.ProjectConfig{ ControllerClass: "styra-controller-pod-restart", SystemUserRoles: []string{string(styra.RoleSystemViewer)}, - SSO: &configv2alpha2.SSOConfig{ + SSO: &configv2alpha3.SSOConfig{ IdentityProvider: "AzureAD Bankdata", JWTGroupsClaim: "groups", }, DatasourceIgnorePatterns: []string{"^.*/ignore$"}, ReadOnly: true, EnableDeltaBundlesDefault: ptr.Bool(false), - PodRestart: &configv2alpha2.PodRestartConfig{ - SLPRestart: &configv2alpha2.SLPRestartConfig{ + PodRestart: &configv2alpha3.PodRestartConfig{ + SLPRestart: &configv2alpha3.SLPRestartConfig{ Enabled: true, DeploymentType: "statefulset", }, From 1fdd851758b68160e306fdcaed025361c4f24bc0 Mon Sep 17 00:00:00 2001 From: jgs_bankdata Date: Fri, 6 Mar 2026 13:04:32 +0100 Subject: [PATCH 10/14] tests works --- api/config/v2alpha2/projectconfig_types.go | 10 ++- api/config/v2alpha3/projectconfig_types.go | 1 + cmd/main.go | 2 + internal/config/config_test.go | 17 ++-- .../controller/styra/system_controller.go | 12 ++- j.json | 14 +++ pkg/ocp/bundles.go | 2 +- pkg/ocp/sources.go | 17 ++-- .../controller/controller_suite_test.go | 7 +- .../controller/system_controller_test.go | 88 ++++++++++++++----- 10 files changed, 120 insertions(+), 50 deletions(-) create mode 100644 j.json diff --git a/api/config/v2alpha2/projectconfig_types.go b/api/config/v2alpha2/projectconfig_types.go index bc9d84ae..207a9039 100644 --- a/api/config/v2alpha2/projectconfig_types.go +++ b/api/config/v2alpha2/projectconfig_types.go @@ -618,7 +618,10 @@ func (c *ProjectConfig) ToV2Alpha3() *v2alpha3.ProjectConfig { } if c.OPAControlPlaneConfig.GitCredentials != nil { - v2cfg3.OPAControlPlaneConfig.GitCredentials = make([]*v2alpha3.GitCredentials, len(c.OPAControlPlaneConfig.GitCredentials)) + v2cfg3.OPAControlPlaneConfig.GitCredentials = make( + []*v2alpha3.GitCredentials, + len(c.OPAControlPlaneConfig.GitCredentials), + ) for i, c := range c.OPAControlPlaneConfig.GitCredentials { v2cfg3.OPAControlPlaneConfig.GitCredentials[i] = &v2alpha3.GitCredentials{ ID: c.ID, @@ -638,7 +641,10 @@ func (c *ProjectConfig) ToV2Alpha3() *v2alpha3.ProjectConfig { } } - v2cfg3.OPAControlPlaneConfig.DefaultRequirements = make([]v2alpha3.DefaultRequirement, len(c.OPAControlPlaneConfig.DefaultRequirements)) + v2cfg3.OPAControlPlaneConfig.DefaultRequirements = make( + []v2alpha3.DefaultRequirement, + len(c.OPAControlPlaneConfig.DefaultRequirements), + ) for i, requirement := range c.OPAControlPlaneConfig.DefaultRequirements { v2cfg3.OPAControlPlaneConfig.DefaultRequirements[i] = v2alpha3.DefaultRequirement{ diff --git a/api/config/v2alpha3/projectconfig_types.go b/api/config/v2alpha3/projectconfig_types.go index 48c9427f..d87a8e4c 100644 --- a/api/config/v2alpha3/projectconfig_types.go +++ b/api/config/v2alpha3/projectconfig_types.go @@ -227,6 +227,7 @@ type OPAControlPlaneConfig struct { DecisionAPIConfig *DecisionAPIConfig `json:"decisionAPIConfig,omitempty"` } +// DefaultRequirement defines a requirement/source that is included in all bundles type DefaultRequirement struct { Name string `json:"name"` GitSource bool `json:"gitSource,omitempty"` diff --git a/cmd/main.go b/cmd/main.go index 572da06c..bb08431d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -43,6 +43,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics" + configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2" configv2alpha3 "github.com/bankdata/styra-controller/api/config/v2alpha3" styrav1alpha1 "github.com/bankdata/styra-controller/api/styra/v1alpha1" styrav1beta1 "github.com/bankdata/styra-controller/api/styra/v1beta1" @@ -74,6 +75,7 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(styrav1alpha1.AddToScheme(scheme)) utilruntime.Must(styrav1beta1.AddToScheme(scheme)) + utilruntime.Must(configv2alpha2.AddToScheme(scheme)) utilruntime.Must(configv2alpha3.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 464d1b79..06ea4fac 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -23,12 +23,15 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/bankdata/styra-controller/api/config/v2alpha2" + "github.com/bankdata/styra-controller/api/config/v2alpha3" ) var _ = ginkgo.DescribeTable("deserialize", - func(data []byte, expected *v2alpha2.ProjectConfig, shouldErr bool) { + func(data []byte, expected *v2alpha3.ProjectConfig, shouldErr bool) { scheme := runtime.NewScheme() - err := v2alpha2.AddToScheme(scheme) + err := v2alpha3.AddToScheme(scheme) + gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) + err = v2alpha2.AddToScheme(scheme) gomega.Ω(err).ShouldNot(gomega.HaveOccurred()) actual, err := deserialize(data, scheme) if shouldErr { @@ -50,19 +53,19 @@ styra: true, ), - ginkgo.Entry("can deserialize v2alpha2", + ginkgo.Entry("can deserialize v2alpha3", []byte(` -apiVersion: config.bankdata.dk/v2alpha2 +apiVersion: config.bankdata.dk/v2alpha3 kind: ProjectConfig styra: token: my-token `), - &v2alpha2.ProjectConfig{ + &v2alpha3.ProjectConfig{ TypeMeta: metav1.TypeMeta{ Kind: "ProjectConfig", - APIVersion: v2alpha2.GroupVersion.Identifier(), + APIVersion: v2alpha3.GroupVersion.Identifier(), }, - Styra: v2alpha2.StyraConfig{ + Styra: v2alpha3.StyraConfig{ Token: "my-token", }, }, diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go index 7c19acaa..de40f9bc 100644 --- a/internal/controller/styra/system_controller.go +++ b/internal/controller/styra/system_controller.go @@ -870,10 +870,14 @@ func requirementRevisionExpression(requirement ocp.Requirement) string { ) } - return fmt.Sprintf( - "commit:{input.sources[\"%s\"].git.commit}", - requirement.Source, - ) + if requirement.RevisionCommit { + return fmt.Sprintf( + "commit:{input.sources[\"%s\"].git.commit}", + requirement.Source, + ) + } + + return "" } func (r *SystemReconciler) reconcileSystemSource( diff --git a/j.json b/j.json new file mode 100644 index 00000000..4dc67240 --- /dev/null +++ b/j.json @@ -0,0 +1,14 @@ +2026-03-06T12: 49: 58+01: 00 ERROR Observed a panic { + "controller": "styra-controller", + "controllerGroup": "styra.bankdata.dk", + "controllerKind": "System", + "System": { + "name": "ocp-system", + "namespace": "default" + }, + "namespace": "default", + "name": "ocp-system", + "reconcileID": "a73c4ae2-a0cf-4234-af4a-93c3ffdc2005", + "panic": "\n\nmock: Unexpected Method Call\n-----------------------------\n\nPutBundle(*context.valueCtx,*ocp.PutBundleRequest)\n\t\t0: &context.valueCtx{Context:(*context.valueCtx)(0x140002e4ab0), key:controller.reconcileIDKey{}, val:\"a73c4ae2-a0cf-4234-af4a-93c3ffdc2005\"}\n\t\t1: &ocp.PutBundleRequest{Name:\"default-ocp-system\", Labels:map[string]string(nil), ObjectStorage:ocp.ObjectStorage{AmazonS3:(*ocp.AmazonS3)(0x140003e4730)}, Requirements:[]ocp.Requirement{ocp.Requirement{Source:\"library1\", RevisionHash:false, RevisionCommit:true}, ocp.Requirement{Source:\"path-to-datasource\", RevisionHash:true, RevisionCommit:false}, ocp.Requirement{Source:\"default-ocp-system\", RevisionHash:false, RevisionCommit:true}}, Revision:\"$\\\"library1:commit:{input.sources[\\\"library1\\\"].git.commit},path-to-datasource:data:{input.sources[\\\"path-to-datasource\\\"].sql.hash},default-ocp-system:commit:{input.sources[\\\"default-ocp-system\\\"].git.commit}\\\"\", ExcludedFiles:[]string(nil)}\n\nThe closest call I have is: \n\nPutBundle(string,*ocp.PutBundleRequest)\n\t\t0: \"mock.Anything\"\n\t\t1: &ocp.PutBundleRequest{Name:\"default-ocp-system\", Labels:map[string]string(nil), ObjectStorage:ocp.ObjectStorage{AmazonS3:(*ocp.AmazonS3)(0x140003e0550)}, Requirements:[]ocp.Requirement{ocp.Requirement{Source:\"library1\", RevisionHash:false, RevisionCommit:false}, ocp.Requirement{Source:\"path-to-datasource\", RevisionHash:false, RevisionCommit:false}, ocp.Requirement{Source:\"default-ocp-system\", RevisionHash:false, RevisionCommit:false}}, Revision:\"$\\\"library1:,path-to-datasource:,default-ocp-system:\\\"\", ExcludedFiles:[]string(nil)}\n\nDifference found in argument 1:\n\n--- Expected\n+++ Actual\n@@ -16,3 +16,3 @@\n RevisionHash: (bool) false,\n- RevisionCommit: (bool) false\n+ RevisionCommit: (bool) true\n },\n@@ -20,3 +20,3 @@\n Source: (string) (len=18) \"path-to-datasource\",\n- RevisionHash: (bool) false,\n+ RevisionHash: (bool) true,\n RevisionCommit: (bool) false\n@@ -26,6 +26,6 @@\n RevisionHash: (bool) false,\n- RevisionCommit: (bool) false\n+ RevisionCommit: (bool) true\n }\n },\n- Revision: (string) (len=52) \"$\\\"library1:,path-to-datasource:,default-ocp-system:\\\"\",\n+ Revision: (string) (len=203) \"$\\\"library1:commit:{input.sources[\\\"library1\\\"].git.commit},path-to-datasource:data:{input.sources[\\\"path-to-datasource\\\"].sql.hash},default-ocp-system:commit:{input.sources[\\\"default-ocp-system\\\"].git.commit}\\\"\",\n ExcludedFiles: ([]string) \n\nDiff: 0: PASS: (*context.valueCtx=context.Background.WithCancel.WithValue(logr.contextKey, logr.Logger).WithValue(controller.reconcileIDKey, types.UID)) == (string=mock.Anything)\n\t1: FAIL: (*ocp.PutBundleRequest=&{default-ocp-system map[] {0x140003e4730} [{library1 false true} {path-to-datasource true false} {default-ocp-system false true}] $\"library1:commit:{input.sources[\"library1\"].git.commit},path-to-datasource:data:{input.sources[\"path-to-datasource\"].sql.hash},default-ocp-system:commit:{input.sources[\"default-ocp-system\"].git.commit}\" []}) != (*ocp.PutBundleRequest=&{default-ocp-system map[] {0x140003e0550} [{library1 false false} {path-to-datasource false false} {default-ocp-system false false}] $\"library1:,path-to-datasource:,default-ocp-system:\" []})\nat: [/Users/jgs/git/github/styra-controller/pkg/ocp/mocks/client_interface.go:85 /Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:827 /Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:423 /Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:351 /Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:132 /Users/jgs/git/github/styra-controller/internal/sentry/sentry.go:38 /Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:216 /Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:461 /Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:421 /Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:296 /opt/homebrew/Cellar/go/1.25.6/libexec/src/runtime/asm_arm64.s:1268]\n", + "stacktrace": "goroutine 418 [running]:\nk8s.io/apimachinery/pkg/util/runtime.logPanic({0x10680e598, 0x140002e4db0}, {0x10645bbc0, 0x14000417c40})\n\t/Users/jgs/go/pkg/mod/k8s.io/apimachinery@v0.35.0/pkg/util/runtime/runtime.go:132 +0x94\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Reconcile.func1()\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:198 +0xd8\npanic({0x10645bbc0?, 0x14000417c40?})\n\t/opt/homebrew/Cellar/go/1.25.6/libexec/src/runtime/panic.go:783 +0x120\ngithub.com/stretchr/testify/mock.(*Mock).fail(0x140004407d0, {0x1060c2ae2?, 0x10?}, {0x140003e47d0?, 0x2?, 0x2?})\n\t/Users/jgs/go/pkg/mod/github.com/stretchr/testify@v1.11.1/mock/mock.go:359 +0x138\ngithub.com/stretchr/testify/mock.(*Mock).MethodCalled(0x140004407d0, {0x106c1722a, 0x9}, {0x140005c4a60, 0x2, 0x2})\n\t/Users/jgs/go/pkg/mod/github.com/stretchr/testify@v1.11.1/mock/mock.go:519 +0x4cc\ngithub.com/stretchr/testify/mock.(*Mock).Called(0x140004407d0, {0x140005c4a60, 0x2, 0x2})\n\t/Users/jgs/go/pkg/mod/github.com/stretchr/testify@v1.11.1/mock/mock.go:491 +0x108\ngithub.com/bankdata/styra-controller/pkg/ocp/mocks.(*ClientInterface).PutBundle(0x140004407d0, {0x10680e598, 0x140002e4db0}, 0x14000f943c0)\n\t/Users/jgs/git/github/styra-controller/pkg/ocp/mocks/client_interface.go:85 +0xa0\ngithub.com/bankdata/styra-controller/internal/controller/styra.(*SystemReconciler).reconcileSystemBundle(0x14000542900, {0x10680e598, 0x140002e4db0}, 0x14000a2a3c0, {0x1400058e630, 0x12}, {0x14000f94360, 0x3, 0x4})\n\t/Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:827 +0x234\ngithub.com/bankdata/styra-controller/internal/controller/styra.(*SystemReconciler).ocpReconcile(0x14000542900, {0x10680e598, 0x140002e4db0}, {{0x106814460?, 0x140006a7d70?}, 0x0?}, 0x14000a2a3c0)\n\t/Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:423 +0x638\ngithub.com/bankdata/styra-controller/internal/controller/styra.(*SystemReconciler).reconcile(0x14000542900, {0x10680e598, 0x140002e4db0}, {{0x106814460?, 0x140006a7d70?}, 0x14000adc096?}, 0x14000a2a3c0)\n\t/Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:351 +0x134\ngithub.com/bankdata/styra-controller/internal/controller/styra.(*SystemReconciler).Reconcile(0x14000542900, {0x10680e598, 0x140002e4db0}, {{{0x14000adc0a0?, 0x14000f01be8?}, {0x14000adc096?, 0xa80001060298e5?}}})\n\t/Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:132 +0x544\ngithub.com/bankdata/styra-controller/internal/sentry.(*sentryReconciler).Reconcile(0x0?, {0x10680e598?, 0x140002e4db0?}, {{{0x14000adc0a0?, 0x14000f01c18?}, {0x14000adc096?, 0x14000830480?}}})\n\t/Users/jgs/git/github/styra-controller/internal/sentry/sentry.go:38 +0x48\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Reconcile(0x140002e4a20?, {0x10680e598?, 0x140002e4db0}, {{{0x14000adc0a0, 0x0?}, {0x14000adc096?, 0x0?}}})\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:216 +0xec\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler(0x1068289c0, {0x10680e5d0, 0x14000440690}, {{{0x14000adc0a0, 0x7}, {0x14000adc096, 0xa}}}, 0x0)\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:461 +0x288\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem(0x1068289c0, {0x10680e5d0, 0x14000440690})\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:421 +0x16c\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func1.1()\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:296 +0x74\ncreated by sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func1 in goroutine 270\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:292 +0x1ec\n" +} \ No newline at end of file diff --git a/pkg/ocp/bundles.go b/pkg/ocp/bundles.go index 136f6980..1ae6d8b5 100644 --- a/pkg/ocp/bundles.go +++ b/pkg/ocp/bundles.go @@ -75,7 +75,7 @@ type PutBundleRequest struct { Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` ObjectStorage ObjectStorage `json:"object_storage,omitempty" yaml:"object_storage,omitempty"` Requirements []Requirement `json:"requirements,omitempty" yaml:"requirements,omitempty"` - Revision string `json:"revision,omitempty" yaml:"revision,omitempty"` + Revision string `json:"revision,omitempty" yaml:"revision,omitempty"` ExcludedFiles []string `json:"excluded_files,omitempty" yaml:"excluded_files,omitempty"` } diff --git a/pkg/ocp/sources.go b/pkg/ocp/sources.go index fc1d0b8e..ae1fd09e 100644 --- a/pkg/ocp/sources.go +++ b/pkg/ocp/sources.go @@ -110,15 +110,9 @@ type Secret struct { // Requirement represents a requirement for a bundle and a source. type Requirement struct { - Source string `json:"source,omitempty" yaml:"source,omitempty"` - Git GitRequirement `json:"git,omitempty" yaml:"git,omitempty"` - RevisionHash bool `json:"revision_hash,omitempty" yaml:"revision_hash,omitempty"` - RevisionCommit bool `json:"revision_commit,omitempty" yaml:"revision_commit,omitempty"` -} - -// GitRequirement represents Git requirement. -type GitRequirement struct { - Commit *string `json:"commit,omitempty" yaml:"commit,omitempty"` + Source string `json:"source,omitempty" yaml:"source,omitempty"` + RevisionHash bool `json:"revision_hash,omitempty" yaml:"revision_hash,omitempty"` + RevisionCommit bool `json:"revision_commit,omitempty" yaml:"revision_commit,omitempty"` } // NewRequirement creates a new Requirement for a bundle. @@ -127,9 +121,10 @@ func NewRequirement(source string, sourceType string) Requirement { Source: source, } - if sourceType == "git" { + switch sourceType { + case "git": requirement.RevisionCommit = true - } else if sourceType == "data" { + case "data": requirement.RevisionHash = true } diff --git a/test/integration/controller/controller_suite_test.go b/test/integration/controller/controller_suite_test.go index f030ae75..0d4f49d2 100644 --- a/test/integration/controller/controller_suite_test.go +++ b/test/integration/controller/controller_suite_test.go @@ -146,7 +146,12 @@ var _ = ginkgo.BeforeSuite(func() { ID: "github-credentials", RepoPrefix: "https://github", }}, - DefaultRequirements: []string{"library1"}, + DefaultRequirements: []configv2alpha3.DefaultRequirement{ + { + Name: "library1", + GitSource: true, + }, + }, BundleObjectStorage: &configv2alpha3.BundleObjectStorage{ S3: &configv2alpha3.S3ObjectStorage{ Bucket: "test-bucket", diff --git a/test/integration/controller/system_controller_test.go b/test/integration/controller/system_controller_test.go index 282661cd..24ee226c 100644 --- a/test/integration/controller/system_controller_test.go +++ b/test/integration/controller/system_controller_test.go @@ -67,7 +67,7 @@ func expectedBundleRevision(_ string, requirements []ocp.Requirement) string { func expectedRequirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash && requirement.RevisionCommit { return fmt.Sprintf( - `commit:{input.sources["%s"].git.commit}-data:{input.sources["%s"].sql.hash}`, + "commit:{input.sources[\"%s\"].git.commit}-data:{input.sources[\"%s\"].sql.hash}", requirement.Source, requirement.Source, ) @@ -75,15 +75,19 @@ func expectedRequirementRevisionExpression(requirement ocp.Requirement) string { if requirement.RevisionHash { return fmt.Sprintf( - `data:{input.sources["%s"].sql.hash}`, + "data:{input.sources[\"%s\"].sql.hash}", requirement.Source, ) } - return fmt.Sprintf( - `commit:{input.sources["%s"].git.commit}`, - requirement.Source, - ) + if requirement.RevisionCommit { + return fmt.Sprintf( + "commit:{input.sources[\"%s\"].git.commit}", + requirement.Source, + ) + } + + return "" } var _ = ginkgo.Describe("SystemReconciler.Reconcile", ginkgo.Label("integration"), func() { @@ -2661,24 +2665,36 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int }, Requirements: []ocp.Requirement{ { - Source: "library1", + Source: "library1", + RevisionHash: false, + RevisionCommit: true, }, { - Source: "path-to-datasource", + Source: "path-to-datasource", + RevisionHash: true, + RevisionCommit: false, }, { - Source: "default-ocp-system", + Source: "default-ocp-system", + RevisionHash: false, + RevisionCommit: true, }, }, Revision: expectedBundleRevision("0123456789abcdef", []ocp.Requirement{ { - Source: "library1", + Source: "library1", + RevisionHash: false, + RevisionCommit: true, }, { - Source: "path-to-datasource", + Source: "path-to-datasource", + RevisionHash: true, + RevisionCommit: false, }, { - Source: "default-ocp-system", + Source: "default-ocp-system", + RevisionHash: false, + RevisionCommit: true, }, }), }).Return(nil).Once() @@ -2730,24 +2746,36 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int }, Requirements: []ocp.Requirement{ { - Source: "library1", + Source: "library1", + RevisionHash: false, + RevisionCommit: true, }, { - Source: "path-to-datasource", + Source: "path-to-datasource", + RevisionHash: true, + RevisionCommit: false, }, { - Source: "default-ocp-system", + Source: "default-ocp-system", + RevisionHash: false, + RevisionCommit: true, }, }, Revision: expectedBundleRevision("0123456789abcdef", []ocp.Requirement{ { - Source: "library1", + Source: "library1", + RevisionHash: false, + RevisionCommit: true, }, { - Source: "path-to-datasource", + Source: "path-to-datasource", + RevisionHash: true, + RevisionCommit: false, }, { - Source: "default-ocp-system", + Source: "default-ocp-system", + RevisionHash: false, + RevisionCommit: true, }, }), }).Return(nil).Once() @@ -2793,24 +2821,36 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int }, Requirements: []ocp.Requirement{ { - Source: "library1", + Source: "library1", + RevisionHash: false, + RevisionCommit: true, }, { - Source: "path-to-datasource", + Source: "path-to-datasource", + RevisionHash: true, + RevisionCommit: false, }, { - Source: "default-ocp-system", + Source: "default-ocp-system", + RevisionHash: false, + RevisionCommit: true, }, }, Revision: expectedBundleRevision("0123456789abcdef", []ocp.Requirement{ { - Source: "library1", + Source: "library1", + RevisionHash: false, + RevisionCommit: true, }, { - Source: "path-to-datasource", + Source: "path-to-datasource", + RevisionHash: true, + RevisionCommit: false, }, { - Source: "default-ocp-system", + Source: "default-ocp-system", + RevisionHash: false, + RevisionCommit: true, }, }), }).Return(nil).Once() From 62b36088e7ebff980330bef61067652b3e43da46 Mon Sep 17 00:00:00 2001 From: jgs_bankdata Date: Fri, 6 Mar 2026 13:45:40 +0100 Subject: [PATCH 11/14] forgot to cp tokensecretpath --- api/config/v2alpha2/projectconfig_types.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/config/v2alpha2/projectconfig_types.go b/api/config/v2alpha2/projectconfig_types.go index 207a9039..c838acc4 100644 --- a/api/config/v2alpha2/projectconfig_types.go +++ b/api/config/v2alpha2/projectconfig_types.go @@ -462,8 +462,9 @@ func (c *ProjectConfig) ToV2Alpha3() *v2alpha3.ProjectConfig { DatasourceIgnorePatterns: c.DatasourceIgnorePatterns, LogLevel: c.LogLevel, Styra: v2alpha3.StyraConfig{ - Token: c.Styra.Token, - Address: c.Styra.Address, + Token: c.Styra.Token, + Address: c.Styra.Address, + TokenSecretPath: c.Styra.TokenSecretPath, }, SystemPrefix: c.SystemPrefix, SystemSuffix: c.SystemSuffix, From f0877925e0e92b8ce51269c76f67f9ebb330fedd Mon Sep 17 00:00:00 2001 From: jgs_bankdata Date: Fri, 6 Mar 2026 13:56:36 +0100 Subject: [PATCH 12/14] dobble check config converter --- api/config/v2alpha2/projectconfig_types.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/config/v2alpha2/projectconfig_types.go b/api/config/v2alpha2/projectconfig_types.go index c838acc4..7ba368f1 100644 --- a/api/config/v2alpha2/projectconfig_types.go +++ b/api/config/v2alpha2/projectconfig_types.go @@ -646,7 +646,6 @@ func (c *ProjectConfig) ToV2Alpha3() *v2alpha3.ProjectConfig { []v2alpha3.DefaultRequirement, len(c.OPAControlPlaneConfig.DefaultRequirements), ) - for i, requirement := range c.OPAControlPlaneConfig.DefaultRequirements { v2cfg3.OPAControlPlaneConfig.DefaultRequirements[i] = v2alpha3.DefaultRequirement{ Name: requirement, @@ -679,8 +678,6 @@ func (c *ProjectConfig) ToV2Alpha3() *v2alpha3.ProjectConfig { } } - v2cfg3.DecisionsExporter = &v2alpha3.ExporterConfig{} - return v2cfg3 } From 10d66458ef62d4a99b543a285d3044ea8c6ab237 Mon Sep 17 00:00:00 2001 From: jgs_bankdata Date: Fri, 6 Mar 2026 13:57:09 +0100 Subject: [PATCH 13/14] delete upss file --- j.json | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 j.json diff --git a/j.json b/j.json deleted file mode 100644 index 4dc67240..00000000 --- a/j.json +++ /dev/null @@ -1,14 +0,0 @@ -2026-03-06T12: 49: 58+01: 00 ERROR Observed a panic { - "controller": "styra-controller", - "controllerGroup": "styra.bankdata.dk", - "controllerKind": "System", - "System": { - "name": "ocp-system", - "namespace": "default" - }, - "namespace": "default", - "name": "ocp-system", - "reconcileID": "a73c4ae2-a0cf-4234-af4a-93c3ffdc2005", - "panic": "\n\nmock: Unexpected Method Call\n-----------------------------\n\nPutBundle(*context.valueCtx,*ocp.PutBundleRequest)\n\t\t0: &context.valueCtx{Context:(*context.valueCtx)(0x140002e4ab0), key:controller.reconcileIDKey{}, val:\"a73c4ae2-a0cf-4234-af4a-93c3ffdc2005\"}\n\t\t1: &ocp.PutBundleRequest{Name:\"default-ocp-system\", Labels:map[string]string(nil), ObjectStorage:ocp.ObjectStorage{AmazonS3:(*ocp.AmazonS3)(0x140003e4730)}, Requirements:[]ocp.Requirement{ocp.Requirement{Source:\"library1\", RevisionHash:false, RevisionCommit:true}, ocp.Requirement{Source:\"path-to-datasource\", RevisionHash:true, RevisionCommit:false}, ocp.Requirement{Source:\"default-ocp-system\", RevisionHash:false, RevisionCommit:true}}, Revision:\"$\\\"library1:commit:{input.sources[\\\"library1\\\"].git.commit},path-to-datasource:data:{input.sources[\\\"path-to-datasource\\\"].sql.hash},default-ocp-system:commit:{input.sources[\\\"default-ocp-system\\\"].git.commit}\\\"\", ExcludedFiles:[]string(nil)}\n\nThe closest call I have is: \n\nPutBundle(string,*ocp.PutBundleRequest)\n\t\t0: \"mock.Anything\"\n\t\t1: &ocp.PutBundleRequest{Name:\"default-ocp-system\", Labels:map[string]string(nil), ObjectStorage:ocp.ObjectStorage{AmazonS3:(*ocp.AmazonS3)(0x140003e0550)}, Requirements:[]ocp.Requirement{ocp.Requirement{Source:\"library1\", RevisionHash:false, RevisionCommit:false}, ocp.Requirement{Source:\"path-to-datasource\", RevisionHash:false, RevisionCommit:false}, ocp.Requirement{Source:\"default-ocp-system\", RevisionHash:false, RevisionCommit:false}}, Revision:\"$\\\"library1:,path-to-datasource:,default-ocp-system:\\\"\", ExcludedFiles:[]string(nil)}\n\nDifference found in argument 1:\n\n--- Expected\n+++ Actual\n@@ -16,3 +16,3 @@\n RevisionHash: (bool) false,\n- RevisionCommit: (bool) false\n+ RevisionCommit: (bool) true\n },\n@@ -20,3 +20,3 @@\n Source: (string) (len=18) \"path-to-datasource\",\n- RevisionHash: (bool) false,\n+ RevisionHash: (bool) true,\n RevisionCommit: (bool) false\n@@ -26,6 +26,6 @@\n RevisionHash: (bool) false,\n- RevisionCommit: (bool) false\n+ RevisionCommit: (bool) true\n }\n },\n- Revision: (string) (len=52) \"$\\\"library1:,path-to-datasource:,default-ocp-system:\\\"\",\n+ Revision: (string) (len=203) \"$\\\"library1:commit:{input.sources[\\\"library1\\\"].git.commit},path-to-datasource:data:{input.sources[\\\"path-to-datasource\\\"].sql.hash},default-ocp-system:commit:{input.sources[\\\"default-ocp-system\\\"].git.commit}\\\"\",\n ExcludedFiles: ([]string) \n\nDiff: 0: PASS: (*context.valueCtx=context.Background.WithCancel.WithValue(logr.contextKey, logr.Logger).WithValue(controller.reconcileIDKey, types.UID)) == (string=mock.Anything)\n\t1: FAIL: (*ocp.PutBundleRequest=&{default-ocp-system map[] {0x140003e4730} [{library1 false true} {path-to-datasource true false} {default-ocp-system false true}] $\"library1:commit:{input.sources[\"library1\"].git.commit},path-to-datasource:data:{input.sources[\"path-to-datasource\"].sql.hash},default-ocp-system:commit:{input.sources[\"default-ocp-system\"].git.commit}\" []}) != (*ocp.PutBundleRequest=&{default-ocp-system map[] {0x140003e0550} [{library1 false false} {path-to-datasource false false} {default-ocp-system false false}] $\"library1:,path-to-datasource:,default-ocp-system:\" []})\nat: [/Users/jgs/git/github/styra-controller/pkg/ocp/mocks/client_interface.go:85 /Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:827 /Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:423 /Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:351 /Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:132 /Users/jgs/git/github/styra-controller/internal/sentry/sentry.go:38 /Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:216 /Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:461 /Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:421 /Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:296 /opt/homebrew/Cellar/go/1.25.6/libexec/src/runtime/asm_arm64.s:1268]\n", - "stacktrace": "goroutine 418 [running]:\nk8s.io/apimachinery/pkg/util/runtime.logPanic({0x10680e598, 0x140002e4db0}, {0x10645bbc0, 0x14000417c40})\n\t/Users/jgs/go/pkg/mod/k8s.io/apimachinery@v0.35.0/pkg/util/runtime/runtime.go:132 +0x94\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Reconcile.func1()\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:198 +0xd8\npanic({0x10645bbc0?, 0x14000417c40?})\n\t/opt/homebrew/Cellar/go/1.25.6/libexec/src/runtime/panic.go:783 +0x120\ngithub.com/stretchr/testify/mock.(*Mock).fail(0x140004407d0, {0x1060c2ae2?, 0x10?}, {0x140003e47d0?, 0x2?, 0x2?})\n\t/Users/jgs/go/pkg/mod/github.com/stretchr/testify@v1.11.1/mock/mock.go:359 +0x138\ngithub.com/stretchr/testify/mock.(*Mock).MethodCalled(0x140004407d0, {0x106c1722a, 0x9}, {0x140005c4a60, 0x2, 0x2})\n\t/Users/jgs/go/pkg/mod/github.com/stretchr/testify@v1.11.1/mock/mock.go:519 +0x4cc\ngithub.com/stretchr/testify/mock.(*Mock).Called(0x140004407d0, {0x140005c4a60, 0x2, 0x2})\n\t/Users/jgs/go/pkg/mod/github.com/stretchr/testify@v1.11.1/mock/mock.go:491 +0x108\ngithub.com/bankdata/styra-controller/pkg/ocp/mocks.(*ClientInterface).PutBundle(0x140004407d0, {0x10680e598, 0x140002e4db0}, 0x14000f943c0)\n\t/Users/jgs/git/github/styra-controller/pkg/ocp/mocks/client_interface.go:85 +0xa0\ngithub.com/bankdata/styra-controller/internal/controller/styra.(*SystemReconciler).reconcileSystemBundle(0x14000542900, {0x10680e598, 0x140002e4db0}, 0x14000a2a3c0, {0x1400058e630, 0x12}, {0x14000f94360, 0x3, 0x4})\n\t/Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:827 +0x234\ngithub.com/bankdata/styra-controller/internal/controller/styra.(*SystemReconciler).ocpReconcile(0x14000542900, {0x10680e598, 0x140002e4db0}, {{0x106814460?, 0x140006a7d70?}, 0x0?}, 0x14000a2a3c0)\n\t/Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:423 +0x638\ngithub.com/bankdata/styra-controller/internal/controller/styra.(*SystemReconciler).reconcile(0x14000542900, {0x10680e598, 0x140002e4db0}, {{0x106814460?, 0x140006a7d70?}, 0x14000adc096?}, 0x14000a2a3c0)\n\t/Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:351 +0x134\ngithub.com/bankdata/styra-controller/internal/controller/styra.(*SystemReconciler).Reconcile(0x14000542900, {0x10680e598, 0x140002e4db0}, {{{0x14000adc0a0?, 0x14000f01be8?}, {0x14000adc096?, 0xa80001060298e5?}}})\n\t/Users/jgs/git/github/styra-controller/internal/controller/styra/system_controller.go:132 +0x544\ngithub.com/bankdata/styra-controller/internal/sentry.(*sentryReconciler).Reconcile(0x0?, {0x10680e598?, 0x140002e4db0?}, {{{0x14000adc0a0?, 0x14000f01c18?}, {0x14000adc096?, 0x14000830480?}}})\n\t/Users/jgs/git/github/styra-controller/internal/sentry/sentry.go:38 +0x48\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Reconcile(0x140002e4a20?, {0x10680e598?, 0x140002e4db0}, {{{0x14000adc0a0, 0x0?}, {0x14000adc096?, 0x0?}}})\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:216 +0xec\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).reconcileHandler(0x1068289c0, {0x10680e5d0, 0x14000440690}, {{{0x14000adc0a0, 0x7}, {0x14000adc096, 0xa}}}, 0x0)\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:461 +0x288\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).processNextWorkItem(0x1068289c0, {0x10680e5d0, 0x14000440690})\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:421 +0x16c\nsigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func1.1()\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:296 +0x74\ncreated by sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller[...]).Start.func1 in goroutine 270\n\t/Users/jgs/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.22.4/pkg/internal/controller/controller.go:292 +0x1ec\n" -} \ No newline at end of file From f96acc784c41331fc13fc8f29ac271791ca2eefe Mon Sep 17 00:00:00 2001 From: jgs_bankdata Date: Fri, 6 Mar 2026 15:02:39 +0100 Subject: [PATCH 14/14] made a requirementstype to avoid literal strings --- api/config/v2alpha2/projectconfig_types.go | 5 +- api/config/v2alpha3/projectconfig_types.go | 22 +++- config/default/config.yaml | 4 +- .../controller/styra/system_controller.go | 10 +- .../styra/system_controller_test.go | 15 ++- pkg/ocp/sources.go | 46 ++++---- .../controller/controller_suite_test.go | 4 +- .../controller/system_controller_test.go | 104 ++++++++---------- 8 files changed, 108 insertions(+), 102 deletions(-) diff --git a/api/config/v2alpha2/projectconfig_types.go b/api/config/v2alpha2/projectconfig_types.go index 7ba368f1..e4bb326f 100644 --- a/api/config/v2alpha2/projectconfig_types.go +++ b/api/config/v2alpha2/projectconfig_types.go @@ -648,9 +648,8 @@ func (c *ProjectConfig) ToV2Alpha3() *v2alpha3.ProjectConfig { ) for i, requirement := range c.OPAControlPlaneConfig.DefaultRequirements { v2cfg3.OPAControlPlaneConfig.DefaultRequirements[i] = v2alpha3.DefaultRequirement{ - Name: requirement, - GitSource: false, - DataSource: false, + Name: requirement, + RequirementType: v2alpha3.RequirementTypeUnknown, } } diff --git a/api/config/v2alpha3/projectconfig_types.go b/api/config/v2alpha3/projectconfig_types.go index d87a8e4c..68a31c65 100644 --- a/api/config/v2alpha3/projectconfig_types.go +++ b/api/config/v2alpha3/projectconfig_types.go @@ -229,11 +229,27 @@ type OPAControlPlaneConfig struct { // DefaultRequirement defines a requirement/source that is included in all bundles type DefaultRequirement struct { - Name string `json:"name"` - GitSource bool `json:"gitSource,omitempty"` - DataSource bool `json:"dataSource,omitempty"` + Name string `json:"name"` + RequirementType RequirementType `json:"requirementType,omitempty"` } +// RequirementType defines the different types of requirements +type RequirementType string + +const ( + // RequirementTypeGit means that the requirement is a git source + RequirementTypeGit RequirementType = "git" + + // RequirementTypeData means that the requirement is a data source + RequirementTypeData RequirementType = "data" + + // RequirementTypeGitAndData means that the requirement is both a git and a data source + RequirementTypeGitAndData RequirementType = "git_and_data" + + // RequirementTypeUnknown means that the requirement type is unknown + RequirementTypeUnknown RequirementType = "unknown" +) + // UserCredentialHandler defines the structure of possible user credential handlers type UserCredentialHandler struct { S3 *S3Handler `json:"s3,omitempty" yaml:"s3,omitempty"` diff --git a/config/default/config.yaml b/config/default/config.yaml index df9caa67..42648226 100644 --- a/config/default/config.yaml +++ b/config/default/config.yaml @@ -35,10 +35,8 @@ opaControlPlaneConfig: url: https://minio-host ocpConfigSecretName: minio # Name of secret in ocp config defaultRequirements: - - library1 - defaultRequirementsWithRevision: - name: library2 - gitSource: true + type: git # used by controller to create users and policies userCredentialHandler: diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go index de40f9bc..f372b336 100644 --- a/internal/controller/styra/system_controller.go +++ b/internal/controller/styra/system_controller.go @@ -400,7 +400,7 @@ func (r *SystemReconciler) ocpReconcile( } } - requirements = append(requirements, ocp.NewRequirement(datasource.Path, "data")) + requirements = append(requirements, ocp.NewRequirement(datasource.Path, ocp.RequirementTypeData)) } system.SetCondition(v1beta1.ConditionTypeRequirementsUpdated, metav1.ConditionTrue) @@ -416,7 +416,7 @@ func (r *SystemReconciler) ocpReconcile( WithEvent(v1beta1.EventErrorUpdateSource). WithSystemCondition(v1beta1.ConditionTypeSystemSourceUpdated) } - requirements = append(requirements, ocp.NewRequirement(uniqueName, "git")) + requirements = append(requirements, ocp.NewRequirement(uniqueName, ocp.RequirementTypeGit)) system.SetCondition(v1beta1.ConditionTypeSystemSourceUpdated, metav1.ConditionTrue) reconcileSystemBundleStart := time.Now() @@ -855,7 +855,7 @@ func bundleRevision(system *v1beta1.System, requirements []ocp.Requirement) stri } func requirementRevisionExpression(requirement ocp.Requirement) string { - if requirement.RevisionHash && requirement.RevisionCommit { + if requirement.RequirementType == ocp.RequirementTypeGitAndData { return fmt.Sprintf( "commit:{input.sources[\"%s\"].git.commit}-data:{input.sources[\"%s\"].sql.hash}", requirement.Source, @@ -863,14 +863,14 @@ func requirementRevisionExpression(requirement ocp.Requirement) string { ) } - if requirement.RevisionHash { + if requirement.RequirementType == ocp.RequirementTypeData { return fmt.Sprintf( "data:{input.sources[\"%s\"].sql.hash}", requirement.Source, ) } - if requirement.RevisionCommit { + if requirement.RequirementType == ocp.RequirementTypeGit { return fmt.Sprintf( "commit:{input.sources[\"%s\"].git.commit}", requirement.Source, diff --git a/internal/controller/styra/system_controller_test.go b/internal/controller/styra/system_controller_test.go index 78b9dfad..9f79dd15 100644 --- a/internal/controller/styra/system_controller_test.go +++ b/internal/controller/styra/system_controller_test.go @@ -137,13 +137,22 @@ var _ = ginkgo.DescribeTable("requirementRevisionExpression", func(requirement ocp.Requirement, expected string) { gomega.Ω(requirementRevisionExpression(requirement)).To(gomega.Equal(expected)) }, - ginkgo.Entry("git revision", ocp.Requirement{Source: "git", RevisionCommit: true}, + ginkgo.Entry("git revision", ocp.Requirement{ + Source: "git", + RequirementType: ocp.RequirementTypeGit, + }, `commit:{input.sources["git"].git.commit}`, ), - ginkgo.Entry("data revision", ocp.Requirement{Source: "data", RevisionHash: true}, + ginkgo.Entry("data revision", ocp.Requirement{ + Source: "data", + RequirementType: ocp.RequirementTypeData, + }, `data:{input.sources["data"].sql.hash}`, ), - ginkgo.Entry("git and data revision", ocp.Requirement{Source: "policy", RevisionCommit: true, RevisionHash: true}, + ginkgo.Entry("git and data revision", ocp.Requirement{ + Source: "policy", + RequirementType: ocp.RequirementTypeGitAndData, + }, `commit:{input.sources["policy"].git.commit}-data:{input.sources["policy"].sql.hash}`, ), ) diff --git a/pkg/ocp/sources.go b/pkg/ocp/sources.go index ae1fd09e..40e92c4f 100644 --- a/pkg/ocp/sources.go +++ b/pkg/ocp/sources.go @@ -110,38 +110,40 @@ type Secret struct { // Requirement represents a requirement for a bundle and a source. type Requirement struct { - Source string `json:"source,omitempty" yaml:"source,omitempty"` - RevisionHash bool `json:"revision_hash,omitempty" yaml:"revision_hash,omitempty"` - RevisionCommit bool `json:"revision_commit,omitempty" yaml:"revision_commit,omitempty"` + Source string + RequirementType RequirementType } -// NewRequirement creates a new Requirement for a bundle. -func NewRequirement(source string, sourceType string) Requirement { - requirement := Requirement{ - Source: source, - } +// RequirementType defines the different types of requirements +type RequirementType string - switch sourceType { - case "git": - requirement.RevisionCommit = true - case "data": - requirement.RevisionHash = true - } +const ( + // RequirementTypeGit means that the requirement is a git source + RequirementTypeGit RequirementType = "git" + + // RequirementTypeData means that the requirement is a data source + RequirementTypeData RequirementType = "data" + + // RequirementTypeGitAndData means that the requirement is both a git and a data source + RequirementTypeGitAndData RequirementType = "git_and_data" - return requirement + // RequirementTypeUnknown means that the requirement type is unknown + RequirementTypeUnknown RequirementType = "unknown" +) + +// NewRequirement creates a new Requirement for a bundle. +func NewRequirement(source string, sourceType RequirementType) Requirement { + return Requirement{ + Source: source, + RequirementType: sourceType, + } } // ToRequirements converts the default requirements to a list of bundle Requirements. func ToRequirements(sources []configv2alpha3.DefaultRequirement) []Requirement { requirements := make([]Requirement, len(sources)) for i, source := range sources { - requirement := NewRequirement(source.Name, "") - if source.GitSource { - requirement.RevisionCommit = true - } - if source.DataSource { - requirement.RevisionHash = true - } + requirement := NewRequirement(source.Name, RequirementType(source.RequirementType)) requirements[i] = requirement } return requirements diff --git a/test/integration/controller/controller_suite_test.go b/test/integration/controller/controller_suite_test.go index 0d4f49d2..89595987 100644 --- a/test/integration/controller/controller_suite_test.go +++ b/test/integration/controller/controller_suite_test.go @@ -148,8 +148,8 @@ var _ = ginkgo.BeforeSuite(func() { }}, DefaultRequirements: []configv2alpha3.DefaultRequirement{ { - Name: "library1", - GitSource: true, + Name: "library1", + RequirementType: configv2alpha3.RequirementTypeGitAndData, }, }, BundleObjectStorage: &configv2alpha3.BundleObjectStorage{ diff --git a/test/integration/controller/system_controller_test.go b/test/integration/controller/system_controller_test.go index 24ee226c..a30dc109 100644 --- a/test/integration/controller/system_controller_test.go +++ b/test/integration/controller/system_controller_test.go @@ -45,7 +45,7 @@ import ( "github.com/bankdata/styra-controller/pkg/styra" ) -func expectedBundleRevision(_ string, requirements []ocp.Requirement) string { +func expectedBundleRevision(requirements []ocp.Requirement) string { if len(requirements) == 0 { return "" } @@ -65,7 +65,7 @@ func expectedBundleRevision(_ string, requirements []ocp.Requirement) string { } func expectedRequirementRevisionExpression(requirement ocp.Requirement) string { - if requirement.RevisionHash && requirement.RevisionCommit { + if requirement.RequirementType == ocp.RequirementTypeGitAndData { return fmt.Sprintf( "commit:{input.sources[\"%s\"].git.commit}-data:{input.sources[\"%s\"].sql.hash}", requirement.Source, @@ -73,14 +73,14 @@ func expectedRequirementRevisionExpression(requirement ocp.Requirement) string { ) } - if requirement.RevisionHash { + if requirement.RequirementType == ocp.RequirementTypeData { return fmt.Sprintf( "data:{input.sources[\"%s\"].sql.hash}", requirement.Source, ) } - if requirement.RevisionCommit { + if requirement.RequirementType == ocp.RequirementTypeGit { return fmt.Sprintf( "commit:{input.sources[\"%s\"].git.commit}", requirement.Source, @@ -2665,36 +2665,30 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int }, Requirements: []ocp.Requirement{ { - Source: "library1", - RevisionHash: false, - RevisionCommit: true, + Source: "library1", + RequirementType: ocp.RequirementTypeGitAndData, }, { - Source: "path-to-datasource", - RevisionHash: true, - RevisionCommit: false, + Source: "path-to-datasource", + RequirementType: ocp.RequirementTypeData, }, { - Source: "default-ocp-system", - RevisionHash: false, - RevisionCommit: true, + Source: "default-ocp-system", + RequirementType: ocp.RequirementTypeGit, }, }, - Revision: expectedBundleRevision("0123456789abcdef", []ocp.Requirement{ + Revision: expectedBundleRevision([]ocp.Requirement{ { - Source: "library1", - RevisionHash: false, - RevisionCommit: true, + Source: "library1", + RequirementType: ocp.RequirementTypeGitAndData, }, { - Source: "path-to-datasource", - RevisionHash: true, - RevisionCommit: false, + Source: "path-to-datasource", + RequirementType: ocp.RequirementTypeData, }, { - Source: "default-ocp-system", - RevisionHash: false, - RevisionCommit: true, + Source: "default-ocp-system", + RequirementType: ocp.RequirementTypeGit, }, }), }).Return(nil).Once() @@ -2746,36 +2740,30 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int }, Requirements: []ocp.Requirement{ { - Source: "library1", - RevisionHash: false, - RevisionCommit: true, + Source: "library1", + RequirementType: ocp.RequirementTypeGitAndData, }, { - Source: "path-to-datasource", - RevisionHash: true, - RevisionCommit: false, + Source: "path-to-datasource", + RequirementType: ocp.RequirementTypeData, }, { - Source: "default-ocp-system", - RevisionHash: false, - RevisionCommit: true, + Source: "default-ocp-system", + RequirementType: ocp.RequirementTypeGit, }, }, - Revision: expectedBundleRevision("0123456789abcdef", []ocp.Requirement{ + Revision: expectedBundleRevision([]ocp.Requirement{ { - Source: "library1", - RevisionHash: false, - RevisionCommit: true, + Source: "library1", + RequirementType: ocp.RequirementTypeGitAndData, }, { - Source: "path-to-datasource", - RevisionHash: true, - RevisionCommit: false, + Source: "path-to-datasource", + RequirementType: ocp.RequirementTypeData, }, { - Source: "default-ocp-system", - RevisionHash: false, - RevisionCommit: true, + Source: "default-ocp-system", + RequirementType: ocp.RequirementTypeGit, }, }), }).Return(nil).Once() @@ -2821,36 +2809,30 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int }, Requirements: []ocp.Requirement{ { - Source: "library1", - RevisionHash: false, - RevisionCommit: true, + Source: "library1", + RequirementType: ocp.RequirementTypeGitAndData, }, { - Source: "path-to-datasource", - RevisionHash: true, - RevisionCommit: false, + Source: "path-to-datasource", + RequirementType: ocp.RequirementTypeData, }, { - Source: "default-ocp-system", - RevisionHash: false, - RevisionCommit: true, + Source: "default-ocp-system", + RequirementType: ocp.RequirementTypeGit, }, }, - Revision: expectedBundleRevision("0123456789abcdef", []ocp.Requirement{ + Revision: expectedBundleRevision([]ocp.Requirement{ { - Source: "library1", - RevisionHash: false, - RevisionCommit: true, + Source: "library1", + RequirementType: ocp.RequirementTypeGitAndData, }, { - Source: "path-to-datasource", - RevisionHash: true, - RevisionCommit: false, + Source: "path-to-datasource", + RequirementType: ocp.RequirementTypeData, }, { - Source: "default-ocp-system", - RevisionHash: false, - RevisionCommit: true, + Source: "default-ocp-system", + RequirementType: ocp.RequirementTypeGit, }, }), }).Return(nil).Once()