From eaf2f443ea8205b032015d13dbd0cfb9f85e4aa7 Mon Sep 17 00:00:00 2001
From: bdumpp
Date: Fri, 19 Dec 2025 14:13:44 +0100
Subject: [PATCH] add support for setting different decision log api
---
api/config/v2alpha2/projectconfig_types.go | 61 +++++--
api/config/v2alpha2/zz_generated.deepcopy.go | 109 ++++++++++++
config/default/config.yaml | 41 ++++-
docs/apis/styra/v1alpha1.md | 2 +-
docs/apis/styra/v1beta1.md | 21 ++-
.../controller/styra/system_controller.go | 40 +++--
internal/k8sconv/k8sconv.go | 166 ++++++++++++------
internal/k8sconv/k8sconv_test.go | 126 ++++++++++---
pkg/ocp/opaconfig.go | 45 ++++-
.../controller/controller_suite_test.go | 72 ++------
.../controller/system_controller_test.go | 132 ++++++++++----
11 files changed, 599 insertions(+), 216 deletions(-)
diff --git a/api/config/v2alpha2/projectconfig_types.go b/api/config/v2alpha2/projectconfig_types.go
index 261ed6d9..260b0897 100644
--- a/api/config/v2alpha2/projectconfig_types.go
+++ b/api/config/v2alpha2/projectconfig_types.go
@@ -222,25 +222,28 @@ type OPAControlPlaneConfig struct {
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"`
}
// UserCredentialHandler defines the structure of possible user credential handlers
type UserCredentialHandler struct {
- S3 *S3Handler `json:"s3,omitempty"`
+ S3 *S3Handler `json:"s3,omitempty" yaml:"s3,omitempty"`
}
// S3Handler defines the structure for S3 handler configuration.
type S3Handler struct {
- Bucket string `json:"bucket"`
- URL string `json:"url"`
- Region string `json:"region"`
- AccessKeyID string `json:"accessKeyID"`
- SecretAccessKey string `json:"secretAccessKey"`
+ 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"`
+ S3 *S3ObjectStorage `json:"s3,omitempty" yaml:"s3,omitempty"`
}
// S3ObjectStorage defines the structure for S3 object storage configuration.
@@ -263,14 +266,50 @@ type GitCredentials struct {
// OPAConfig contains default configuration for the opa config generated by the styra-controller
type OPAConfig struct {
- DecisionLogs DecisionLog `json:"decision_logs"`
- PersistBundle bool `json:"persist_bundle,omitempty"`
- PersistBundleDirectory string `json:"persist_bundle_directory,omitempty"`
+ 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:"request_context"`
+ 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
diff --git a/api/config/v2alpha2/zz_generated.deepcopy.go b/api/config/v2alpha2/zz_generated.deepcopy.go
index 36f45098..eef69be5 100644
--- a/api/config/v2alpha2/zz_generated.deepcopy.go
+++ b/api/config/v2alpha2/zz_generated.deepcopy.go
@@ -44,6 +44,22 @@ func (in *BundleObjectStorage) DeepCopy() *BundleObjectStorage {
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
@@ -60,6 +76,21 @@ func (in *DecisionLog) DeepCopy() *DecisionLog {
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 *ExporterConfig) DeepCopyInto(out *ExporterConfig) {
*out = *in
@@ -130,6 +161,26 @@ func (in *HTTP) DeepCopy() *HTTP {
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
@@ -173,6 +224,22 @@ func (in *LeaderElectionConfig) DeepCopy() *LeaderElectionConfig {
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
@@ -188,10 +255,31 @@ func (in *NotificationWebhooksConfig) DeepCopy() *NotificationWebhooksConfig {
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.
@@ -228,6 +316,11 @@ func (in *OPAControlPlaneConfig) DeepCopyInto(out *OPAControlPlaneConfig) {
*out = make([]string, 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.
@@ -377,6 +470,22 @@ func (in *ProjectConfig) DeepCopyObject() runtime.Object {
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
diff --git a/config/default/config.yaml b/config/default/config.yaml
index 4128c1bd..efc6d78b 100644
--- a/config/default/config.yaml
+++ b/config/default/config.yaml
@@ -58,6 +58,44 @@ systemUserRoles:
#decisionsExporter:
+opa:
+ bundleServer:
+ url: https://minio-host
+ path: /ocp
+ metrics:
+ prometheus:
+ http:
+ buckets:
+ - 0.0005
+ - 0.001
+ - 0.002
+ - 0.003
+ - 0.004
+ - 0.005
+ - 0.006
+ - 0.007
+ - 0.008
+ - 0.009
+ - 0.01
+ - 0.02
+ - 0.03
+ - 0.04
+ - 0.05
+ - 0.06
+ - 0.07
+ - 0.08
+ - 0.09
+ - 0.1
+ - 0.2
+ - 0.3
+ - 0.4
+ - 0.5
+ - 0.6
+ - 0.7
+ - 0.8
+ - 0.9
+ - 1
+
#activityExporter:
podRestart:
@@ -65,8 +103,7 @@ podRestart:
enabled: true
deploymentType: StatefulSet
-
-#opa:
+# opa:
# decision_logs:
# request_context:
# http:
diff --git a/docs/apis/styra/v1alpha1.md b/docs/apis/styra/v1alpha1.md
index 510e5a0e..1f0e8fb7 100644
--- a/docs/apis/styra/v1alpha1.md
+++ b/docs/apis/styra/v1alpha1.md
@@ -458,5 +458,5 @@ GitRepo
Generated with gen-crd-api-reference-docs
-on git commit b550add7.
+on git commit 4901ef1.
diff --git a/docs/apis/styra/v1beta1.md b/docs/apis/styra/v1beta1.md
index 5c43bb9b..84a95cd0 100644
--- a/docs/apis/styra/v1beta1.md
+++ b/docs/apis/styra/v1beta1.md
@@ -246,7 +246,7 @@ the configuration of the System are updated in Styra.
"ErrorUpdateStatus" |
EventErrorUpdateStatus is an EventType used when the controller fails to update
the status of the System resource.
@@ -1407,5 +1424,5 @@ System.
Generated with gen-crd-api-reference-docs
-on git commit b550add7.
+on git commit 4901ef1.
diff --git a/internal/controller/styra/system_controller.go b/internal/controller/styra/system_controller.go
index d65791e0..4950f1e2 100644
--- a/internal/controller/styra/system_controller.go
+++ b/internal/controller/styra/system_controller.go
@@ -115,6 +115,7 @@ func (r *SystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
log = log.WithValues("systemID", system.Status.ID)
log = log.WithValues("controlPlane", system.Labels["styra-controller/control-plane"])
+ log = log.WithValues("uniqueName", system.OCPUniqueName(r.Config.SystemPrefix, r.Config.SystemSuffix))
if !labels.ControllerClassMatches(&system, r.Config.ControllerClass) {
log.Info("This is not a System we are managing. Skipping reconciliation.")
@@ -524,17 +525,31 @@ func (r *SystemReconciler) reconcileOPAConfigMapForOCP(
}
opaconf := ocp.OPAConfig{
- BundleResource: fmt.Sprintf("bundles/%s/bundle.tar.gz", uniqueName),
- BundleService: "s3",
- ServiceURL: fmt.Sprintf("%s/%s",
- r.Config.OPAControlPlaneConfig.BundleObjectStorage.S3.URL,
- r.Config.OPAControlPlaneConfig.BundleObjectStorage.S3.Bucket),
- ServiceName: "s3",
- UniqueName: uniqueName,
- Namespace: system.Namespace,
+ BundleService: &ocp.OPAServiceConfig{
+ Name: "s3",
+ URL: path.Join(r.Config.OPA.BundleServer.URL, r.Config.OPA.BundleServer.Path),
+ Credentials: &ocp.ServiceCredentials{
+ S3: &ocp.S3Signing{
+ S3EnvironmentCredentials: map[string]ocp.EmptyStruct{},
+ },
+ },
+ },
+ LogService: &ocp.OPAServiceConfig{
+ Name: "logs",
+ URL: r.Config.OPAControlPlaneConfig.DecisionAPIConfig.ServiceURL,
+ Credentials: &ocp.ServiceCredentials{
+ Bearer: &ocp.Bearer{
+ TokenPath: "/run/secrets/kubernetes.io/serviceaccount/token",
+ },
+ },
+ },
+ DecisionLogReporting: r.Config.OPAControlPlaneConfig.DecisionAPIConfig.Reporting,
+ BundleResource: fmt.Sprintf("bundles/%s/bundle.tar.gz", uniqueName),
+ UniqueName: uniqueName,
+ Namespace: system.Namespace,
}
- expectedOPAConfigMap, err = k8sconv.OpaConfToK8sOPAConfigMapforOCP(opaconf, r.Config.OPA, customConfig)
+ expectedOPAConfigMap, err = k8sconv.OPAConfToK8sOPAConfigMapforOCP(opaconf, r.Config.OPA, customConfig, log)
if err != nil {
return ctrl.Result{}, false, ctrlerr.Wrap(err, "Could not convert OPA conf to ConfigMap").
WithEvent(v1beta1.EventErrorConvertOPAConf).
@@ -906,7 +921,6 @@ func (r *SystemReconciler) styraReconcile(
systemID := system.Status.ID
migrationID := system.ObjectMeta.Annotations["styra-controller/migration-id"]
-
if r.Config.EnableMigrations && systemID == "" && migrationID != "" {
log.Info(fmt.Sprintf("Use migrationId(%s) to fetch system from Styra DAS", migrationID))
getSystemStart := time.Now()
@@ -1855,10 +1869,10 @@ func (r *SystemReconciler) reconcileOPAConfigMap(
if system.Spec.LocalPlane == nil {
log.Info("No styra local plane defined for System")
- expectedOPAConfigMap, err = k8sconv.OpaConfToK8sOPAConfigMapNoSLP(opaconf, r.Config.OPA, customConfig)
+ expectedOPAConfigMap, err = k8sconv.OPAConfToK8sOPAConfigMapNoSLP(opaconf, r.Config.OPA, customConfig)
} else {
slpURL := fmt.Sprintf("http://%s/v1", system.Spec.LocalPlane.Name)
- expectedOPAConfigMap, err = k8sconv.OpaConfToK8sOPAConfigMap(opaconf, slpURL, r.Config.OPA, customConfig)
+ expectedOPAConfigMap, err = k8sconv.OPAConfToK8sOPAConfigMap(opaconf, slpURL, r.Config.OPA, customConfig)
}
if err != nil {
return ctrl.Result{}, false, ctrlerr.Wrap(err, "Could not convert OPA conf to ConfigMap").
@@ -1939,7 +1953,7 @@ func (r *SystemReconciler) reconcileSLPConfigMap(
configmapName := fmt.Sprintf("%s-slp", system.Name)
log = log.WithValues("configmapName", configmapName)
- expectedSLPConfigMap, err := k8sconv.OpaConfToK8sSLPConfigMap(opaconf)
+ expectedSLPConfigMap, err := k8sconv.OPAConfToK8sSLPConfigMap(opaconf)
if err != nil {
return ctrl.Result{}, false, ctrlerr.Wrap(err, "Could not convert OPA Conf to SLP ConfigMap").
WithEvent(v1beta1.EventErrorConvertOPAConf).
diff --git a/internal/k8sconv/k8sconv.go b/internal/k8sconv/k8sconv.go
index e9de636d..438b1244 100644
--- a/internal/k8sconv/k8sconv.go
+++ b/internal/k8sconv/k8sconv.go
@@ -21,6 +21,7 @@ package k8sconv
import (
"fmt"
+ "github.com/go-logr/logr"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
corev1 "k8s.io/api/core/v1"
@@ -38,14 +39,6 @@ type credentials struct {
Bearer bearer `yaml:"bearer"`
}
-type s3credentials struct {
- S3Signing s3signing `yaml:"s3_signing"`
-}
-
-type s3signing struct {
- S3EnvironmentCredentials map[string]interface{} `yaml:"environment_credentials"`
-}
-
type authz struct {
Service string `yaml:"service"`
Resource string `yaml:"resource"`
@@ -62,12 +55,6 @@ type service struct {
Credentials credentials `yaml:"credentials,omitempty"`
}
-type s3service struct {
- Name string `yaml:"name"`
- URL string `yaml:"url"`
- S3Credentials s3credentials `yaml:"credentials"`
-}
-
type labels struct {
SystemID string `yaml:"system-id"`
SystemType string `yaml:"system-type"`
@@ -92,71 +79,135 @@ type requestContext struct {
HTTP http `yaml:"http"`
}
-type decisionLogs struct {
- RequestContext requestContext `yaml:"request_context"`
+// DecisionLogs contains configuration for decision logs
+type DecisionLogs struct {
+ RequestContext requestContext `json:"request_context,omitempty" yaml:"request_context,omitempty"`
+ ServiceName string `json:"service,omitempty" yaml:"service,omitempty"`
+ ResourcePath string `json:"resource_path,omitempty" yaml:"resource_path,omitempty"`
+ Reporting *DecisionLogReporting `json:"reporting,omitempty" yaml:"reporting,omitempty"`
+}
+
+// DecisionLogReporting contains configuration for decision log reporting
+type DecisionLogReporting struct {
+ MaxDelaySeconds int `json:"max_delay_seconds,omitempty" yaml:"max_delay_seconds,omitempty"`
+ MinDelaySeconds int `json:"min_delay_seconds,omitempty" yaml:"min_delay_seconds,omitempty"`
+ UploadSizeLimitBytes int `json:"upload_size_limit_bytes,omitempty" yaml:"upload_size_limit_bytes,omitempty"`
}
-type opaConfigMap struct {
+// OPAConfigMap represents the structure of the OPA configuration file
+type OPAConfigMap struct {
Services []service `yaml:"services"`
Labels labels `yaml:"labels"`
Discovery discovery `yaml:"discovery"`
- DecisionLogs decisionLogs `yaml:"decision_logs,omitempty"`
+ DecisionLogs DecisionLogs `yaml:"decision_logs,omitempty"`
+}
+
+// OcpOPAConfigMap represents the structure of the OPA configuration file for OCP
+type OcpOPAConfigMap struct {
+ Services []*ocp.OPAServiceConfig `yaml:"services"`
+ Bundles bundle `yaml:"bundles,omitempty"`
+ DecisionLogs DecisionLogs `yaml:"decision_logs,omitempty"`
+ PersistenceDirectory string `yaml:"persistence_directory,omitempty"`
+ Labels labelsOCP `yaml:"labels,omitempty"`
+ Server Serverconfig `yaml:"server,omitempty"`
+ Status StatusConfig `yaml:"status,omitempty"`
}
-type s3opaConfigMap struct {
- Services []s3service `yaml:"services"`
- Bundles bundle `yaml:"bundles,omitempty"`
- DecisionLogs decisionLogs `yaml:"decision_logs,omitempty"`
- PersistenceDirectory string `yaml:"persistence_directory,omitempty"`
- Labels labelsOCP `yaml:"labels,omitempty"`
+// StatusConfig represents the status configuration for OPA
+type StatusConfig struct {
+ Prometheus bool `yaml:"prometheus,omitempty"`
}
-// OpaConfToK8sOPAConfigMapforOCP creates a ConfigMap for the OPA.
+// Serverconfig represents the server configuration for OPA
+type Serverconfig struct {
+ Metrics Metricsconfig `yaml:"metrics,omitempty"`
+}
+
+// Metricsconfig represents the metrics configuration for OPA
+type Metricsconfig struct {
+ Prometheus PrometheusMetricsConfig `yaml:"prom,omitempty"`
+}
+
+// PrometheusMetricsConfig represents the Prometheus metrics configuration for OPA
+type PrometheusMetricsConfig struct {
+ HTTP HTTPMetricsConfig `yaml:"http_request_duration_seconds,omitempty"`
+}
+
+// HTTPMetricsConfig represents the HTTP metrics configuration for OPA
+type HTTPMetricsConfig struct {
+ Buckets []float64 `yaml:"buckets,omitempty"`
+}
+
+// OPAConfToK8sOPAConfigMapforOCP creates a ConfigMap for the OPA.
// It configures OPA to fetch bundle from MinIO.
-// OpaConfToK8sOPAConfigMapforOCP merges the information given as input into a ConfigMap for OPA
-func OpaConfToK8sOPAConfigMapforOCP(
+// OPAConfToK8sOPAConfigMapforOCP merges the information given as input into a ConfigMap for OPA
+func OPAConfToK8sOPAConfigMapforOCP(
opaconf ocp.OPAConfig,
opaDefaultConfig configv2alpha2.OPAConfig,
customConfig map[string]interface{},
+ _ logr.Logger,
) (corev1.ConfigMap, error) {
+ var services []*ocp.OPAServiceConfig
+
+ if opaconf.BundleService != nil {
+ services = append(services, opaconf.BundleService)
+ }
+ if opaconf.LogService != nil {
+ services = append(services, opaconf.LogService)
+ }
- s3opaConfigMap := s3opaConfigMap{
+ ocpOPAConfigMap := OcpOPAConfigMap{
Bundles: bundle{
Authz: authz{
- Service: opaconf.BundleService,
+ Service: opaconf.BundleService.Name,
Resource: opaconf.BundleResource,
},
},
- Services: []s3service{{
- Name: opaconf.ServiceName,
- URL: opaconf.ServiceURL,
- S3Credentials: s3credentials{
- S3Signing: s3signing{
- S3EnvironmentCredentials: map[string]interface{}{},
- },
- },
- }},
+ Services: services,
Labels: labelsOCP{
UniqueName: opaconf.UniqueName,
Namespace: opaconf.Namespace,
},
+ DecisionLogs: DecisionLogs{
+ ServiceName: opaconf.LogService.Name,
+ ResourcePath: "/logs",
+ Reporting: &DecisionLogReporting{
+ MaxDelaySeconds: opaconf.DecisionLogReporting.MaxDelaySeconds,
+ MinDelaySeconds: opaconf.DecisionLogReporting.MinDelaySeconds,
+ UploadSizeLimitBytes: opaconf.DecisionLogReporting.UploadSizeLimitBytes,
+ },
+ },
}
+
+ if opaDefaultConfig.Metrics.Prometheus.HTTP.Buckets != nil {
+ ocpOPAConfigMap.Server = Serverconfig{
+ Metrics: Metricsconfig{
+ Prometheus: PrometheusMetricsConfig{
+ HTTP: HTTPMetricsConfig{
+ Buckets: opaDefaultConfig.Metrics.Prometheus.HTTP.Buckets,
+ },
+ },
+ },
+ }
+ ocpOPAConfigMap.Status = StatusConfig{
+ Prometheus: true,
+ }
+ }
+
if opaDefaultConfig.PersistBundle {
- s3opaConfigMap.Bundles.Authz.Persist = opaDefaultConfig.PersistBundle
- s3opaConfigMap.PersistenceDirectory = opaDefaultConfig.PersistBundleDirectory
+ ocpOPAConfigMap.Bundles.Authz.Persist = opaDefaultConfig.PersistBundle
+ ocpOPAConfigMap.PersistenceDirectory = opaDefaultConfig.PersistBundleDirectory
}
if opaDefaultConfig.DecisionLogs.RequestContext.HTTP.Headers != nil {
- s3opaConfigMap.DecisionLogs = decisionLogs{
- RequestContext: requestContext{
- HTTP: http{
- Headers: opaDefaultConfig.DecisionLogs.RequestContext.HTTP.Headers,
- },
+ ocpOPAConfigMap.DecisionLogs.RequestContext = requestContext{
+ HTTP: http{
+ Headers: opaDefaultConfig.DecisionLogs.RequestContext.HTTP.Headers,
},
}
}
- opaConfigMapMapStringInterface, err := opaConfigMapToMap(s3opaConfigMap)
+ opaConfigMapMapStringInterface, err := opaConfigMapToMap(ocpOPAConfigMap)
if err != nil {
return corev1.ConfigMap{}, err
}
@@ -176,17 +227,17 @@ func OpaConfToK8sOPAConfigMapforOCP(
return cm, nil
}
-// OpaConfToK8sOPAConfigMap creates a corev1.ConfigMap for the OPA based on the
+// OPAConfToK8sOPAConfigMap creates a corev1.ConfigMap for the OPA based on the
// configuration from Styra. The configmap configures the OPA to communicate to
// an SLP.
-func OpaConfToK8sOPAConfigMap(
+func OPAConfToK8sOPAConfigMap(
opaconf styra.OPAConfig,
slpURL string,
opaDefaultConfig configv2alpha2.OPAConfig,
customConfig map[string]interface{},
) (corev1.ConfigMap, error) {
- opaConfigMap := opaConfigMap{
+ opaConfigMap := OPAConfigMap{
Services: []service{{
Name: "styra",
URL: slpURL,
@@ -202,7 +253,7 @@ func OpaConfToK8sOPAConfigMap(
}
if opaDefaultConfig.DecisionLogs.RequestContext.HTTP.Headers != nil {
- opaConfigMap.DecisionLogs = decisionLogs{
+ opaConfigMap.DecisionLogs = DecisionLogs{
RequestContext: requestContext{
HTTP: http{
Headers: opaDefaultConfig.DecisionLogs.RequestContext.HTTP.Headers,
@@ -232,9 +283,9 @@ func OpaConfToK8sOPAConfigMap(
return cm, nil
}
-// OpaConfToK8sSLPConfigMap creates a ConfigMap for the SLP based on the
+// OPAConfToK8sSLPConfigMap creates a ConfigMap for the SLP based on the
// configuration from Styra.
-func OpaConfToK8sSLPConfigMap(opaconf styra.OPAConfig) (corev1.ConfigMap, error) {
+func OPAConfToK8sSLPConfigMap(opaconf styra.OPAConfig) (corev1.ConfigMap, error) {
type Bearer struct {
TokenPath string `yaml:"token_path"`
}
@@ -300,16 +351,16 @@ func OpaConfToK8sSLPConfigMap(opaconf styra.OPAConfig) (corev1.ConfigMap, error)
return cm, nil
}
-// OpaConfToK8sOPAConfigMapNoSLP creates a ConfigMap for the OPA based on the
+// OPAConfToK8sOPAConfigMapNoSLP creates a ConfigMap for the OPA based on the
// configuration from Styra. The ConfigMap configures the OPA to communicate
// directly to Styra and not via an SLP.
-func OpaConfToK8sOPAConfigMapNoSLP(
+func OPAConfToK8sOPAConfigMapNoSLP(
opaconf styra.OPAConfig,
opaDefaultConfig configv2alpha2.OPAConfig,
customConfig map[string]interface{},
) (corev1.ConfigMap, error) {
- opaConfigMap := opaConfigMap{
+ opaConfigMap := OPAConfigMap{
Services: []service{{
Name: "styra",
URL: opaconf.HostURL,
@@ -341,7 +392,7 @@ func OpaConfToK8sOPAConfigMapNoSLP(
}
if opaDefaultConfig.DecisionLogs.RequestContext.HTTP.Headers != nil {
- opaConfigMap.DecisionLogs = decisionLogs{
+ opaConfigMap.DecisionLogs = DecisionLogs{
RequestContext: requestContext{
HTTP: http{
Headers: opaDefaultConfig.DecisionLogs.RequestContext.HTTP.Headers,
@@ -389,6 +440,9 @@ func opaConfigMapToMap(cm interface{}) (map[string]interface{}, error) {
// mergeMaps recursively merges two map[string]interface{} variables
func mergeMaps(map1, map2 map[string]interface{}) map[string]interface{} {
+ // TODO: some times, yaml structs have a name as a key and the value under it
+ // but other times, it is a list, where 'name' is one of the fields.
+ // This function does not handle that case yet.
mergedMap := make(map[string]interface{})
// Copy all key-value pairs from map1 to mergedMap
diff --git a/internal/k8sconv/k8sconv_test.go b/internal/k8sconv/k8sconv_test.go
index 51ea3d48..02d8d22a 100644
--- a/internal/k8sconv/k8sconv_test.go
+++ b/internal/k8sconv/k8sconv_test.go
@@ -19,6 +19,7 @@ package k8sconv_test
import (
"strings"
+ "github.com/go-logr/logr"
ginkgo "github.com/onsi/ginkgo/v2"
gomega "github.com/onsi/gomega"
@@ -29,7 +30,7 @@ import (
"gopkg.in/yaml.v2"
)
-var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMap", func() {
+var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMap", func() {
type test struct {
opaDefaultConfig configv2alpha2.OPAConfig
@@ -38,8 +39,8 @@ var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMap", func() {
expectedCMContent string
}
- ginkgo.DescribeTable("OpaConfToK8sOPAConfigMap", func(test test) {
- cm, err := k8sconv.OpaConfToK8sOPAConfigMap(test.opaconf, test.slpURL, test.opaDefaultConfig, nil)
+ ginkgo.DescribeTable("OPAConfToK8sOPAConfigMap", func(test test) {
+ cm, err := k8sconv.OPAConfToK8sOPAConfigMap(test.opaconf, test.slpURL, test.opaDefaultConfig, nil)
gomega.Expect(err).To(gomega.BeNil())
@@ -94,15 +95,15 @@ decision_logs:
)
})
-var _ = ginkgo.Describe("OpaConfToK8sSLPConfigMap", func() {
+var _ = ginkgo.Describe("OPAConfToK8sSLPConfigMap", func() {
type test struct {
opaconf styra.OPAConfig
expectedCMContent string
}
- ginkgo.DescribeTable("OpaConfToK8sOPAConfigMap", func(test test) {
- cm, err := k8sconv.OpaConfToK8sSLPConfigMap(test.opaconf)
+ ginkgo.DescribeTable("OPAConfToK8sOPAConfigMap", func(test test) {
+ cm, err := k8sconv.OPAConfToK8sSLPConfigMap(test.opaconf)
gomega.Expect(err).To(gomega.BeNil())
@@ -144,7 +145,7 @@ discovery:
)
})
-var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapNoSLP", func() {
+var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapNoSLP", func() {
type test struct {
opaDefaultConfig configv2alpha2.OPAConfig
@@ -152,8 +153,8 @@ var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapNoSLP", func() {
expectedCMContent string
}
- ginkgo.DescribeTable("OpaConfToK8sOPAConfigMapNoSLP", func(test test) {
- cm, err := k8sconv.OpaConfToK8sOPAConfigMapNoSLP(test.opaconf, test.opaDefaultConfig, nil)
+ ginkgo.DescribeTable("OPAConfToK8sOPAConfigMapNoSLP", func(test test) {
+ cm, err := k8sconv.OPAConfToK8sOPAConfigMapNoSLP(test.opaconf, test.opaDefaultConfig, nil)
gomega.Expect(err).To(gomega.BeNil())
@@ -217,7 +218,7 @@ decision_logs:
)
})
-var _ = ginkgo.Describe("OpaCustomConfToK8sWithSLP", func() {
+var _ = ginkgo.Describe("OPACustomConfToK8sWithSLP", func() {
type test struct {
opaDefaultConfig configv2alpha2.OPAConfig
@@ -227,8 +228,8 @@ var _ = ginkgo.Describe("OpaCustomConfToK8sWithSLP", func() {
expectedCMContent string
}
- ginkgo.DescribeTable("OpaCustomConfToK8sWithSLP", func(test test) {
- cm, err := k8sconv.OpaConfToK8sOPAConfigMap(test.opaconf, test.slpURL, test.opaDefaultConfig, test.customConfig)
+ ginkgo.DescribeTable("OPACustomConfToK8sWithSLP", func(test test) {
+ cm, err := k8sconv.OPAConfToK8sOPAConfigMap(test.opaconf, test.slpURL, test.opaDefaultConfig, test.customConfig)
gomega.Expect(err).To(gomega.BeNil())
@@ -292,7 +293,7 @@ distributed_tracing:
)
})
-var _ = ginkgo.Describe("OpaCustomConfToK8sNoSLP", func() {
+var _ = ginkgo.Describe("OPACustomConfToK8sNoSLP", func() {
type test struct {
opaDefaultConfig configv2alpha2.OPAConfig
@@ -301,8 +302,8 @@ var _ = ginkgo.Describe("OpaCustomConfToK8sNoSLP", func() {
expectedCMContent string
}
- ginkgo.DescribeTable("OpaCustomConfToK8sNoSLP", func(test test) {
- cm, err := k8sconv.OpaConfToK8sOPAConfigMapNoSLP(test.opaconf, test.opaDefaultConfig, test.customConfig)
+ ginkgo.DescribeTable("OPACustomConfToK8sNoSLP", func(test test) {
+ cm, err := k8sconv.OPAConfToK8sOPAConfigMapNoSLP(test.opaconf, test.opaDefaultConfig, test.customConfig)
gomega.Expect(err).To(gomega.BeNil())
@@ -375,7 +376,7 @@ distributed_tracing:
})
// Test PersistBundle config
-var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapforOCP", func() {
+var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapforOCP", func() {
type test struct {
opaDefaultConfig configv2alpha2.OPAConfig
@@ -384,8 +385,12 @@ var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapforOCP", func() {
expectedCMContent string
}
- ginkgo.DescribeTable("OpaConfToK8sOPAConfigMapforOCP", func(test test) {
- cm, err := k8sconv.OpaConfToK8sOPAConfigMapforOCP(test.opaconf, test.opaDefaultConfig, test.customConfig)
+ ginkgo.DescribeTable("OPAConfToK8sOPAConfigMapforOCP", func(test test) {
+ cm, err := k8sconv.OPAConfToK8sOPAConfigMapforOCP(
+ test.opaconf,
+ test.opaDefaultConfig,
+ test.customConfig,
+ logr.Discard())
gomega.Expect(err).To(gomega.BeNil())
@@ -401,7 +406,6 @@ var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapforOCP", func() {
gomega.Expect(actualMap).To(gomega.Equal(expectedMap))
},
-
ginkgo.Entry("success", test{
opaDefaultConfig: configv2alpha2.OPAConfig{
DecisionLogs: configv2alpha2.DecisionLog{
@@ -416,9 +420,29 @@ var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapforOCP", func() {
},
opaconf: ocp.OPAConfig{
BundleResource: "bundles/system/bundle.tar.gz",
- ServiceURL: "https://minio/ocp",
- ServiceName: "s3",
- BundleService: "s3",
+ BundleService: &ocp.OPAServiceConfig{
+ Name: "s3",
+ URL: "https://minio/ocp",
+ Credentials: &ocp.ServiceCredentials{
+ S3: &ocp.S3Signing{
+ S3EnvironmentCredentials: map[string]ocp.EmptyStruct{},
+ },
+ },
+ },
+ LogService: &ocp.OPAServiceConfig{
+ Name: "logs",
+ URL: "https://log-service/ocp",
+ Credentials: &ocp.ServiceCredentials{
+ Bearer: &ocp.Bearer{
+ TokenPath: "/etc/opa/auth/token",
+ },
+ },
+ },
+ DecisionLogReporting: configv2alpha2.DecisionLogReporting{
+ UploadSizeLimitBytes: 1,
+ MinDelaySeconds: 2,
+ MaxDelaySeconds: 3,
+ },
},
customConfig: map[string]interface{}{
"distributed_tracing": map[string]interface{}{
@@ -437,6 +461,11 @@ var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapforOCP", func() {
credentials:
s3_signing:
environment_credentials: {}
+- name: logs
+ url: https://log-service/ocp
+ credentials:
+ bearer:
+ token_path: /etc/opa/auth/token
bundles:
authz:
resource: bundles/system/bundle.tar.gz
@@ -445,11 +474,17 @@ bundles:
test: 123
persistence_directory: /opa-bundles
decision_logs:
+ reporting:
+ upload_size_limit_bytes: 1
+ min_delay_seconds: 2
+ max_delay_seconds: 3
request_context:
http:
headers:
- header1
- header2
+ service: logs
+ resource_path: /logs
distributed_tracing:
type: grpc
address: localhost:1234
@@ -460,7 +495,7 @@ distributed_tracing:
// Test without PersistBundle config
// And custom config overriding opaconf
-var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapforOCP", func() {
+var _ = ginkgo.Describe("OPAConfToK8sOPAConfigMapforOCP", func() {
type test struct {
opaDefaultConfig configv2alpha2.OPAConfig
@@ -469,8 +504,12 @@ var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapforOCP", func() {
expectedCMContent string
}
- ginkgo.DescribeTable("OpaConfToK8sOPAConfigMapforOCP", func(test test) {
- cm, err := k8sconv.OpaConfToK8sOPAConfigMapforOCP(test.opaconf, test.opaDefaultConfig, test.customConfig)
+ ginkgo.DescribeTable("OPAConfToK8sOPAConfigMapforOCP", func(test test) {
+ cm, err := k8sconv.OPAConfToK8sOPAConfigMapforOCP(
+ test.opaconf,
+ test.opaDefaultConfig,
+ test.customConfig,
+ logr.Discard())
gomega.Expect(err).To(gomega.BeNil())
@@ -499,10 +538,30 @@ var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapforOCP", func() {
PersistBundle: false,
},
opaconf: ocp.OPAConfig{
+ LogService: &ocp.OPAServiceConfig{
+ Name: "logs",
+ URL: "https://log-service/ocp",
+ Credentials: &ocp.ServiceCredentials{
+ Bearer: &ocp.Bearer{
+ TokenPath: "/etc/opa/auth/token",
+ },
+ },
+ },
BundleResource: "bundles/system/bundle.tar.gz",
- ServiceURL: "https://minio/ocp",
- ServiceName: "s3",
- BundleService: "s3",
+ BundleService: &ocp.OPAServiceConfig{
+ Name: "s3",
+ URL: "https://minio/ocp",
+ Credentials: &ocp.ServiceCredentials{
+ S3: &ocp.S3Signing{
+ S3EnvironmentCredentials: map[string]ocp.EmptyStruct{},
+ },
+ },
+ },
+ DecisionLogReporting: configv2alpha2.DecisionLogReporting{
+ UploadSizeLimitBytes: 1048576,
+ MinDelaySeconds: 1,
+ MaxDelaySeconds: 30,
+ },
},
customConfig: map[string]interface{}{
"distributed_tracing": map[string]interface{}{
@@ -521,11 +580,22 @@ var _ = ginkgo.Describe("OpaConfToK8sOPAConfigMapforOCP", func() {
credentials:
s3_signing:
environment_credentials: {}
+- name: logs
+ url: https://log-service/ocp
+ credentials:
+ bearer:
+ token_path: /etc/opa/auth/token
bundles:
authz:
resource: bundles/system/bundle.tar.gz
service: s4
decision_logs:
+ reporting:
+ upload_size_limit_bytes: 1048576
+ min_delay_seconds: 1
+ max_delay_seconds: 30
+ service: logs
+ resource_path: /logs
request_context:
http:
headers:
diff --git a/pkg/ocp/opaconfig.go b/pkg/ocp/opaconfig.go
index ecacedcc..2b228691 100644
--- a/pkg/ocp/opaconfig.go
+++ b/pkg/ocp/opaconfig.go
@@ -5,7 +5,7 @@ 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
+ 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,
@@ -16,12 +16,43 @@ limitations under the License.
package ocp
+import (
+ configv2alpha2 "github.com/bankdata/styra-controller/api/config/v2alpha2"
+)
+
// OPAConfig stores the information going into the ConfigMap for the OPA
type OPAConfig struct {
- BundleResource string
- BundleService string
- ServiceURL string
- ServiceName string
- UniqueName string
- Namespace string
+ BundleService *OPAServiceConfig
+ LogService *OPAServiceConfig
+ UniqueName string
+ Namespace string
+ BundleResource string
+ DecisionLogReporting configv2alpha2.DecisionLogReporting
+}
+
+// OPAServiceConfig defines a services added to the OPAs' config files.
+type OPAServiceConfig struct {
+ Name string `json:"name" yaml:"name"`
+ Credentials *ServiceCredentials `json:"credentials" yaml:"credentials"`
+ ResponseHeaderTimeoutSeconds int `json:"response_header_timeout_seconds,omitempty" yaml:"response_header_timeout_seconds,omitempty"` //nolint:lll
+ URL string `json:"url" yaml:"url"`
+}
+
+// ServiceCredentials defines the structure for service credentials.
+type ServiceCredentials struct {
+ Bearer *Bearer `json:"bearer,omitempty" yaml:"bearer,omitempty"`
+ S3 *S3Signing `json:"s3_signing,omitempty" yaml:"s3_signing,omitempty"`
+}
+
+// S3Signing defines the structure for S3 signing configuration.
+type S3Signing struct {
+ S3EnvironmentCredentials map[string]EmptyStruct `json:"environment_credentials" yaml:"environment_credentials"`
+}
+
+// EmptyStruct is an empty struct used for mapping empty values in S3EnvironmentCredentials
+type EmptyStruct struct{}
+
+// Bearer defines the structure for bearer token credentials.
+type Bearer struct {
+ TokenPath string `json:"token_path" yaml:"token_path"`
}
diff --git a/test/integration/controller/controller_suite_test.go b/test/integration/controller/controller_suite_test.go
index 9cec8e99..65c37c6d 100644
--- a/test/integration/controller/controller_suite_test.go
+++ b/test/integration/controller/controller_suite_test.go
@@ -18,7 +18,6 @@ package styra
import (
"context"
- "fmt"
"path/filepath"
"testing"
"time"
@@ -28,9 +27,6 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/mock"
- appsv1 "k8s.io/api/apps/v1"
- corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -159,6 +155,14 @@ var _ = ginkgo.BeforeSuite(func() {
OCPConfigSecretName: "s3-credentials",
},
},
+ DecisionAPIConfig: &configv2alpha2.DecisionAPIConfig{
+ ServiceURL: "log-api-url",
+ Reporting: configv2alpha2.DecisionLogReporting{
+ MaxDelaySeconds: 60,
+ MinDelaySeconds: 5,
+ UploadSizeLimitBytes: 1024,
+ },
+ },
},
UserCredentialHandler: &configv2alpha2.UserCredentialHandler{
S3: &configv2alpha2.S3Handler{
@@ -169,6 +173,12 @@ var _ = ginkgo.BeforeSuite(func() {
SecretAccessKey: "secret-access-key",
},
},
+ OPA: configv2alpha2.OPAConfig{
+ BundleServer: &configv2alpha2.OPABundleServer{
+ URL: "s3-url2",
+ Path: "/test-bucket",
+ },
+ },
},
Metrics: &styractrls.SystemReconcilerMetrics{
@@ -295,61 +305,7 @@ var _ = ginkgo.BeforeSuite(func() {
managerCtxPodRestart, managerCancelPodRestart = context.WithCancel(context.Background())
go func() {
- // Test setup function to systemReconcilerPodRestart that deploys a system with an ID and a Statefulset for a SLP
- // and restarts the SLP pods.
defer ginkgo.GinkgoRecover()
-
- systemName := "test-pod-restart"
- systemNamespace := "default"
-
- systemToCreate := &styrav1beta1.System{
- ObjectMeta: metav1.ObjectMeta{
- Name: systemName,
- Namespace: systemNamespace,
- Labels: map[string]string{
- "styra-controller/class": "styra-controller-pod-restart",
- },
- },
- Spec: styrav1beta1.SystemSpec{
- LocalPlane: &styrav1beta1.LocalPlane{
- Name: fmt.Sprintf("%v-slp", systemName),
- },
- },
- }
-
- gomega.Expect(k8sClient.Create(managerCtxPodRestart, systemToCreate)).To(gomega.Succeed())
- patch := client.MergeFrom(systemToCreate.DeepCopy())
- systemToCreate.Status.ID = "system_id"
- systemToCreate.Status.Ready = true
- gomega.Expect(k8sClient.Status().Patch(managerCtxPodRestart, systemToCreate, patch)).To(gomega.Succeed())
-
- sts := &appsv1.StatefulSet{
- ObjectMeta: metav1.ObjectMeta{
- Name: fmt.Sprintf("%v-slp", systemName),
- Namespace: systemNamespace,
- },
- Spec: appsv1.StatefulSetSpec{
- Selector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"app": fmt.Sprintf("%v-slp", systemName)},
- },
- ServiceName: fmt.Sprintf("%v-slp", systemName),
- Template: corev1.PodTemplateSpec{
- ObjectMeta: metav1.ObjectMeta{
- Labels: map[string]string{"app": fmt.Sprintf("%v-slp", systemName)},
- },
- Spec: corev1.PodSpec{
- Containers: []corev1.Container{{
- Name: "busybox",
- Image: "busybox",
- Command: []string{"sleep", "3600"},
- }},
- },
- },
- },
- }
-
- gomega.Expect(k8sClient.Create(managerCtxPodRestart, sts)).To(gomega.Succeed())
-
err = k8sManagerPodRestart.Start(managerCtxPodRestart)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
}()
diff --git a/test/integration/controller/system_controller_test.go b/test/integration/controller/system_controller_test.go
index 2a8a8ed2..6876e2e4 100644
--- a/test/integration/controller/system_controller_test.go
+++ b/test/integration/controller/system_controller_test.go
@@ -2238,14 +2238,55 @@ distributed_tracing:
})
})
-var _ = ginkgo.Describe("SystemReconciler.Reconcile1", ginkgo.Label("integration"), func() {
+var _ = ginkgo.Describe("SystemReconciler.WithPodRestart", ginkgo.Label("integration"), func() {
ginkgo.It("should reconcile", func() {
+
+ ctx := context.Background()
+
key := types.NamespacedName{
Name: "test-pod-restart",
Namespace: "default",
}
- ctx := context.Background()
+ systemToCreate := &styrav1beta1.System{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: key.Name,
+ Namespace: key.Namespace,
+ Labels: map[string]string{
+ "styra-controller/class": "styra-controller-pod-restart",
+ },
+ },
+ Spec: styrav1beta1.SystemSpec{
+ LocalPlane: &styrav1beta1.LocalPlane{
+ Name: fmt.Sprintf("%v-slp", key.Name),
+ },
+ },
+ }
+
+ sts := &appsv1.StatefulSet{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: fmt.Sprintf("%v-slp", key.Name),
+ Namespace: key.Namespace,
+ },
+ Spec: appsv1.StatefulSetSpec{
+ Selector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"app": fmt.Sprintf("%v-slp", key.Name)},
+ },
+ ServiceName: fmt.Sprintf("%v-slp", key.Name),
+ Template: corev1.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Labels: map[string]string{"app": fmt.Sprintf("%v-slp", key.Name)},
+ },
+ Spec: corev1.PodSpec{
+ Containers: []corev1.Container{{
+ Name: "busybox",
+ Image: "busybox",
+ Command: []string{"sleep", "3600"},
+ }},
+ },
+ },
+ },
+ }
cfg := &styra.SystemConfig{
ID: "system_id",
@@ -2253,30 +2294,14 @@ var _ = ginkgo.Describe("SystemReconciler.Reconcile1", ginkgo.Label("integration
ReadOnly: true,
}
- ginkgo.By("Empty System already has ID but does not exist in Styra")
+ ginkgo.By("Create system and see SLP pods are restarted")
- styraClientMock.On("GetSystem", mock.Anything, "system_id").Return(&styra.GetSystemResponse{
- StatusCode: http.StatusNotFound,
+ styraClientMock.On("GetSystemByName", mock.Anything, key.String()).Return(&styra.GetSystemResponse{
+ StatusCode: http.StatusOK,
SystemConfig: nil,
- }, &httperror.HTTPError{
- StatusCode: http.StatusNotFound,
- Body: "nil",
- }).Once()
+ }, nil).Once()
- styraClientMock.On("PutSystem",
- mock.Anything,
- mock.MatchedBy(func(req *styra.PutSystemRequest) bool {
- matchesDecisionmapping := len(req.SystemConfig.DecisionMappings) == 0
- matchesDescription := req.SystemConfig.Description == cfg.Description
- matchesSourceControl := req.SystemConfig.SourceControl == nil
-
- return matchesDecisionmapping &&
- matchesDescription &&
- matchesSourceControl
- }),
- "system_id",
- map[string]string{"If-None-Match": "*"},
- ).Return(&styra.PutSystemResponse{
+ styraClientMock.On("CreateSystem", mock.Anything, mock.Anything).Return(&styra.CreateSystemResponse{
StatusCode: http.StatusOK,
SystemConfig: cfg,
}, nil).Once()
@@ -2424,11 +2449,16 @@ var _ = ginkgo.Describe("SystemReconciler.Reconcile1", ginkgo.Label("integration
SystemType: "custom",
}, nil).Once()
+ // Create StatefulSet representing SLP and deploy system
+ gomega.Expect(k8sClient.Create(ctx, sts)).To(gomega.Succeed())
+ gomega.Expect(k8sClient.Create(ctx, systemToCreate)).To(gomega.Succeed())
+
gomega.Eventually(func() bool {
fetched := &styrav1beta1.System{}
if err := k8sClient.Get(ctx, key, fetched); err != nil {
return false
}
+
return finalizer.IsSet(fetched) &&
fetched.Status.ID == "system_id" &&
fetched.Status.Phase == styrav1beta1.SystemPhaseCreated &&
@@ -2454,36 +2484,44 @@ var _ = ginkgo.Describe("SystemReconciler.Reconcile1", ginkgo.Label("integration
gomega.Eventually(func() bool {
var (
- getSystem int
- putSystem int
+ getSystemByName int
+ createSystem int
deletePolicy int
+ updateSystem int
+ getSystem int
getUsers int
rolebindingsListed int
getOPAConfig int
)
for _, call := range styraClientMock.Calls {
switch call.Method {
- case "GetSystem":
- getSystem++
- case "PutSystem":
- putSystem++
+ case "GetSystemByName":
+ getSystemByName++
+ case "CreateSystem":
+ createSystem++
case "DeletePolicy":
deletePolicy++
- case "GetUsers":
- getUsers++
+ case "UpdateSystem":
+ updateSystem++
case "ListRoleBindingsV2":
rolebindingsListed++
+ case "GetSystem":
+ getSystem++
+ case "GetUsers":
+ getUsers++
case "GetOPAConfig":
getOPAConfig++
}
}
- return getSystem == 4 &&
- putSystem == 1 &&
+ return getSystemByName == 1 &&
+ createSystem == 1 &&
deletePolicy == 2 &&
+ updateSystem == 1 &&
getUsers == 4 &&
rolebindingsListed == 4 &&
- getOPAConfig == 4
+ getOPAConfig == 4 &&
+ getSystem == 3
}, timeout, interval).Should(gomega.BeTrue())
resetMock(&styraClientMock.Mock)
@@ -2761,6 +2799,7 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int
key := types.NamespacedName{Name: fmt.Sprintf("%s-opa-config", key.Name), Namespace: key.Namespace}
if fetchSuceeded := k8sClient.Get(ctx, key, fetched) == nil; !fetchSuceeded {
+ fmt.Println("Failed to fetch ConfigMap")
return false
}
@@ -2769,6 +2808,13 @@ var _ = ginkgo.Describe("SystemReconciler.ReconcileOCPSystem", ginkgo.Label("int
authz:
resource: bundles/default-ocp-system/bundle.tar.gz
service: s3
+decision_logs:
+ reporting:
+ max_delay_seconds: 60
+ min_delay_seconds: 5
+ upload_size_limit_bytes: 1024
+ resource_path: /logs
+ service: logs
labels:
namespace: default
unique-name: default-ocp-system
@@ -2777,20 +2823,30 @@ services:
s3_signing:
environment_credentials: {}
name: s3
- url: s3-url/test-bucket
+ url: s3-url2/test-bucket
+- credentials:
+ bearer:
+ token_path: /run/secrets/kubernetes.io/serviceaccount/token
+ name: logs
+ url: log-api-url
`
if err := yaml.Unmarshal([]byte(actualYAML), &actualMap); err != nil {
+ fmt.Println("Failed to unmarshal actual YAML:", err)
return false
}
if err := yaml.Unmarshal([]byte(expectedYAML), &expectedMap); err != nil {
+ fmt.Println("Failed to unmarshal expected YAML:", err)
return false
}
equal := reflect.DeepEqual(expectedMap, actualMap)
if !equal {
- fmt.Println("Actual", string(actualYAML))
- fmt.Println("Expected", string(expectedYAML))
+ fmt.Println("reconciliation failed")
+ fmt.Println("Actual: \n", string(actualYAML))
+ fmt.Println("Expected: \n", string(expectedYAML))
+ fmt.Println("Actual Map: \n", actualMap)
+ fmt.Println("Expected Map: \n", expectedMap)
}
return equal
}, timeout, interval).Should(gomega.BeTrue())
@@ -2885,6 +2941,6 @@ services:
return k8serrors.IsNotFound(err)
}, timeout, interval).Should(gomega.BeTrue())
- resetMock(&styraClientMock.Mock)
+ resetMock(&ocpClientMock.Mock)
})
})
|