├── tests
├── templates
│ ├── .gitkeep
│ └── kuttl
│ │ ├── ad-user-info
│ │ ├── 30-prepare-test-regorule.yaml
│ │ ├── 00-limit-range.yaml
│ │ ├── 01-assert.yaml.j2
│ │ ├── 30-assert.yaml
│ │ ├── 10-assert.yaml
│ │ ├── 01-install-vector-aggregator-discovery-configmap.yaml.j2
│ │ ├── 20-assert.yaml
│ │ ├── 00-patch-ns.yaml.j2
│ │ ├── 20-install-test-regorule.yaml
│ │ └── 10-install-opa.yaml.j2
│ │ ├── aas-user-info
│ │ ├── 30-prepare-test-regorule.yaml
│ │ ├── 02-assert.yaml
│ │ ├── 01-assert.yaml.j2
│ │ ├── 10-assert.yaml
│ │ ├── 30-assert.yaml
│ │ ├── 01-install-vector-aggregator-discovery-configmap.yaml.j2
│ │ ├── 20-assert.yaml
│ │ ├── 00-patch-ns.yaml.j2
│ │ ├── 20-install-test-regorule.yaml
│ │ ├── 00-misc-setup.yaml.j2
│ │ ├── 10-install-opa.yaml.j2
│ │ ├── test-regorule.py
│ │ └── 02-install-aas.yaml.j2
│ │ ├── keycloak-user-info
│ │ ├── 30-prepare-test-regorule.yaml
│ │ ├── 00-limit-range.yaml
│ │ ├── 04-assert.yaml
│ │ ├── 01-assert.yaml.j2
│ │ ├── 10-assert.yaml
│ │ ├── 30-assert.yaml
│ │ ├── 01-install-vector-aggregator-discovery-configmap.yaml.j2
│ │ ├── 20-assert.yaml
│ │ ├── 00-patch-ns.yaml.j2
│ │ ├── 20-install-test-regorule.yaml
│ │ ├── 10-install-opa.yaml.j2
│ │ ├── test-regorule.py
│ │ └── 04-keycloak-realm-cm.yaml
│ │ ├── openldap-user-info
│ │ ├── 50-prepare-test-regorule.yaml
│ │ ├── 10-assert.yaml
│ │ ├── 00-limit-range.yaml
│ │ ├── 01-assert.yaml.j2
│ │ ├── 01-install-vector-aggregator-discovery-configmap.yaml.j2
│ │ ├── 40-assert.yaml
│ │ ├── 00-patch-ns.yaml.j2
│ │ ├── 20-load-ldif.yaml
│ │ ├── 30-assert.yaml
│ │ ├── 50-assert.yaml
│ │ └── 40-install-test-regorule.yaml
│ │ ├── logging
│ │ ├── 03-assert.yaml
│ │ ├── 04-assert.yaml
│ │ ├── 01-assert.yaml
│ │ ├── 05-test-log-aggregation.yaml
│ │ ├── 02-create-configmap-with-prepared-logs.yaml
│ │ ├── 05-assert.yaml
│ │ ├── 00-patch-ns.yaml.j2
│ │ ├── 01-install-opa-vector-aggregator.yaml
│ │ ├── test_log_aggregation.sh
│ │ ├── 04-install-opa-test-runner.yaml
│ │ ├── prepared-bundle-builder-logs.tracing-rs.json
│ │ ├── prepared-opa-logs.json
│ │ ├── test_log_aggregation.py
│ │ └── opa-vector-aggregator-values.yaml.j2
│ │ ├── smoke
│ │ ├── 00-limit-range.yaml
│ │ ├── 20-assert.yaml
│ │ ├── 30-prepare-test-opa.yaml
│ │ ├── 01-assert.yaml.j2
│ │ ├── 01-install-vector-aggregator-discovery-configmap.yaml.j2
│ │ ├── 00-patch-ns.yaml.j2
│ │ ├── 32-assert.yaml
│ │ ├── 11-assert.yaml
│ │ ├── 30-assert.yaml.j2
│ │ ├── 31-assert.yaml.j2
│ │ ├── 09-install-secretclass.yaml.j2
│ │ ├── 30_test-metrics.py
│ │ ├── 10-assert.yaml.j2
│ │ ├── 30_test-regorule.py
│ │ ├── 20-install-test-opa.yaml.j2
│ │ └── 10-install-opa.yaml.j2
│ │ ├── resources
│ │ ├── 00-limit-range.yaml
│ │ ├── 01-assert.yaml.j2
│ │ ├── 01-install-vector-aggregator-discovery-configmap.yaml.j2
│ │ ├── 00-patch-ns.yaml.j2
│ │ ├── 10-install-opa.yaml.j2
│ │ └── 10-assert.yaml.j2
│ │ └── cluster-operation
│ │ ├── 00-assert.yaml.j2
│ │ ├── 00-install-vector-aggregator-discovery-configmap.yaml.j2
│ │ ├── 10-assert.yaml
│ │ ├── 20-assert.yaml
│ │ ├── 40-assert.yaml
│ │ ├── 30-assert.yaml
│ │ ├── 00-patch-ns.yaml.j2
│ │ ├── 10-install-opa.yaml.j2
│ │ ├── 40-restart-opa.yaml.j2
│ │ ├── 20-stop-opa.yaml.j2
│ │ └── 30-pause-opa.yaml.j2
├── release.yaml
├── interu.yaml
├── kuttl-test.yaml.jinja2
├── test-definition.yaml
└── README-templating.md
├── rust
├── operator-binary
│ ├── src
│ │ ├── lib.rs
│ │ ├── operations
│ │ │ ├── mod.rs
│ │ │ └── graceful_shutdown.rs
│ │ └── product_logging.rs
│ ├── build.rs
│ └── Cargo.toml
├── user-info-fetcher
│ ├── src
│ │ ├── utils
│ │ │ ├── mod.rs
│ │ │ ├── http.rs
│ │ │ └── tls.rs
│ │ ├── backend
│ │ │ └── mod.rs
│ │ └── http_error.rs
│ ├── build.rs
│ ├── README.md
│ └── Cargo.toml
├── bundle-builder
│ ├── build.rs
│ └── Cargo.toml
└── regorule-library
│ ├── src
│ ├── lib.rs
│ └── userinfo
│ │ └── v1.rego
│ ├── Cargo.toml
│ └── README.md
├── docs
├── antora.yml
├── modules
│ └── opa
│ │ ├── examples
│ │ └── getting_started
│ │ │ ├── expected_response_world.json
│ │ │ ├── expected_response_hello.json
│ │ │ ├── install_output.txt
│ │ │ ├── install_output.txt.j2
│ │ │ ├── test_getting_started_helm.sh
│ │ │ ├── test_getting_started_stackablectl.sh
│ │ │ ├── opa.yaml
│ │ │ ├── opa.yaml.j2
│ │ │ ├── simple-rule.yaml
│ │ │ ├── getting_started.sh
│ │ │ └── getting_started.sh.j2
│ │ ├── images
│ │ └── keycloak-user-info-fetcher
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ └── 4.png
│ │ ├── pages
│ │ ├── reference
│ │ │ ├── crds.adoc
│ │ │ ├── index.adoc
│ │ │ ├── commandline-parameters.adoc
│ │ │ ├── discovery.adoc
│ │ │ └── environment-variables.adoc
│ │ ├── usage-guide
│ │ │ ├── operations
│ │ │ │ ├── cluster-operations.adoc
│ │ │ │ ├── index.adoc
│ │ │ │ ├── pod-disruptions.adoc
│ │ │ │ └── graceful-shutdown.adoc
│ │ │ ├── index.adoc
│ │ │ ├── listenerclass.adoc
│ │ │ ├── monitoring.adoc
│ │ │ ├── policies.adoc
│ │ │ ├── resources.adoc
│ │ │ ├── configuration-environment-overrides.adoc
│ │ │ ├── OpenTelemetry.adoc
│ │ │ ├── logging.adoc
│ │ │ └── tls.adoc
│ │ ├── getting_started
│ │ │ ├── index.adoc
│ │ │ ├── installation.adoc
│ │ │ └── first_steps.adoc
│ │ └── implementation-notes.adoc
│ │ └── partials
│ │ ├── supported-versions.adoc
│ │ └── nav.adoc
└── templating_vars.yaml
├── scripts
├── run_tests.sh
├── render_readme.sh
├── generate-manifests.sh
├── docs_templating.sh
└── ensure_one_trailing_newline.py
├── deploy
├── config-spec
│ └── properties.yaml
├── helm
│ ├── opa-operator
│ │ ├── configs
│ │ │ └── properties.yaml
│ │ ├── templates
│ │ │ ├── configmap.yaml
│ │ │ ├── service.yaml
│ │ │ ├── _maintenance.tpl
│ │ │ ├── roles-opa-builder.yaml
│ │ │ ├── serviceaccount.yaml
│ │ │ ├── _telemetry.tpl
│ │ │ ├── _helpers.tpl
│ │ │ └── roles.yaml
│ │ ├── Chart.yaml
│ │ ├── .helmignore
│ │ ├── README.md
│ │ └── values.yaml
│ ├── chart_testing.yaml
│ └── ct.yaml
├── stackable-operators-ns.yaml
└── DO_NOT_EDIT.md
├── .gitattributes
├── .actionlint.yaml
├── .github
├── actionlint.yaml
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── normal-issue.md
│ ├── 01-normal-issue.md
│ ├── new_version.md
│ └── 02-bug_report.yml
├── workflows
│ ├── general_daily_security.yml
│ ├── pr_pre-commit.yaml
│ └── integration-test.yml
├── PULL_REQUEST_TEMPLATE
│ ├── pre-release-getting-started-script.md
│ └── pre-release-rust-deps.md
└── pull_request_template.md
├── rust-toolchain.toml
├── .readme
├── static
│ └── borrowed
│ │ ├── stackable_overview.png
│ │ └── Icon_Stackable.svg
├── partials
│ ├── borrowed
│ │ ├── header.md.j2
│ │ ├── related_reading.md.j2
│ │ ├── overview_blurb.md.j2
│ │ ├── documentation.md.j2
│ │ ├── links.md.j2
│ │ └── footer.md.j2
│ └── main.md.j2
└── README.md.j2
├── .envrc.sample
├── nix
├── meta.json
├── README.md
└── sources.json
├── .vscode
├── settings.json
└── launch.json
├── .pylintrc
├── .gitignore
├── renovate.json
├── .dockerignore
├── .hadolint.yaml
├── rustfmt.toml
├── .yamllint.yaml
├── .markdownlint.yaml
├── crate-hashes.json
├── shell.nix
├── Cargo.toml
├── deny.toml
├── Tiltfile
└── Makefile
/tests/templates/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/rust/operator-binary/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod crd;
2 |
--------------------------------------------------------------------------------
/docs/antora.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: home
3 | version: "nightly"
4 |
--------------------------------------------------------------------------------
/rust/operator-binary/src/operations/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod graceful_shutdown;
2 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/expected_response_world.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/rust/user-info-fetcher/src/utils/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod http;
2 | pub mod tls;
3 |
--------------------------------------------------------------------------------
/scripts/run_tests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ./scripts/run-tests "$@"
4 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/expected_response_hello.json:
--------------------------------------------------------------------------------
1 | {"result":true}
2 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/install_output.txt:
--------------------------------------------------------------------------------
1 | Installed opa=0.0.0-dev operator
2 |
--------------------------------------------------------------------------------
/rust/bundle-builder/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | built::write_built_file().unwrap();
3 | }
4 |
--------------------------------------------------------------------------------
/rust/operator-binary/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | built::write_built_file().unwrap();
3 | }
4 |
--------------------------------------------------------------------------------
/rust/user-info-fetcher/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | built::write_built_file().unwrap();
3 | }
4 |
--------------------------------------------------------------------------------
/deploy/config-spec/properties.yaml:
--------------------------------------------------------------------------------
1 | version: 0.1.0
2 | spec:
3 | units: []
4 |
5 | properties: []
6 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/install_output.txt.j2:
--------------------------------------------------------------------------------
1 | Installed opa={{ versions.opa }} operator
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | nix/** linguist-generated
2 | Cargo.nix linguist-generated
3 | crate-hashes.json linguist-generated
4 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/configs/properties.yaml:
--------------------------------------------------------------------------------
1 | version: 0.1.0
2 | spec:
3 | units: []
4 |
5 | properties: []
6 |
--------------------------------------------------------------------------------
/deploy/helm/chart_testing.yaml:
--------------------------------------------------------------------------------
1 | remote: origin
2 | target-branch: main
3 | chart-dirs:
4 | - deploy/helm
5 | all: true
6 |
--------------------------------------------------------------------------------
/deploy/stackable-operators-ns.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Namespace
4 | metadata:
5 | name: stackable-operators
6 |
--------------------------------------------------------------------------------
/.actionlint.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | self-hosted-runner:
3 | # Ubicloud machines we are using
4 | labels:
5 | - ubicloud-standard-8-arm
6 |
--------------------------------------------------------------------------------
/.github/actionlint.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | self-hosted-runner:
3 | # Ubicloud machines we are using
4 | labels:
5 | - ubicloud-standard-8-arm
6 |
--------------------------------------------------------------------------------
/docs/templating_vars.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | helm:
3 | repo_name: sdp-charts
4 | repo_url: oci.stackable.tech
5 | versions:
6 | opa: 0.0.0-dev
7 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | # DO NOT EDIT, this file is generated by operator-templating
2 | [toolchain]
3 | channel = "1.89.0"
4 | profile = "default"
5 |
--------------------------------------------------------------------------------
/.readme/static/borrowed/stackable_overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stackabletech/opa-operator/HEAD/.readme/static/borrowed/stackable_overview.png
--------------------------------------------------------------------------------
/rust/user-info-fetcher/src/backend/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod active_directory;
2 | pub mod entra;
3 | pub mod keycloak;
4 | pub mod openldap;
5 | pub mod xfsc_aas;
6 |
--------------------------------------------------------------------------------
/.envrc.sample:
--------------------------------------------------------------------------------
1 | # vim: syntax=conf
2 | #
3 | # If you use direnv, you can autoload the nix shell:
4 | # You will need to allow the directory the first time.
5 | use nix
6 |
--------------------------------------------------------------------------------
/rust/regorule-library/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub const REGORULES: &[(&str, &str)] = &[(
2 | "stackable/opa/userinfo/v1.rego",
3 | include_str!("userinfo/v1.rego"),
4 | )];
5 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/test_getting_started_helm.sh:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 | set -euo pipefail
3 |
4 | cd "$(dirname "$0")"
5 | ./getting_started.sh helm
6 |
--------------------------------------------------------------------------------
/docs/modules/opa/images/keycloak-user-info-fetcher/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stackabletech/opa-operator/HEAD/docs/modules/opa/images/keycloak-user-info-fetcher/1.png
--------------------------------------------------------------------------------
/docs/modules/opa/images/keycloak-user-info-fetcher/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stackabletech/opa-operator/HEAD/docs/modules/opa/images/keycloak-user-info-fetcher/2.png
--------------------------------------------------------------------------------
/docs/modules/opa/images/keycloak-user-info-fetcher/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stackabletech/opa-operator/HEAD/docs/modules/opa/images/keycloak-user-info-fetcher/3.png
--------------------------------------------------------------------------------
/docs/modules/opa/images/keycloak-user-info-fetcher/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stackabletech/opa-operator/HEAD/docs/modules/opa/images/keycloak-user-info-fetcher/4.png
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/test_getting_started_stackablectl.sh:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 | set -euo pipefail
3 |
4 | cd "$(dirname "$0")"
5 | ./getting_started.sh stackablectl
6 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/reference/crds.adoc:
--------------------------------------------------------------------------------
1 | = CRD reference
2 |
3 | Find all CRD reference for the Stackable operator for OpenPolicyAgent at: {crd-docs-base-url}/opa-operator/{crd-docs-version}.
4 |
--------------------------------------------------------------------------------
/nix/meta.json:
--------------------------------------------------------------------------------
1 | {"operator": {"name": "opa-operator", "extra_crates": ["stackable-opa-bundle-builder"], "pretty_string": "OpenPolicyAgent", "product_string": "opa", "url": "stackabletech/opa-operator.git"}}
2 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/30-prepare-test-regorule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: kubectl cp -n $NAMESPACE ./test-regorule.py test-regorule-0:/tmp
6 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/30-prepare-test-regorule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: kubectl cp -n $NAMESPACE ./test-regorule.py test-regorule-0:/tmp
6 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "rust-analyzer.rustfmt.overrideCommand": [
3 | "rustfmt",
4 | "+nightly-2025-10-23",
5 | "--edition",
6 | "2024",
7 | "--"
8 | ],
9 | }
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/30-prepare-test-regorule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: kubectl cp -n $NAMESPACE ./test-regorule.py test-regorule-0:/tmp
6 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/50-prepare-test-regorule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: kubectl cp -n $NAMESPACE ./test-regorule.py test-regorule-0:/tmp
6 |
--------------------------------------------------------------------------------
/.pylintrc:
--------------------------------------------------------------------------------
1 | [MESSAGES CONTROL]
2 |
3 | # These rules are for missing docstrings which doesn't matter much for most of our simple scripts
4 | disable=C0114,C0115,C0116
5 |
6 | [FORMAT]
7 |
8 | max-line-length=999
9 | indent-string=' '
10 |
--------------------------------------------------------------------------------
/.readme/partials/borrowed/header.md.j2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{title}}
7 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/03-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 600
5 | commands:
6 | - script: kubectl -n $NAMESPACE rollout status daemonset test-opa-server-automatic-log-config --timeout 600s
7 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/10-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 300
5 | commands:
6 | - script: kubectl wait --for=condition=ready pod/test-openldap-0 -n $NAMESPACE --timeout=300s
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | tests/_work/
2 | debug/
3 | target/
4 | **/*.rs.bk
5 |
6 | .idea/
7 | *.iws
8 | *.iml
9 |
10 | *.tgz
11 |
12 | result
13 | image.tar
14 |
15 | tilt_options.json
16 |
17 | .direnv/
18 | .direnvrc
19 | .envrc
20 |
21 | .DS_Store
22 |
--------------------------------------------------------------------------------
/.readme/partials/borrowed/related_reading.md.j2:
--------------------------------------------------------------------------------
1 |
2 | {%- if related_reading_links -%}
3 | ## Related Reading
4 | {% for (text, link) in related_reading_links %}
5 | * [{{text}}]({{link}})
6 | {%- endfor %}
7 | {%- endif -%}
8 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/00-limit-range.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: LimitRange
4 | metadata:
5 | name: limit-request-ratio
6 | spec:
7 | limits:
8 | - type: "Container"
9 | maxLimitRequestRatio:
10 | cpu: 5
11 | memory: 1
12 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/20-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 300
5 | ---
6 | apiVersion: apps/v1
7 | kind: StatefulSet
8 | metadata:
9 | name: test-opa
10 | status:
11 | readyReplicas: 1
12 | replicas: 1
13 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/30-prepare-test-opa.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | commands:
4 | - script: kubectl cp -n $NAMESPACE ./30_test-regorule.py test-opa-0:/tmp
5 | - script: kubectl cp -n $NAMESPACE ./30_test-metrics.py test-opa-0:/tmp
6 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/02-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 300
5 | ---
6 | apiVersion: apps/v1
7 | kind: Deployment
8 | metadata:
9 | name: aas
10 | status:
11 | readyReplicas: 1
12 | replicas: 1
13 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/resources/00-limit-range.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: LimitRange
4 | metadata:
5 | name: limit-request-ratio
6 | spec:
7 | limits:
8 | - type: "Container"
9 | maxLimitRequestRatio:
10 | cpu: 5
11 | memory: 1
12 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/opa.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: opa.stackable.tech/v1alpha1
3 | kind: OpaCluster
4 | metadata:
5 | name: simple-opa
6 | spec:
7 | image:
8 | productVersion: "1.8.0"
9 | servers:
10 | roleGroups:
11 | default: {}
12 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: opa.stackable.tech/v1alpha1
3 | kind: OpaCluster
4 | metadata:
5 | name: simple-opa
6 | spec:
7 | image:
8 | productVersion: "1.8.0"
9 | servers:
10 | roleGroups:
11 | default: {}
12 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/00-limit-range.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: LimitRange
4 | metadata:
5 | name: limit-request-ratio
6 | spec:
7 | limits:
8 | - type: "Container"
9 | maxLimitRequestRatio:
10 | cpu: 5
11 | memory: 1
12 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/04-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 300
5 | ---
6 | apiVersion: apps/v1
7 | kind: StatefulSet
8 | metadata:
9 | name: opa-test-runner
10 | status:
11 | readyReplicas: 1
12 | replicas: 1
13 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/resources/01-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
5 | ---
6 | apiVersion: v1
7 | kind: ConfigMap
8 | metadata:
9 | name: vector-aggregator-discovery
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/01-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
5 | ---
6 | apiVersion: v1
7 | kind: ConfigMap
8 | metadata:
9 | name: vector-aggregator-discovery
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/01-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
5 | ---
6 | apiVersion: v1
7 | kind: ConfigMap
8 | metadata:
9 | name: vector-aggregator-discovery
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/01-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
5 | ---
6 | apiVersion: v1
7 | kind: ConfigMap
8 | metadata:
9 | name: vector-aggregator-discovery
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/00-limit-range.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: LimitRange
4 | metadata:
5 | name: limit-request-ratio
6 | spec:
7 | limits:
8 | - type: "Container"
9 | maxLimitRequestRatio:
10 | cpu: 5
11 | memory: 1
12 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/04-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 300
5 | ---
6 | apiVersion: apps/v1
7 | kind: Deployment
8 | metadata:
9 | name: keycloak
10 | status:
11 | readyReplicas: 1
12 | replicas: 1
13 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/01-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 600
5 | ---
6 | apiVersion: apps/v1
7 | kind: StatefulSet
8 | metadata:
9 | name: opa-vector-aggregator
10 | status:
11 | readyReplicas: 1
12 | replicas: 1
13 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/00-limit-range.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: LimitRange
4 | metadata:
5 | name: limit-request-ratio
6 | spec:
7 | limits:
8 | - type: "Container"
9 | maxLimitRequestRatio:
10 | cpu: 5
11 | memory: 1
12 |
--------------------------------------------------------------------------------
/docs/modules/opa/partials/supported-versions.adoc:
--------------------------------------------------------------------------------
1 | // The version ranges supported by the OPA operator
2 | // This is a separate file, since it is used by both the direct OPA documentation, and the overarching
3 | // Stackable Platform documentation.
4 |
5 | * 1.8.0
6 | * 1.4.2 (deprecated)
7 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/00-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
5 | ---
6 | apiVersion: v1
7 | kind: ConfigMap
8 | metadata:
9 | name: vector-aggregator-discovery
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/01-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
5 | ---
6 | apiVersion: v1
7 | kind: ConfigMap
8 | metadata:
9 | name: vector-aggregator-discovery
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/01-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
5 | ---
6 | apiVersion: v1
7 | kind: ConfigMap
8 | metadata:
9 | name: vector-aggregator-discovery
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/30-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-regorule
6 | commands:
7 | - script: kubectl exec -n $NAMESPACE test-regorule-0 -- python /tmp/test-regorule.py -u 'http://test-opa-server:8081/v1/data/test'
8 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/10-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: install-opa
6 | timeout: 300
7 | commands:
8 | - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa --timeout 301s
9 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/30-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-regorule
6 | commands:
7 | - script: kubectl exec -n $NAMESPACE test-regorule-0 -- python /tmp/test-regorule.py -u 'http://test-opa-server:8081/v1/data/test'
8 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/10-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: install-opa
6 | timeout: 300
7 | commands:
8 | - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa --timeout 301s
9 |
--------------------------------------------------------------------------------
/deploy/helm/ct.yaml:
--------------------------------------------------------------------------------
1 | # This file is used for chart-testing (https://github.com/helm/chart-testing)
2 | # The name "ct.yaml" is not very self-descriptive but it is the default that chart-testing is looking for
3 | ---
4 | remote: origin
5 | target-branch: main
6 | chart-dirs:
7 | - deploy/helm
8 | all: true
9 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/templates/configmap.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | data:
4 | {{ (.Files.Glob "configs/*").AsConfig | indent 2 }}
5 | kind: ConfigMap
6 | metadata:
7 | name: {{ include "operator.fullname" . }}-configmap
8 | labels:
9 | {{- include "operator.labels" . | nindent 4 }}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/10-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: install-opa
6 | timeout: 300
7 | commands:
8 | - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa --timeout 301s
9 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/30-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-regorule
6 | commands:
7 | - script: kubectl exec -n $NAMESPACE test-regorule-0 -- python /tmp/test-regorule.py -u 'http://test-opa-server:8081/v1/data/test'
8 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/operations/cluster-operations.adoc:
--------------------------------------------------------------------------------
1 | = Cluster Operation
2 |
3 | OPA installations can be configured with different cluster operations like pausing reconciliation or stopping the cluster. See xref:concepts:operations/cluster_operations.adoc[cluster operations] for more details.
4 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/01-install-vector-aggregator-discovery-configmap.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
2 | ---
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: vector-aggregator-discovery
7 | data:
8 | ADDRESS: {{ lookup('env', 'VECTOR_AGGREGATOR') }}
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/01-install-vector-aggregator-discovery-configmap.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
2 | ---
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: vector-aggregator-discovery
7 | data:
8 | ADDRESS: {{ lookup('env', 'VECTOR_AGGREGATOR') }}
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/20-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-regorule
6 | timeout: 300
7 | ---
8 | apiVersion: apps/v1
9 | kind: StatefulSet
10 | metadata:
11 | name: test-regorule
12 | status:
13 | readyReplicas: 1
14 | replicas: 1
15 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/01-install-vector-aggregator-discovery-configmap.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
2 | ---
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: vector-aggregator-discovery
7 | data:
8 | ADDRESS: {{ lookup('env', 'VECTOR_AGGREGATOR') }}
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/20-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-regorule
6 | timeout: 300
7 | ---
8 | apiVersion: apps/v1
9 | kind: StatefulSet
10 | metadata:
11 | name: test-regorule
12 | status:
13 | readyReplicas: 1
14 | replicas: 1
15 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/05-test-log-aggregation.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: |
6 | kubectl cp ./test_log_aggregation.py $NAMESPACE/opa-test-runner-0:/tmp
7 | - script: |
8 | kubectl cp ./test_log_aggregation.sh $NAMESPACE/opa-test-runner-0:/tmp
9 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/resources/01-install-vector-aggregator-discovery-configmap.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
2 | ---
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: vector-aggregator-discovery
7 | data:
8 | ADDRESS: {{ lookup('env', 'VECTOR_AGGREGATOR') }}
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/00-install-vector-aggregator-discovery-configmap.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
2 | ---
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: vector-aggregator-discovery
7 | data:
8 | ADDRESS: {{ lookup('env', 'VECTOR_AGGREGATOR') }}
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/01-install-vector-aggregator-discovery-configmap.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
2 | ---
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: vector-aggregator-discovery
7 | data:
8 | ADDRESS: {{ lookup('env', 'VECTOR_AGGREGATOR') }}
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/20-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-regorule
6 | timeout: 300
7 | ---
8 | apiVersion: apps/v1
9 | kind: StatefulSet
10 | metadata:
11 | name: test-regorule
12 | status:
13 | readyReplicas: 1
14 | replicas: 1
15 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/01-install-vector-aggregator-discovery-configmap.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
2 | ---
3 | apiVersion: v1
4 | kind: ConfigMap
5 | metadata:
6 | name: vector-aggregator-discovery
7 | data:
8 | ADDRESS: {{ lookup('env', 'VECTOR_AGGREGATOR') }}
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/40-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-regorule
6 | timeout: 300
7 | ---
8 | apiVersion: apps/v1
9 | kind: StatefulSet
10 | metadata:
11 | name: test-regorule
12 | status:
13 | readyReplicas: 1
14 | replicas: 1
15 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/Chart.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v2
3 | name: opa-operator
4 | version: "0.0.0-dev"
5 | appVersion: "0.0.0-dev"
6 | description: The Stackable Operator for OpenPolicyAgent
7 | home: https://github.com/stackabletech/opa-operator
8 | maintainers:
9 | - name: Stackable
10 | url: https://www.stackable.tech
11 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/simple-rule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: ConfigMap
4 | metadata:
5 | name: test
6 | labels:
7 | opa.stackable.tech/bundle: "true"
8 | data:
9 | test.rego: |
10 | package test
11 |
12 | hello if {
13 | true
14 | }
15 |
16 | world if {
17 | false
18 | }
19 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "local>stackabletech/.github:renovate-config"
5 | ],
6 | "ignorePaths": [".github/workflows/build.yaml", ".github/workflows/general_daily_security.yml", ".github/workflows/integration-test.yml", ".github/workflows/pr_pre-commit.yaml"]
7 | }
8 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/02-create-configmap-with-prepared-logs.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: >
6 | kubectl create configmap prepared-logs
7 | --from-file=prepared-opa-logs.json
8 | --from-file=prepared-bundle-builder-logs.tracing-rs.json
9 | --namespace=$NAMESPACE
10 |
--------------------------------------------------------------------------------
/rust/user-info-fetcher/README.md:
--------------------------------------------------------------------------------
1 | # User info fetcher
2 |
3 | Fetches user metadata from a directory service, and exposes it in a normalized format for OPA rules to read.
4 |
5 | It is deployed by the Stackable OPA Operator, and is not recommended for standalone use.
6 |
7 | ## Supported backends
8 |
9 | - `none` - Dummy backend
10 | - `keycloak` - Keycloak
11 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/10-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 600
5 | commands:
6 | - script: kubectl -n $NAMESPACE rollout status daemonset test-opa-server-default --timeout 600s
7 | - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa --timeout 600s
8 |
--------------------------------------------------------------------------------
/.readme/partials/borrowed/overview_blurb.md.j2:
--------------------------------------------------------------------------------
1 |
2 | It is part of the Stackable Data Platform, a curated selection of the best open source data apps like Apache Kafka, Apache Druid, Trino or Apache Spark, [all](#other-operators) working together seamlessly. Based on Kubernetes, it runs everywhere – [on prem or in the cloud](#supported-platforms).
3 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/operations/index.adoc:
--------------------------------------------------------------------------------
1 |
2 | = Operations
3 |
4 | This section of the documentation is intended for the operations teams that maintain a Stackable Data Platform installation.
5 |
6 | Please read the xref:concepts:operations/index.adoc[Concepts page on Operations] that contains the necessary details to operate the platform in a production environment.
7 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/05-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | commands:
5 | - script: >-
6 | kubectl exec --namespace=$NAMESPACE opa-test-runner-0 --
7 | python /tmp/test_log_aggregation.py -n $NAMESPACE
8 | - script: >-
9 | kubectl exec --namespace=$NAMESPACE opa-test-runner-0 --
10 | /tmp/test_log_aggregation.sh
11 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | debug/
2 | target/
3 | **/*.rs.bk
4 |
5 | .idea/
6 | *.iws
7 |
8 | Cargo.nix
9 | crate-hashes.json
10 | result
11 | image.tar
12 |
13 | # We do NOT want to ignore .git because we use the `built` crate to gather the current git commit hash at built time
14 | # This means we need the .git directory in our Docker image, it will be thrown away and won't be included in the final image
15 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/20-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 180
5 | commands:
6 | - script: kubectl -n $NAMESPACE wait --for=condition=stopped opaclusters.opa.stackable.tech/test-opa --timeout 181s
7 | ---
8 | apiVersion: apps/v1
9 | kind: DaemonSet
10 | metadata:
11 | name: test-opa-server-default
12 | status:
13 | numberReady: 0
14 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/40-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 300
5 | commands:
6 | # Wait for at least one pod come back up
7 | - script: sleep 5 && kubectl -n $NAMESPACE get pods | grep "opa-server-default-*" | grep Running
8 | - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa --timeout 301s
9 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/00-patch-ns.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if test_scenario['values']['openshift'] == 'true' %}
2 | # see https://github.com/stackabletech/issues/issues/566
3 | ---
4 | apiVersion: kuttl.dev/v1beta1
5 | kind: TestStep
6 | commands:
7 | - script: kubectl patch namespace $NAMESPACE -p '{"metadata":{"labels":{"pod-security.kubernetes.io/enforce":"privileged"}}}'
8 | timeout: 120
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/00-patch-ns.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if test_scenario['values']['openshift'] == 'true' %}
2 | # see https://github.com/stackabletech/issues/issues/566
3 | ---
4 | apiVersion: kuttl.dev/v1beta1
5 | kind: TestStep
6 | commands:
7 | - script: kubectl patch namespace $NAMESPACE -p '{"metadata":{"labels":{"pod-security.kubernetes.io/enforce":"privileged"}}}'
8 | timeout: 120
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/resources/00-patch-ns.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if test_scenario['values']['openshift'] == 'true' %}
2 | # see https://github.com/stackabletech/issues/issues/566
3 | ---
4 | apiVersion: kuttl.dev/v1beta1
5 | kind: TestStep
6 | commands:
7 | - script: kubectl patch namespace $NAMESPACE -p '{"metadata":{"labels":{"pod-security.kubernetes.io/enforce":"privileged"}}}'
8 | timeout: 120
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/00-patch-ns.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if test_scenario['values']['openshift'] == 'true' %}
2 | # see https://github.com/stackabletech/issues/issues/566
3 | ---
4 | apiVersion: kuttl.dev/v1beta1
5 | kind: TestStep
6 | commands:
7 | - script: kubectl patch namespace $NAMESPACE -p '{"metadata":{"labels":{"pod-security.kubernetes.io/enforce":"privileged"}}}'
8 | timeout: 120
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/00-patch-ns.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if test_scenario['values']['openshift'] == 'true' %}
2 | # see https://github.com/stackabletech/issues/issues/566
3 | ---
4 | apiVersion: kuttl.dev/v1beta1
5 | kind: TestStep
6 | commands:
7 | - script: kubectl patch namespace $NAMESPACE -p '{"metadata":{"labels":{"pod-security.kubernetes.io/enforce":"privileged"}}}'
8 | timeout: 120
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/30-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 180
5 | commands:
6 | - script: kubectl -n $NAMESPACE wait --for=condition=reconciliationPaused opaclusters.opa.stackable.tech/test-opa --timeout 181s
7 | ---
8 | apiVersion: apps/v1
9 | kind: DaemonSet
10 | metadata:
11 | name: test-opa-server-default
12 | status:
13 | numberReady: 0
14 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/00-patch-ns.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if test_scenario['values']['openshift'] == 'true' %}
2 | # see https://github.com/stackabletech/issues/issues/566
3 | ---
4 | apiVersion: kuttl.dev/v1beta1
5 | kind: TestStep
6 | commands:
7 | - script: kubectl patch namespace $NAMESPACE -p '{"metadata":{"labels":{"pod-security.kubernetes.io/enforce":"privileged"}}}'
8 | timeout: 120
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/00-patch-ns.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if test_scenario['values']['openshift'] == 'true' %}
2 | # see https://github.com/stackabletech/issues/issues/566
3 | ---
4 | apiVersion: kuttl.dev/v1beta1
5 | kind: TestStep
6 | commands:
7 | - script: kubectl patch namespace $NAMESPACE -p '{"metadata":{"labels":{"pod-security.kubernetes.io/enforce":"privileged"}}}'
8 | timeout: 120
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/00-patch-ns.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if test_scenario['values']['openshift'] == 'true' %}
2 | # see https://github.com/stackabletech/issues/issues/566
3 | ---
4 | apiVersion: kuttl.dev/v1beta1
5 | kind: TestStep
6 | commands:
7 | - script: kubectl patch namespace $NAMESPACE -p '{"metadata":{"labels":{"pod-security.kubernetes.io/enforce":"privileged"}}}'
8 | timeout: 120
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/20-load-ldif.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: |
6 | # Load group structure and memberships (use -c to continue on errors like "already exists")
7 | kubectl exec -n $NAMESPACE test-openldap-0 -- \
8 | ldapadd -c -x -H ldap://localhost:1389 -D "cn=ldapadmin,dc=example,dc=org" -w ldapadminpassword -f /tmp/ldifs/add-groups.ldif
9 |
--------------------------------------------------------------------------------
/scripts/render_readme.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | # Check if jinja2 is there
5 | if ! command -v jinja2 &> /dev/null
6 | then
7 | echo "jinja2 could not be found. Use 'pip install jinja2-cli' to install it."
8 | exit 1
9 | fi
10 |
11 | SCRIPT_DIR=$(dirname "$0")
12 | cd "$SCRIPT_DIR/../.readme"
13 | jinja2 README.md.j2 -o ../README.md
14 | cd ..
15 |
16 | python3 scripts/ensure_one_trailing_newline.py README.md
17 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/32-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-env-overrides
6 | commands:
7 | # Role level env var
8 | - script: kubectl exec -n $NAMESPACE -c opa svc/test-opa-server -- env | grep SERVER_ROLE_LEVEL_ENV_VAR
9 | # RoleGroup level env var
10 | - script: kubectl exec -n $NAMESPACE -c opa svc/test-opa-server -- env | grep SERVER_ROLE_GROUP_LEVEL_ENV_VAR
11 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/reference/index.adoc:
--------------------------------------------------------------------------------
1 | = Reference
2 |
3 | Consult the reference documentation section to find exhaustive information on:
4 |
5 | * Descriptions and default values of all properties in the CRDs used by this operator in the xref:reference/crds.adoc[].
6 | * The properties in the xref:reference/discovery.adoc[].
7 | * The xref:reference/commandline-parameters.adoc[] and xref:reference/environment-variables.adoc[] accepted by the operator.
8 |
--------------------------------------------------------------------------------
/rust/regorule-library/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "stackable-opa-regorule-library"
3 | description = "Contains Stackable's library of common regorules"
4 | version.workspace = true
5 | authors.workspace = true
6 | license.workspace = true
7 | edition.workspace = true
8 | repository.workspace = true
9 | publish = false
10 |
11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12 |
13 | [dependencies]
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | blank_issues_enabled: true
3 | contact_links:
4 | - name: 🙋🏾 Question
5 | about: Use this to ask a question about this project
6 | url: https://github.com/orgs/stackabletech/discussions/new?category=q-a
7 | - name: 🚀 Feature Requests and other things
8 | about: Open an issue with your feature request or any other issue not covered elsewhere
9 | url: https://github.com/stackabletech/opa-operator/issues/new
10 |
--------------------------------------------------------------------------------
/.hadolint.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | ignored:
3 | # Warning: Use the -y switch to avoid manual input dnf install -y
4 | # https://github.com/hadolint/hadolint/wiki/DL3038
5 | # Reason: We set `assumeyes=True` in dnf.conf in our base image
6 | - DL3038
7 |
8 | # Warning: Specify version with dnf install -y -
9 | # https://github.com/hadolint/hadolint/wiki/DL3041
10 | # Reason: It's good advice, but we're not set up to pin versions just yet
11 | - DL3041
12 |
--------------------------------------------------------------------------------
/deploy/DO_NOT_EDIT.md:
--------------------------------------------------------------------------------
1 | # DO NOT EDIT
2 |
3 | These Helm charts and manifests are automatically generated.
4 | Please do not edit anything except for files explicitly mentioned below in this
5 | directory manually.
6 |
7 | The following files are ok to edit:
8 |
9 | - helm/opa-operator/templates/roles.yaml
10 | - helm/opa-operator/values.yaml
11 |
12 | The details are in-motion but check this repository for a few details:
13 |
14 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/index.adoc:
--------------------------------------------------------------------------------
1 | = Usage guide
2 | :description: Learn how to configure and use the Stackable OPA operator. Follow the guide for setup instructions and advanced configurations.
3 | :page-aliases: usage.adoc
4 |
5 | This section helps you to use and configure the Stackable operator for OPA in various ways.
6 | You should already be familiar with how to set up a basic instance.
7 | Follow the xref:getting_started/index.adoc[] guide to learn how to set up a basic instance.
8 |
--------------------------------------------------------------------------------
/rustfmt.toml:
--------------------------------------------------------------------------------
1 | # This file includes unstable features, so you need to run "cargo +nightly fmt" to format your code.
2 | # It's also ok to use the stable toolchain by simple running "cargo fmt", but using the nigthly formatter is prefered.
3 |
4 | # https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rustfmt-style-edition.html
5 | style_edition = "2024"
6 | imports_granularity = "Crate"
7 | group_imports = "StdExternalCrate"
8 | reorder_impl_items = true
9 | use_field_init_shorthand = true
10 |
--------------------------------------------------------------------------------
/tests/release.yaml:
--------------------------------------------------------------------------------
1 | # Contains all operators required to run the test suite.
2 | ---
3 | releases:
4 | # Do not change the name of the release as it's referenced from run-tests
5 | tests:
6 | releaseDate: 1970-01-01
7 | description: Integration test
8 | products:
9 | commons:
10 | operatorVersion: 0.0.0-dev
11 | secret:
12 | operatorVersion: 0.0.0-dev
13 | listener:
14 | operatorVersion: 0.0.0-dev
15 | opa:
16 | operatorVersion: 0.0.0-dev
17 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/01-install-opa-vector-aggregator.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: >-
6 | helm install opa-vector-aggregator vector
7 | --namespace $NAMESPACE
8 | --version 0.45.0
9 | --repo https://helm.vector.dev
10 | --values opa-vector-aggregator-values.yaml
11 | ---
12 | apiVersion: v1
13 | kind: ConfigMap
14 | metadata:
15 | name: opa-vector-aggregator-discovery
16 | data:
17 | ADDRESS: opa-vector-aggregator:6123
18 |
--------------------------------------------------------------------------------
/.yamllint.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | extends: default
3 |
4 | ignore: |
5 | deploy/helm/**/templates
6 |
7 | rules:
8 | line-length: disable
9 | truthy:
10 | check-keys: false
11 | comments:
12 | min-spaces-from-content: 1 # Needed due to https://github.com/adrienverge/yamllint/issues/443
13 | indentation:
14 | indent-sequences: consistent
15 | comments-indentation: disable # This is generally useless and interferes with commented example values
16 | braces:
17 | max-spaces-inside: 1
18 | max-spaces-inside-empty: 0
19 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "type": "lldb",
6 | "request": "launch",
7 | "name": "Debug operator binary",
8 | "cargo": {
9 | "args": ["build"],
10 | "filter": {
11 | "name": "stackable-{[ operator.name }]",
12 | "kind": "bin"
13 | }
14 | },
15 | "args": ["run"],
16 | "cwd": "${workspaceFolder}"
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/11-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # This test checks if the containerdebug-state.json file is present and valid
3 | apiVersion: kuttl.dev/v1beta1
4 | kind: TestAssert
5 | timeout: 600
6 | commands:
7 | - script: |
8 | FIRST_OPA_POD=$(kubectl get -n $NAMESPACE pods --field-selector=status.phase=Running --selector app.kubernetes.io/instance=test-opa -o jsonpath='{.items[0].metadata.name}')
9 | kubectl exec -n $NAMESPACE --container opa $FIRST_OPA_POD -- cat /stackable/log/containerdebug-state.json | jq --exit-status '"valid JSON"'
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/30-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-regorule
6 | commands:
7 | {% if test_scenario['values']['use-tls'] == "true" %}
8 | - script: kubectl exec -n $NAMESPACE test-opa-0 -- python /tmp/30_test-regorule.py -u "https://test-opa-server.$NAMESPACE.svc.cluster.local:8443/v1/data/test"
9 | {% else %}
10 | - script: kubectl exec -n $NAMESPACE test-opa-0 -- python /tmp/30_test-regorule.py -u "http://test-opa-server.$NAMESPACE.svc.cluster.local:8081/v1/data/test"
11 | {% endif %}
12 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/operations/pod-disruptions.adoc:
--------------------------------------------------------------------------------
1 | = Allowed Pod disruptions
2 |
3 | For OPA clusters, the operator does not deploy any PodDisruptionBudgets (PDBs), as there is one instance per Kubernetes node running (Daemonset).
4 | When a Kubernetes node gets drained to gracefully shut it down, the OPA Pod get's evicted - there is no point in blocking the eviction.
5 | In case the OPA Pod terminated before the products depending on OPA (e.g. Trino coordinator) on the same node, the products can still use the OPA Service, as it routes to OPA Pods running on other Kubernetes nodes.
6 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/31-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-metrics
6 | commands:
7 | {% if test_scenario['values']['use-tls'] == "true" %}
8 | - script: kubectl exec -n $NAMESPACE test-opa-0 -- python /tmp/30_test-metrics.py -u "https://test-opa-server-default-metrics.$NAMESPACE.svc.cluster.local:8443/metrics"
9 | {% else %}
10 | - script: kubectl exec -n $NAMESPACE test-opa-0 -- python /tmp/30_test-metrics.py -u "http://test-opa-server-default-metrics.$NAMESPACE.svc.cluster.local:8081/metrics"
11 | {% endif %}
12 |
--------------------------------------------------------------------------------
/tests/interu.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | runners:
3 | amd64:
4 | platform: aks-1.32
5 | ttl: 6h
6 | node-groups:
7 | - name: default
8 | arch: amd64
9 | size: medium
10 | disk-gb: 100
11 | nodes: 3
12 |
13 | profiles:
14 | # TODO (@Techassi): This will be enabled later
15 | # schedule:
16 | # strategy: use-runner
17 | # runner: amd64
18 | # options:
19 | # beku-parallelism: 2
20 | smoke-latest:
21 | strategy: use-runner
22 | runner: amd64
23 | options:
24 | beku-parallelism: 2
25 | beku-test-suite: smoke-latest
26 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/30-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: install-opa
6 | timeout: 300
7 | commands:
8 | - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa-groupofnames-tls --timeout 301s
9 | - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa-groupofnames-notls --timeout 301s
10 | - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa-posixgroup-tls --timeout 301s
11 |
--------------------------------------------------------------------------------
/rust/regorule-library/src/userinfo/v1.rego:
--------------------------------------------------------------------------------
1 | package stackable.opa.userinfo.v1
2 |
3 | # Lookup by (human-readable) username
4 | userInfoByUsername(username) := http.send({
5 | "method": "POST",
6 | "url": "http://127.0.0.1:9476/user",
7 | "body": {"username": username},
8 | "headers": {"Content-Type": "application/json"},
9 | "raise_error": true
10 | }).body
11 |
12 | # Lookup by stable user identifier
13 | userInfoById(id) := http.send({
14 | "method": "POST",
15 | "url": "http://127.0.0.1:9476/user",
16 | "body": {"id": id},
17 | "headers": {"Content-Type": "application/json"},
18 | "raise_error": true
19 | }).body
20 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/.helmignore:
--------------------------------------------------------------------------------
1 | # =============
2 | # This file is automatically generated from the templates in stackabletech/operator-templating
3 | # DON'T MANUALLY EDIT THIS FILE
4 | # =============
5 |
6 | # Patterns to ignore when building packages.
7 | # This supports shell glob matching, relative path matching, and
8 | # negation (prefixed with !). Only one pattern per line.
9 | .DS_Store
10 | # Common VCS dirs
11 | .git/
12 | .gitignore
13 | .bzr/
14 | .bzrignore
15 | .hg/
16 | .hgignore
17 | .svn/
18 | # Common backup files
19 | *.swp
20 | *.bak
21 | *.tmp
22 | *.orig
23 | *~
24 | # Various IDEs
25 | .project
26 | .idea/
27 | *.tmproj
28 | .vscode/
29 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/09-install-secretclass.yaml.j2:
--------------------------------------------------------------------------------
1 | {% if test_scenario['values']['use-tls'] == "true" %}
2 | ---
3 | apiVersion: kuttl.dev/v1beta1
4 | kind: TestStep
5 | commands:
6 | - script: |
7 | kubectl apply -n $NAMESPACE -f - << EOF
8 | ---
9 | apiVersion: secrets.stackable.tech/v1alpha1
10 | kind: SecretClass
11 | metadata:
12 | name: opa-tls-$NAMESPACE
13 | spec:
14 | backend:
15 | autoTls:
16 | ca:
17 | autoGenerate: true
18 | secret:
19 | name: opa-tls-ca
20 | namespace: $NAMESPACE
21 | EOF
22 | {% endif %}
23 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/templates/service.yaml:
--------------------------------------------------------------------------------
1 |
2 | ---
3 | apiVersion: v1
4 | kind: Service
5 | metadata:
6 | # Note(@sbernauer): We could also call the Service something like
7 | # "product-operator-conversion-webhook". However, in the future we will have more webhooks, and
8 | # it seems like an overkill to have a dedicated Service per webhook.
9 | name: {{ include "operator.fullname" . }}
10 | labels:
11 | {{- include "operator.labels" . | nindent 4 }}
12 | spec:
13 | selector:
14 | {{- include "operator.selectorLabels" . | nindent 6 }}
15 | ports:
16 | - name: conversion-webhook
17 | protocol: TCP
18 | port: 8443
19 | targetPort: 8443
20 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/50-assert.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | metadata:
5 | name: test-regorule
6 | commands:
7 | - script: kubectl exec -n $NAMESPACE test-regorule-0 -- python /tmp/test-regorule.py -u 'http://test-opa-groupofnames-tls-server:8081/v1/data/test' -t groupofnames-tls
8 | - script: kubectl exec -n $NAMESPACE test-regorule-0 -- python /tmp/test-regorule.py -u 'http://test-opa-groupofnames-notls-server:8081/v1/data/test' -t groupofnames-notls
9 | - script: kubectl exec -n $NAMESPACE test-regorule-0 -- python /tmp/test-regorule.py -u 'http://test-opa-posixgroup-tls-server:8081/v1/data/test' -t posixgroup-tls
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/normal-issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Normal issue
3 | about: This is just a normal empty issue with a simple checklist
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Issue checklist
11 |
12 | This is a simple checklist of things to bear in mind when creating a new issue.
13 |
14 | - [ ] Describe the use-case, as far is possible. For instance, using the pattern "As a XXXX, I would like XXXX to be able to do XXXX" helps to identify the feature as well as the problem it is intended to address.
15 | - [ ] Indicate an approximate level of importance and urgency.
16 | - [ ] Indicate if there is a known work-around until such time as the issue has been implemented.
17 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/test_log_aggregation.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # we should distinguish between cases where the variable is not set
4 | # if the service or container is not found (which we don't want), and where
5 | # grep does not find anything (which is what we *do* want).
6 |
7 | set -Eeuo pipefail
8 |
9 | # do not mask kubectl errors, but the right-hand group must always be true
10 | # so the test does not fail on the grep
11 | DECISION_LOGS=$(kubectl logs service/test-opa-server -c opa | { grep "decision_id" || true; })
12 |
13 | if [ -n "$DECISION_LOGS" ];
14 | then
15 | echo "Error: Decision logs printed to console";
16 | exit 1;
17 | fi
18 |
19 | echo "Test successful!";
20 |
--------------------------------------------------------------------------------
/.readme/README.md.j2:
--------------------------------------------------------------------------------
1 | {%- set title="Stackable Operator for Open Policy Agent" -%}
2 | {%- set operator_name="opa" -%}
3 | {%- set operator_docs_slug="opa" -%}
4 | {%- set related_reading_links=[] -%}
5 |
6 | {% filter trim %}
7 | {%- include "partials/borrowed/header.md.j2" -%}
8 | {% endfilter %}
9 |
10 | {% filter trim %}
11 | {%- include "partials/borrowed/links.md.j2" -%}
12 | {% endfilter %}
13 |
14 | {% filter trim %}
15 | {%- include "partials/main.md.j2" -%}
16 | {% endfilter %}
17 |
18 | {% filter trim %}
19 | {%- include "partials/borrowed/footer.md.j2" -%}
20 | {% endfilter %}
21 |
22 | {% filter trim %}
23 | {%- include "partials/borrowed/related_reading.md.j2" -%}
24 | {% endfilter %}
25 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/listenerclass.adoc:
--------------------------------------------------------------------------------
1 | = Service exposition with ListenerClasses
2 |
3 | OPA offers an API.
4 | The operator deploys a Service called `` (where `` is the name of the OpaCluster) through which OPA can be reached.
5 |
6 | This service can have three different types: `cluster-internal`, `external-unstable` and `external-stable`.
7 | Read more about the types in the xref:concepts:service-exposition.adoc[service exposition] documentation at platform level.
8 |
9 | This is how the ListenerClass is configured:
10 |
11 | [source,yaml]
12 | ----
13 | spec:
14 | clusterConfig:
15 | listenerClass: cluster-internal # <1>
16 | ----
17 | <1> The default `cluster-internal` setting.
18 |
--------------------------------------------------------------------------------
/.github/workflows/general_daily_security.yml:
--------------------------------------------------------------------------------
1 | # =============
2 | # This file is automatically generated from the templates in stackabletech/operator-templating
3 | # DON'T MANUALLY EDIT THIS FILE
4 | # =============
5 | ---
6 | name: Daily Security Audit
7 |
8 | on:
9 | schedule:
10 | - cron: '15 4 * * *'
11 | workflow_dispatch:
12 |
13 | permissions: {}
14 |
15 | jobs:
16 | audit:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
20 | with:
21 | persist-credentials: false
22 | - uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2.0.0
23 | with:
24 | token: ${{ secrets.GITHUB_TOKEN }}
25 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/20-install-test-regorule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apps/v1
3 | kind: StatefulSet
4 | metadata:
5 | name: test-regorule
6 | labels:
7 | app: test-regorule
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: test-regorule
13 | template:
14 | metadata:
15 | labels:
16 | app: test-regorule
17 | spec:
18 | containers:
19 | - name: test-regorule
20 | image: oci.stackable.tech/sdp/testing-tools:0.2.0-stackable0.0.0-dev
21 | stdin: true
22 | tty: true
23 | resources:
24 | requests:
25 | memory: "128Mi"
26 | cpu: "512m"
27 | limits:
28 | memory: "128Mi"
29 | cpu: "1"
30 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/templates/_maintenance.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | Create a list of maintenance related env vars.
3 | */}}
4 | {{- define "maintenance.envVars" -}}
5 | {{- with .Values.maintenance }}
6 | {{- if not .endOfSupportCheck.enabled }}
7 | - name: EOS_DISABLED
8 | value: "true"
9 | {{- end }}
10 | {{- if and .endOfSupportCheck.enabled .endOfSupportCheck.mode }}
11 | - name: EOS_CHECK_MODE
12 | value: {{ .endOfSupportCheck.mode }}
13 | {{ end }}
14 | {{- if and .endOfSupportCheck.enabled .endOfSupportCheck.interval }}
15 | - name: EOS_INTERVAL
16 | value: {{ .endOfSupportCheck.interval }}
17 | {{ end }}
18 | {{- if not .customResourceDefinitions.maintain }}
19 | - name: DISABLE_CRD_MAINTENANCE
20 | value: "true"
21 | {{- end }}
22 | {{- end }}
23 | {{- end }}
24 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/20-install-test-regorule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apps/v1
3 | kind: StatefulSet
4 | metadata:
5 | name: test-regorule
6 | labels:
7 | app: test-regorule
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: test-regorule
13 | template:
14 | metadata:
15 | labels:
16 | app: test-regorule
17 | spec:
18 | containers:
19 | - name: test-regorule
20 | image: oci.stackable.tech/sdp/testing-tools:0.2.0-stackable0.0.0-dev
21 | stdin: true
22 | tty: true
23 | resources:
24 | requests:
25 | memory: "128Mi"
26 | cpu: "512m"
27 | limits:
28 | memory: "128Mi"
29 | cpu: "1"
30 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/openldap-user-info/40-install-test-regorule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apps/v1
3 | kind: StatefulSet
4 | metadata:
5 | name: test-regorule
6 | labels:
7 | app: test-regorule
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: test-regorule
13 | template:
14 | metadata:
15 | labels:
16 | app: test-regorule
17 | spec:
18 | containers:
19 | - name: test-regorule
20 | image: oci.stackable.tech/sdp/testing-tools:0.2.0-stackable0.0.0-dev
21 | stdin: true
22 | tty: true
23 | resources:
24 | requests:
25 | memory: "128Mi"
26 | cpu: "512m"
27 | limits:
28 | memory: "128Mi"
29 | cpu: "1"
30 |
--------------------------------------------------------------------------------
/nix/README.md:
--------------------------------------------------------------------------------
1 |
5 |
6 | # Updating nix dependencies
7 |
8 | ## Run the following for an operator
9 |
10 | > [!NOTE]
11 | > We track the `master` branch of crate2nix as that is relatively up to date, but the releases are infrequent.
12 |
13 | ```shell
14 | niv update crate2nix
15 | niv update nixpkgs
16 | niv update beku.py -b X.Y.Z # Using the release tag
17 | ```
18 |
19 | ### Test
20 |
21 | - Run make `regenerate-nix` to ensure crate2nix works
22 | - Run a smoke test to ensure beku.py works.
23 | - Run `make run-dev` to ensure nixpkgs are fine.
24 |
25 | ## Update operator-templating
26 |
27 | Do the same as above, but from `template/`
28 |
--------------------------------------------------------------------------------
/.readme/partials/borrowed/documentation.md.j2:
--------------------------------------------------------------------------------
1 |
2 | ## Documentation
3 |
4 | The stable documentation for this operator can be found in our [Stackable Data Platform documentation](https://docs.stackable.tech/home/stable/{{operator_docs_slug}}).
5 | If you are interested in the most recent state of this repository, check out the [nightly docs](https://docs.stackable.tech/home/nightly/{{operator_docs_slug}}) instead.
6 |
7 | The documentation for all Stackable products can be found at [docs.stackable.tech](https://docs.stackable.tech).
8 |
9 | If you have a question about the Stackable Data Platform, contact us via our [homepage](https://stackable.tech/) or ask a public question in our [Discussions forum](https://github.com/orgs/stackabletech/discussions).
10 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/20-install-test-regorule.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apps/v1
3 | kind: StatefulSet
4 | metadata:
5 | name: test-regorule
6 | labels:
7 | app: test-regorule
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | app: test-regorule
13 | template:
14 | metadata:
15 | labels:
16 | app: test-regorule
17 | spec:
18 | serviceAccountName: test-sa
19 | containers:
20 | - name: test-regorule
21 | image: oci.stackable.tech/sdp/testing-tools:0.2.0-stackable0.0.0-dev
22 | stdin: true
23 | tty: true
24 | resources:
25 | requests:
26 | memory: "128Mi"
27 | cpu: "512m"
28 | limits:
29 | memory: "128Mi"
30 | cpu: "1"
31 |
--------------------------------------------------------------------------------
/.readme/partials/main.md.j2:
--------------------------------------------------------------------------------
1 | This is a Kubernetes operator to manage [Open Policy Agent](https://www.openpolicyagent.org/) servers.
2 |
3 | {% filter trim %}
4 | {%- include "partials/borrowed/overview_blurb.md.j2" -%}
5 | {% endfilter %}
6 |
7 | ## Installation
8 |
9 | You can install the operator using [stackablectl or helm](https://docs.stackable.tech/home/stable/{{operator_name}}/getting_started/installation).
10 |
11 | Read on to get started with it, or see it in action in one of our [demos](https://stackable.tech/en/demos/).
12 |
13 | ## Getting Started
14 |
15 | You can follow this [tutorial](https://docs.stackable.tech/home/stable/{{operator_name}}/getting_started/first_steps) .
16 |
17 | {% filter trim %}
18 | {%- include "partials/borrowed/documentation.md.j2" -%}
19 | {% endfilter %}
20 |
--------------------------------------------------------------------------------
/scripts/generate-manifests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # This script reads a Helm chart from deploy/helm/opa-operator and
3 | # generates manifest files into deploy/manifestss
4 | set -e
5 |
6 | tmp=$(mktemp -d ./manifests-XXXXX)
7 |
8 | helm template --output-dir "$tmp" \
9 | --include-crds \
10 | --name-template opa-operator \
11 | deploy/helm/opa-operator
12 |
13 | for file in "$tmp"/opa-operator/*/*; do
14 | yq eval -i 'del(.. | select(has("app.kubernetes.io/managed-by")) | ."app.kubernetes.io/managed-by")' /dev/stdin < "$file"
15 | yq eval -i 'del(.. | select(has("helm.sh/chart")) | ."helm.sh/chart")' /dev/stdin < "$file"
16 | sed -i '/# Source: .*/d' "$file"
17 | done
18 |
19 | cp -r "$tmp"/opa-operator/*/* deploy/manifests/
20 |
21 | rm -rf "$tmp"
22 |
--------------------------------------------------------------------------------
/.readme/partials/borrowed/links.md.j2:
--------------------------------------------------------------------------------
1 |
2 | [](https://GitHub.com/stackabletech/{{operator_name}}-operator/graphs/commit-activity)
3 | [](https://docs.stackable.tech/home/stable/contributor/index.html)
4 | [](./LICENSE)
5 |
6 | [Documentation](https://docs.stackable.tech/home/stable/{{operator_docs_slug}}) {% if quickstart_link %}| [Quickstart]({{quickstart_link}}) {% endif %}| [Stackable Data Platform](https://stackable.tech/) | [Platform Docs](https://docs.stackable.tech/) | [Discussions](https://github.com/orgs/stackabletech/discussions) | [Discord](https://discord.gg/7kZ3BNnCAF)
7 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/monitoring.adoc:
--------------------------------------------------------------------------------
1 | = Monitoring
2 | :description: The managed OPA instances are automatically configured to export Prometheus metrics.
3 |
4 | The managed OPA instances are automatically configured to export Prometheus metrics.
5 | See xref:operators:monitoring.adoc[] for more details.
6 |
7 | You can read the list of exported metrics in the https://www.openpolicyagent.org/docs/monitoring#prometheus[OPA monitoring documentation].
8 |
9 | Especially worth mentioning are the https://www.openpolicyagent.org/docs/monitoring#status-metrics[Status metrics], which contain e.g. `opa_info`, `bundle_loaded_counter` and `bundle_failed_load_counter`.
10 | You can use these to create a dashboard to see any bundle loading problems at a glance.
11 | You can also set up alerts to get notified proactively.
12 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/templates/roles-opa-builder.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: {{ .Release.Name }}-opa-bundle-builder-clusterrole
5 | # This role is used for the OPA bundle builder.
6 | # It needs to read ConfigMaps and watch ConfigMaps for changes,
7 | # because the Rego rules that are used to build the bundles are
8 | # stored in the ConfigMaps.
9 | rules:
10 | - apiGroups:
11 | - ""
12 | resources:
13 | - configmaps
14 | verbs:
15 | - get
16 | - watch
17 | - list
18 | {{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }}
19 | - apiGroups:
20 | - security.openshift.io
21 | resources:
22 | - securitycontextconstraints
23 | resourceNames:
24 | - opa-scc
25 | verbs:
26 | - use
27 | {{ end }}
28 |
--------------------------------------------------------------------------------
/rust/regorule-library/README.md:
--------------------------------------------------------------------------------
1 | # Stackable library of shared regorules
2 |
3 | This contains regorules that are shipped by the Stackable Data Platform (SDP) as libraries to help simplify writing authorization rules.
4 |
5 | ## What this is not
6 |
7 | This library should *not* contain rules that only concern one SDP product. Those are the responsibility of their individual operators.
8 |
9 | ## Versioning
10 |
11 | All regorules exposed by this library should be versioned, according to Kubernetes conventions.
12 |
13 | This version covers *breaking changes to the interface*, not the implementation. If a proposed change breaks existing clients,
14 | add a new version. Otherwise, change the latest version inline.
15 |
16 | Ideally, old versions should be implemented on top of newer versions, rather than carry independent implementations.
17 |
--------------------------------------------------------------------------------
/rust/operator-binary/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "stackable-opa-operator"
3 | description = "Stackable Operator for OPA"
4 | version.workspace = true
5 | authors.workspace = true
6 | license.workspace = true
7 | edition.workspace = true
8 | repository.workspace = true
9 | publish = false
10 |
11 | [dependencies]
12 | product-config.workspace = true
13 | stackable-operator.workspace = true
14 |
15 | anyhow.workspace = true
16 | clap.workspace = true
17 | const_format.workspace = true
18 | fnv.workspace = true
19 | futures.workspace = true
20 | indoc.workspace = true
21 | pin-project.workspace = true
22 | semver.workspace = true
23 | serde_json.workspace = true
24 | serde.workspace = true
25 | snafu.workspace = true
26 | strum.workspace = true
27 | tokio.workspace = true
28 | tracing.workspace = true
29 |
30 | [build-dependencies]
31 | built.workspace = true
32 |
--------------------------------------------------------------------------------
/rust/bundle-builder/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "stackable-opa-bundle-builder"
3 | description = "Builds OPA bundles from Kubernetes ConfigMaps"
4 | version.workspace = true
5 | authors.workspace = true
6 | license.workspace = true
7 | edition.workspace = true
8 | repository.workspace = true
9 | publish = false
10 |
11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12 |
13 | [dependencies]
14 | stackable-opa-regorule-library = { path = "../regorule-library" }
15 | stackable-operator.workspace = true
16 |
17 | axum.workspace = true
18 | clap.workspace = true
19 | flate2.workspace = true
20 | futures.workspace = true
21 | hyper.workspace = true
22 | snafu.workspace = true
23 | tar.workspace = true
24 | tokio.workspace = true
25 | tracing.workspace = true
26 |
27 | [build-dependencies]
28 | built.workspace = true
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/01-normal-issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Normal issue
3 | about: This is just a normal empty issue with a simple checklist
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Issue checklist
11 |
12 | This is a simple checklist of things to bear in mind when creating a new issue.
13 |
14 | - [ ] **Describe the use-case**: As far as possible, use the pattern "As a [type of user], I would like [feature/functionality] to be able to do [specific action]." This helps identify the feature and the problem it addresses.
15 | - [ ] **Indicate importance and urgency**: Use a scale (e.g., low, medium, high) to indicate the level of importance and urgency.
16 | - [ ] **Work-around**: If there is a known work-around, describe it briefly.
17 | - [ ] **Environment**: Describe the environment where the issue occurs (e.g., SDP version, K8S version, etc.).
18 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/10-install-opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: opa.stackable.tech/v1alpha1
3 | kind: OpaCluster
4 | metadata:
5 | name: test-opa
6 | spec:
7 | image:
8 | {% if test_scenario['values']['opa-latest'].find(",") > 0 %}
9 | custom: "{{ test_scenario['values']['opa-latest'].split(',')[1] }}"
10 | productVersion: "{{ test_scenario['values']['opa-latest'].split(',')[0] }}"
11 | {% else %}
12 | productVersion: "{{ test_scenario['values']['opa-latest'] }}"
13 | {% endif %}
14 | pullPolicy: IfNotPresent
15 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
16 | clusterConfig:
17 | vectorAggregatorConfigMapName: vector-aggregator-discovery
18 | {% endif %}
19 | servers:
20 | config:
21 | logging:
22 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
23 | roleGroups:
24 | default: {}
25 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/40-restart-opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: opa.stackable.tech/v1alpha1
3 | kind: OpaCluster
4 | metadata:
5 | name: test-opa
6 | spec:
7 | image:
8 | {% if test_scenario['values']['opa-latest'].find(",") > 0 %}
9 | custom: "{{ test_scenario['values']['opa-latest'].split(',')[1] }}"
10 | productVersion: "{{ test_scenario['values']['opa-latest'].split(',')[0] }}"
11 | {% else %}
12 | productVersion: "{{ test_scenario['values']['opa-latest'] }}"
13 | {% endif %}
14 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
15 | clusterConfig:
16 | vectorAggregatorConfigMapName: vector-aggregator-discovery
17 | {% endif %}
18 | clusterOperation:
19 | stopped: false
20 | reconciliationPaused: false
21 | servers:
22 | config:
23 | logging:
24 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
25 | roleGroups:
26 | default: {}
27 |
--------------------------------------------------------------------------------
/.markdownlint.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # All defaults or options can be checked here:
3 | # https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml
4 |
5 | # Default state for all rules
6 | default: true
7 |
8 | # MD013/line-length - Line length
9 | MD013:
10 | # Number of characters
11 | line_length: 9999
12 | # Number of characters for headings
13 | heading_line_length: 9999
14 | # Number of characters for code blocks
15 | code_block_line_length: 9999
16 |
17 | # MD033/no-inline-html
18 | MD033:
19 | allowed_elements: [h1, img, p]
20 |
21 | # MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
22 | MD024:
23 | # Only check sibling headings
24 | siblings_only: true
25 |
26 | # MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading
27 | MD041: false # Github issues and PRs already have titles, and H1 is enormous in the description box.
28 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/20-stop-opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: opa.stackable.tech/v1alpha1
3 | kind: OpaCluster
4 | metadata:
5 | name: test-opa
6 | spec:
7 | image:
8 | {% if test_scenario['values']['opa-latest'].find(",") > 0 %}
9 | custom: "{{ test_scenario['values']['opa-latest'].split(',')[1] }}"
10 | productVersion: "{{ test_scenario['values']['opa-latest'].split(',')[0] }}"
11 | {% else %}
12 | productVersion: "{{ test_scenario['values']['opa-latest'] }}"
13 | {% endif %}
14 | pullPolicy: IfNotPresent
15 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
16 | clusterConfig:
17 | vectorAggregatorConfigMapName: vector-aggregator-discovery
18 | {% endif %}
19 | clusterOperation:
20 | stopped: true
21 | reconciliationPaused: false
22 | servers:
23 | config:
24 | logging:
25 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
26 | roleGroups:
27 | default: {}
28 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/cluster-operation/30-pause-opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: opa.stackable.tech/v1alpha1
3 | kind: OpaCluster
4 | metadata:
5 | name: test-opa
6 | spec:
7 | image:
8 | {% if test_scenario['values']['opa-latest'].find(",") > 0 %}
9 | custom: "{{ test_scenario['values']['opa-latest'].split(',')[1] }}"
10 | productVersion: "{{ test_scenario['values']['opa-latest'].split(',')[0] }}"
11 | {% else %}
12 | productVersion: "{{ test_scenario['values']['opa-latest'] }}"
13 | {% endif %}
14 | pullPolicy: IfNotPresent
15 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
16 | clusterConfig:
17 | vectorAggregatorConfigMapName: vector-aggregator-discovery
18 | {% endif %}
19 | clusterOperation:
20 | stopped: false
21 | reconciliationPaused: true
22 | servers:
23 | config:
24 | logging:
25 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
26 | roleGroups:
27 | default: {}
28 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/30_test-metrics.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import requests
3 | import argparse
4 |
5 | if __name__ == "__main__":
6 | all_args = argparse.ArgumentParser()
7 | all_args.add_argument("-u", "--url", required=True, help="OPA metrics url")
8 | args = vars(all_args.parse_args())
9 |
10 | metrics_url = args["url"]
11 |
12 | # Determine verification setting based on whether TLS is used
13 | if metrics_url.startswith("http://"):
14 | verify = False
15 | protocol = "HTTP"
16 | else:
17 | verify = "/tls/ca.crt"
18 | protocol = "HTTPS"
19 |
20 | response = requests.get(metrics_url, verify=verify)
21 |
22 | assert response.status_code == 200, "Metrics endpoint must return a 200 status code"
23 | assert "bundle_loaded_counter" in response.text, (
24 | f"Metric bundle_loaded_counter should exist in {metrics_url}"
25 | )
26 | print(f"Metrics test ({protocol}) successful!")
27 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/00-misc-setup.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: LimitRange
4 | metadata:
5 | name: limit-request-ratio
6 | spec:
7 | limits:
8 | - type: "Container"
9 | maxLimitRequestRatio:
10 | cpu: 5
11 | memory: 1
12 | ---
13 | kind: Role
14 | apiVersion: rbac.authorization.k8s.io/v1
15 | metadata:
16 | name: test-role
17 | rules:
18 | {% if test_scenario['values']['openshift'] == "true" %}
19 | - apiGroups: ["security.openshift.io"]
20 | resources: ["securitycontextconstraints"]
21 | resourceNames: ["privileged"]
22 | verbs: ["use"]
23 | {% endif %}
24 | ---
25 | apiVersion: v1
26 | kind: ServiceAccount
27 | metadata:
28 | name: test-sa
29 | ---
30 | kind: RoleBinding
31 | apiVersion: rbac.authorization.k8s.io/v1
32 | metadata:
33 | name: test-rb
34 | subjects:
35 | - kind: ServiceAccount
36 | name: test-sa
37 | roleRef:
38 | kind: Role
39 | name: test-role
40 | apiGroup: rbac.authorization.k8s.io
41 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Helm Chart for Stackable Operator for OpenPolicyAgent
3 |
4 | This Helm Chart can be used to install Custom Resource Definitions and the Operator for OpenPolicyAgent provided by Stackable.
5 |
6 | ## Requirements
7 |
8 | - Create a [Kubernetes Cluster](../Readme.md)
9 | - Install [Helm](https://helm.sh/docs/intro/install/)
10 |
11 | ## Install the Stackable Operator for OpenPolicyAgent
12 |
13 | ```bash
14 | # From the root of the operator repository
15 | make compile-chart
16 |
17 | helm install opa-operator deploy/helm/opa-operator
18 | ```
19 |
20 | ## Usage of the CRDs
21 |
22 | The usage of this operator and its CRDs is described in the [documentation](https://docs.stackable.tech/opa/index.html)
23 |
24 | The operator has example requests included in the [`/examples`](https://github.com/stackabletech/opa-operator/tree/main/examples) directory.
25 |
26 | ## Links
27 |
28 |
29 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/getting_started/index.adoc:
--------------------------------------------------------------------------------
1 | = Getting started
2 | :description: Get started with OPA using Stackable Operator. Install, set up OPA, create a policy rule, and query it in Kubernetes.
3 |
4 | This guide gets you started with the OpenPolicyAgent (OPA) using the Stackable operator.
5 | It guides you through the installation of the operator, setting up your first OPA instance, creating a policy rule and querying it.
6 |
7 | == Prerequisites
8 |
9 | You need:
10 |
11 | * a Kubernetes cluster
12 | * kubectl
13 | * curl
14 | * optional: Helm
15 |
16 | Resource sizing depends on cluster type(s), usage and scope, but as a starting point a minimum of the following resources is recommended for this operator:
17 |
18 | * 0.2 cores (e.g. i5 or similar)
19 | * 256MB RAM
20 |
21 | == What's next
22 |
23 | The Guide is divided into two steps:
24 |
25 | * xref:getting_started/installation.adoc[Installing the operator].
26 | * xref:getting_started/first_steps.adoc[Setting up an OPA, a policy rule and querying it].
27 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/policies.adoc:
--------------------------------------------------------------------------------
1 | = Defining policies
2 | :description: Define OPA policies using Rego in ConfigMaps. Mark them with a bundle label and include Rego rules to deploy and manage your policies effectively.
3 | :rego-docs: https://www.openpolicyagent.org/docs/latest/policy-language/
4 |
5 | You can define policies by using Rego, OPAs {rego-docs}[policy language].
6 |
7 | Policy definitions are deployed as ConfigMap resources as described in xref:implementation-notes.adoc[implementation notes].
8 |
9 | Here is an example:
10 |
11 | [source,yaml]
12 | ----
13 | ---
14 | apiVersion: v1
15 | kind: ConfigMap
16 | metadata:
17 | name: test
18 | labels:
19 | opa.stackable.tech/bundle: "true" # <1>
20 | data:
21 | test.rego: | # <2>
22 | package test
23 |
24 | hello if {
25 | true
26 | }
27 |
28 | world if {
29 | false
30 | }
31 | ----
32 | <1> Mark this `ConfigMap` as a bundle source.
33 | <2> `test.rego` is the file name to use inside the bundle for these rules.
34 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/10-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestAssert
3 | timeout: 300
4 | commands:
5 | - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa --timeout 301s
6 | ---
7 | apiVersion: apps/v1
8 | kind: DaemonSet
9 | metadata:
10 | name: test-opa-server-default
11 | spec:
12 | template:
13 | spec:
14 | containers:
15 | - name: opa
16 | resources:
17 | limits:
18 | cpu: 500m
19 | memory: 256Mi
20 | requests:
21 | cpu: 250m
22 | memory: 256Mi
23 | - name: bundle-builder
24 | resources:
25 | limits:
26 | cpu: 200m
27 | memory: 128Mi
28 | requests:
29 | cpu: 100m
30 | memory: 128Mi
31 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
32 | - name: vector
33 | {% endif %}
34 | terminationGracePeriodSeconds: 125 # 2 minutes + 5s safety buffer
35 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/templates/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | {{ if .Values.serviceAccount.create -}}
3 | apiVersion: v1
4 | kind: ServiceAccount
5 | metadata:
6 | name: {{ include "operator.fullname" . }}-serviceaccount
7 | labels:
8 | {{- include "operator.labels" . | nindent 4 }}
9 | {{- with .Values.serviceAccount.annotations }}
10 | annotations:
11 | {{- toYaml . | nindent 4 }}
12 | {{- end }}
13 | ---
14 | apiVersion: rbac.authorization.k8s.io/v1
15 | # This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
16 | kind: ClusterRoleBinding
17 | metadata:
18 | name: {{ include "operator.fullname" . }}-clusterrolebinding
19 | labels:
20 | {{- include "operator.labels" . | nindent 4 }}
21 | subjects:
22 | - kind: ServiceAccount
23 | name: {{ include "operator.fullname" . }}-serviceaccount
24 | namespace: {{ .Release.Namespace }}
25 | roleRef:
26 | kind: ClusterRole
27 | name: {{ include "operator.fullname" . }}-clusterrole
28 | apiGroup: rbac.authorization.k8s.io
29 | {{- end }}
30 |
--------------------------------------------------------------------------------
/rust/user-info-fetcher/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "stackable-opa-user-info-fetcher"
3 | description = "Fetches user metadata from directory services"
4 | version.workspace = true
5 | authors.workspace = true
6 | license.workspace = true
7 | edition.workspace = true
8 | repository.workspace = true
9 | publish = false
10 |
11 | [dependencies]
12 | stackable-opa-operator = { path = "../operator-binary" }
13 | stackable-operator.workspace = true
14 | krb5.workspace = true
15 |
16 | axum.workspace = true
17 | base64.workspace = true
18 | byteorder.workspace = true
19 | clap.workspace = true
20 | futures.workspace = true
21 | hyper.workspace = true
22 | ldap3.workspace = true
23 | moka.workspace = true
24 | native-tls.workspace = true
25 | pin-project.workspace = true
26 | reqwest.workspace = true
27 | rustls-pemfile.workspace = true
28 | semver.workspace = true
29 | serde.workspace = true
30 | serde_json.workspace = true
31 | snafu.workspace = true
32 | tokio.workspace = true
33 | tracing.workspace = true
34 | url.workspace = true
35 | uuid.workspace = true
36 |
37 | [build-dependencies]
38 | built.workspace = true
39 |
--------------------------------------------------------------------------------
/rust/operator-binary/src/operations/graceful_shutdown.rs:
--------------------------------------------------------------------------------
1 | use snafu::{ResultExt, Snafu};
2 | use stackable_opa_operator::crd::{SERVER_GRACEFUL_SHUTDOWN_SAFETY_OVERHEAD, v1alpha1};
3 | use stackable_operator::builder::pod::PodBuilder;
4 |
5 | #[derive(Debug, Snafu)]
6 | pub enum Error {
7 | #[snafu(display("Failed to set terminationGracePeriod"))]
8 | SetTerminationGracePeriod {
9 | source: stackable_operator::builder::pod::Error,
10 | },
11 | }
12 |
13 | pub fn add_graceful_shutdown_config(
14 | merged_config: &v1alpha1::OpaConfig,
15 | pod_builder: &mut PodBuilder,
16 | ) -> Result<(), Error> {
17 | // This must be always set by the merge mechanism, as we provide a default value,
18 | // users can not disable graceful shutdown.
19 | if let Some(graceful_shutdown_timeout) = merged_config.graceful_shutdown_timeout {
20 | pod_builder
21 | .termination_grace_period(
22 | &(graceful_shutdown_timeout + SERVER_GRACEFUL_SHUTDOWN_SAFETY_OVERHEAD),
23 | )
24 | .context(SetTerminationGracePeriodSnafu)?;
25 | }
26 |
27 | Ok(())
28 | }
29 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/resources.adoc:
--------------------------------------------------------------------------------
1 | = Resource requests
2 |
3 | include::concepts:stackable_resource_requests.adoc[]
4 |
5 | A minimal OPA setup consists of 1 Pod per Node (DaemonSet) and has the following https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/[resource requirements] per scheduled Pod:
6 |
7 | * `600m` CPU request
8 | * `1200m` CPU limit
9 | * `512Mi` memory request and limit
10 |
11 | Of course, additional services, require additional resources.
12 | For Stackable components, see the corresponding documentation on further resource requirements.
13 |
14 | Corresponding to the values above, the operator uses the following resource defaults for the main app container:
15 |
16 | [source,yaml]
17 | ----
18 | servers:
19 | roleGroups:
20 | default:
21 | config:
22 | resources:
23 | cpu:
24 | min: 250m
25 | max: 500m
26 | memory:
27 | limit: 256Mi
28 | ----
29 |
30 | WARNING: The default values are _most likely_ not sufficient to run a proper cluster in production.
31 | Please adapt according to your requirements.
32 |
--------------------------------------------------------------------------------
/scripts/docs_templating.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | # Reads a file with variables to insert into templates, and templates all .*.j2 files
5 | # in the 'docs' directory.
6 | #
7 | # dependencies
8 | # pip install jinja2-cli
9 |
10 | docs_dir="$(dirname "$0")/../docs"
11 | templating_vars_file="$docs_dir/templating_vars.yaml"
12 |
13 | # Check if files need templating
14 | if [[ -z $(find "$docs_dir" -name '*.j2') ]];
15 | then
16 | echo "No files need templating, exiting."
17 | exit
18 | fi
19 |
20 | # Check if jinja2 is there
21 | if ! command -v jinja2 &> /dev/null
22 | then
23 | echo "jinja2 could not be found. Use 'pip install jinja2-cli' to install it."
24 | exit 1
25 | fi
26 |
27 | # Check if templating vars file exists
28 | if [[ ! -f "$templating_vars_file" ]];
29 | then
30 | echo "$templating_vars_file does not exist, cannot start templating."
31 | fi
32 |
33 | find "$docs_dir" -name '*.j2' |
34 | while read -r file
35 | do
36 | new_file_name=${file%.j2} # Remove .j2 suffix
37 | echo "templating $new_file_name"
38 | jinja2 "$file" "$templating_vars_file" -o "$new_file_name"
39 | done
40 |
41 | echo "done"
42 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/reference/commandline-parameters.adoc:
--------------------------------------------------------------------------------
1 | = Command line parameters
2 |
3 | This operator accepts the following command line parameters:
4 |
5 | == product-config
6 |
7 | *Default value*: `/etc/stackable/opa-operator/config-spec/properties.yaml`
8 |
9 | *Required*: false
10 |
11 | *Multiple values:* false
12 |
13 | [source]
14 | ----
15 | stackable-opa-operator run --product-config /foo/bar/properties.yaml
16 | ----
17 |
18 | == watch-namespace
19 |
20 | *Default value*: All namespaces
21 |
22 | *Required*: false
23 |
24 | *Multiple values:* false
25 |
26 | The operator **only** watches for resources in the provided namespace `test`:
27 |
28 | [source]
29 | ----
30 | stackable-opa-operator run --watch-namespace test
31 | ----
32 |
33 | == opa-bundle-builder-clusterrole
34 |
35 | *Default value*: `None`. A value is generated automatically by Helm.
36 |
37 | *Required*: false. When not specified, the environment variable `OPA_BUNDLE_BUILDER_CLUSTERROLE` must be present.
38 |
39 | *Multiple values:* false
40 |
41 |
42 | The name of the `ClusterRole` object that is referenced by the OPA pods. This object must exist in the Kubernetes cluster and is created by Helm.
43 |
--------------------------------------------------------------------------------
/tests/kuttl-test.yaml.jinja2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestSuite
4 | testDirs:
5 | {% for testcase in testinput.tests %}
6 | - ./tests/{{ testcase.name }}
7 | {% endfor %}
8 |
9 | startKIND: false
10 | suppress: ["events"]
11 | parallel: 2
12 |
13 | # The timeout (in seconds) is used when namespaces are created or
14 | # deleted, and, if not overridden, in TestSteps, TestAsserts, and
15 | # Commands. If not set, the timeout is 30 seconds by default.
16 | #
17 | # The deletion of a namespace can take a while until all resources,
18 | # especially PersistentVolumeClaims, are gracefully shut down. If the
19 | # timeout is reached in the meantime, even a successful test case is
20 | # considered a failure.
21 | #
22 | # For instance, the termination grace period of the Vector aggregator in
23 | # the logging tests is set to 60 seconds. If there are logs entries
24 | # which could not be forwarded yet to the external aggregator defined in
25 | # the VECTOR_AGGREGATOR environment variable, then the test aggregator
26 | # uses this period of time by trying to forward the events. In this
27 | # case, deleting a namespace with several Pods takes about 90 seconds.
28 | timeout: 300
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/new_version.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: New Version
3 | about: Request support for a new product version
4 | title: "[NEW VERSION]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Which new version of OpenPolicyAgent should we support?
11 |
12 | Please specify the version, version range or version numbers to support, please also add these to the issue title
13 |
14 | ## Additional information
15 |
16 | If possible, provide a link to release notes/changelog
17 |
18 | ## Changes required
19 |
20 | Are there any upstream changes that we need to support?
21 | e.g. new features, changed features, deprecated features etc.
22 |
23 | ## Implementation checklist
24 |
25 |
29 |
30 | - [ ] Update the Docker image
31 | - [ ] Update documentation to include supported version(s)
32 | - [ ] Update and test getting started guide with updated version(s)
33 | - [ ] Update operator to support the new version (if needed)
34 | - [ ] Update integration tests to test use the new versions (in addition or replacing old versions
35 | - [ ] Update examples to use new versions
36 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/pre-release-getting-started-script.md:
--------------------------------------------------------------------------------
1 | ## Check and Update Getting Started Script
2 |
3 |
7 |
8 |
11 |
12 | Part of
13 |
14 | > [!NOTE]
15 | > During a Stackable release we need to check (and optionally update) the
16 | > getting-started scripts to ensure they still work after product and operator
17 | > updates.
18 |
19 | ```shell
20 | # Some of the scripts are in a code/ subdirectory
21 | # pushd docs/modules/superset/examples/getting_started
22 | # pushd docs/modules/superset/examples/getting_started/code
23 | pushd $(fd -td getting_started | grep examples); cd code 2>/dev/null || true
24 |
25 | # Make a fresh cluster (~12 seconds)
26 | kind delete cluster && kind create cluster
27 | ./getting_started.sh stackablectl
28 |
29 | # Make a fresh cluster (~12 seconds)
30 | kind delete cluster && kind create cluster
31 | ./getting_started.sh helm
32 |
33 | popd
34 | ```
35 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/04-install-opa-test-runner.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | kind: Role
3 | apiVersion: rbac.authorization.k8s.io/v1
4 | metadata:
5 | name: logging-role
6 | rules:
7 | - apiGroups: [""]
8 | resources: ["services", "pods", "pods/log"]
9 | verbs: ["get", "list"]
10 | ---
11 | apiVersion: v1
12 | kind: ServiceAccount
13 | metadata:
14 | name: logging-sa
15 | ---
16 | kind: RoleBinding
17 | apiVersion: rbac.authorization.k8s.io/v1
18 | metadata:
19 | name: logging-rb
20 | subjects:
21 | - kind: ServiceAccount
22 | name: logging-sa
23 | roleRef:
24 | kind: Role
25 | name: logging-role
26 | apiGroup: rbac.authorization.k8s.io
27 | ---
28 | apiVersion: apps/v1
29 | kind: StatefulSet
30 | metadata:
31 | name: opa-test-runner
32 | labels:
33 | app: opa-test-runner
34 | spec:
35 | replicas: 1
36 | selector:
37 | matchLabels:
38 | app: opa-test-runner
39 | template:
40 | metadata:
41 | labels:
42 | app: opa-test-runner
43 | spec:
44 | serviceAccountName: logging-sa
45 | containers:
46 | - name: opa-test-runner
47 | image: oci.stackable.tech/sdp/testing-tools:0.2.0-stackable0.0.0-dev
48 | stdin: true
49 | tty: true
50 |
--------------------------------------------------------------------------------
/docs/modules/opa/partials/nav.adoc:
--------------------------------------------------------------------------------
1 | * xref:opa:getting_started/index.adoc[]
2 | ** xref:opa:getting_started/installation.adoc[]
3 | ** xref:opa:getting_started/first_steps.adoc[]
4 | * xref:opa:usage-guide/index.adoc[]
5 | ** xref:opa:usage-guide/listenerclass.adoc[]
6 | ** xref:opa:usage-guide/policies.adoc[]
7 | ** xref:opa:usage-guide/user-info-fetcher.adoc[]
8 | ** xref:opa:usage-guide/resources.adoc[]
9 | ** xref:opa:usage-guide/logging.adoc[]
10 | ** xref:opa:usage-guide/monitoring.adoc[]
11 | ** xref:opa:usage-guide/OpenTelemetry.adoc[]
12 | ** xref:opa:usage-guide/configuration-environment-overrides.adoc[]
13 | ** xref:opa:usage-guide/tls.adoc[]
14 | ** xref:opa:usage-guide/operations/index.adoc[]
15 | *** xref:opa:usage-guide/operations/cluster-operations.adoc[]
16 | // *** xref:hdfs:usage-guide/operations/pod-placement.adoc[] Missing
17 | *** xref:opa:usage-guide/operations/pod-disruptions.adoc[]
18 | *** xref:opa:usage-guide/operations/graceful-shutdown.adoc[]
19 | * xref:opa:reference/index.adoc[]
20 | ** xref:opa:reference/crds.adoc[]
21 | *** {crd-docs}/opa.stackable.tech/opacluster/v1alpha1/[OpaCluster {external-link-icon}^]
22 | ** xref:opa:reference/discovery.adoc[]
23 | ** xref:opa:reference/commandline-parameters.adoc[]
24 | ** xref:opa:reference/environment-variables.adoc[]
25 | * xref:opa:implementation-notes.adoc[]
26 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/configuration-environment-overrides.adoc:
--------------------------------------------------------------------------------
1 | = Configuration & Environment Overrides
2 | :description: Configure OPA with environment variable and Pod overrides.
3 |
4 | The cluster definition also supports overriding configuration properties and environment variables, either per role or per role group, where the more specific override (role group) has precedence over the less specific one (role).
5 |
6 | IMPORTANT: Do not override port numbers.
7 | This will lead to faulty installations.
8 |
9 | == Configuration properties
10 |
11 | Currently, not supported for `config.json`.
12 |
13 | == Environment variables
14 |
15 | Environment variables can be (over)written by adding the `envOverrides` property.
16 |
17 | For example per role group:
18 |
19 | [source,yaml]
20 | ----
21 | servers:
22 | roleGroups:
23 | default:
24 | config: {}
25 | envOverrides:
26 | MY_ENV_VAR: "MY_VALUE"
27 | ----
28 |
29 | or per role:
30 |
31 | [source,yaml]
32 | ----
33 | servers:
34 | envOverrides:
35 | MY_ENV_VAR: "MY_VALUE"
36 | roleGroups:
37 | default:
38 | config: {}
39 | ----
40 |
41 | == Pod overrides
42 |
43 | The OPA operator also supports Pod overrides, allowing you to override any property that you can set on a Kubernetes Pod.
44 | Read the xref:concepts:overrides.adoc#pod-overrides[Pod overrides documentation] to learn more about this feature.
45 |
--------------------------------------------------------------------------------
/.github/workflows/pr_pre-commit.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: pre-commit
3 |
4 | on:
5 | pull_request:
6 | merge_group:
7 |
8 | env:
9 | CARGO_TERM_COLOR: always
10 | NIX_PKG_MANAGER_VERSION: "2.30.0"
11 | RUST_TOOLCHAIN_VERSION: "nightly-2025-10-23"
12 | HADOLINT_VERSION: "v2.14.0"
13 | PYTHON_VERSION: "3.14"
14 | JINJA2_CLI_VERSION: "0.8.2"
15 |
16 | jobs:
17 | pre-commit:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - name: Install host dependencies
21 | uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0
22 | with:
23 | packages: protobuf-compiler krb5-user libkrb5-dev libclang-dev liblzma-dev libssl-dev pkg-config apt-transport-https
24 | version: ubuntu-latest
25 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
26 | with:
27 | persist-credentials: false
28 | submodules: recursive
29 | fetch-depth: 0
30 | - uses: stackabletech/actions/run-pre-commit@29bea1b451c0c2e994bd495969286f95bf49ed6a # v0.11.0
31 | with:
32 | python-version: ${{ env.PYTHON_VERSION }}
33 | rust: ${{ env.RUST_TOOLCHAIN_VERSION }}
34 | hadolint: ${{ env.HADOLINT_VERSION }}
35 | nix: ${{ env.NIX_PKG_MANAGER_VERSION }}
36 | nix-github-token: ${{ secrets.GITHUB_TOKEN }}
37 | jinja2-cli: ${{ env.JINJA2_CLI_VERSION }}
38 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/pre-release-rust-deps.md:
--------------------------------------------------------------------------------
1 | ## Bump Rust Dependencies for Stackable Release YY.M.X
2 |
3 |
7 |
8 |
11 |
12 | Part of
13 |
14 | > [!NOTE]
15 | > During a Stackable release we need to update various Rust dependencies before
16 | > entering the final release period to ensure we run the latest versions of
17 | > crates. These bumps also include previously updated and released crates from
18 | > the `operator-rs` repository.
19 |
20 | ### Tasks
21 |
22 | - [ ] Bump Rust Dependencies, see below for more details.
23 | - [ ] Add changelog entry stating which important crates were bumped (including the version).
24 |
25 | > [!NOTE]
26 | > The bumping / updating of Rust dependencies is done in multiple steps:
27 | >
28 | > 1. Update the minimum Version in the root `Cargo.toml` manifest.
29 | > 2. Run the `cargo update` command, which also updates the `Cargo.lock` file.
30 | > 3. Lastly, run `make regenerate-nix` to update the `Cargo.nix` file.
31 |
32 | ### Bump Rust Dependencies
33 |
34 | - [ ] Bump `stackable-operator` and friends
35 | - [ ] Bump `product-config`
36 | - [ ] Bump all other dependencies
37 |
--------------------------------------------------------------------------------
/rust/user-info-fetcher/src/http_error.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Arc;
2 |
3 | use axum::{Json, response::IntoResponse};
4 | use hyper::StatusCode;
5 | use serde::Serialize;
6 |
7 | pub trait Error: std::error::Error {
8 | fn status_code(&self) -> StatusCode;
9 | }
10 | impl Error for Arc {
11 | fn status_code(&self) -> StatusCode {
12 | let inner: &T = self;
13 | inner.status_code()
14 | }
15 | }
16 |
17 | pub struct JsonResponse {
18 | pub error: E,
19 | }
20 |
21 | impl From for JsonResponse {
22 | fn from(error: E) -> Self {
23 | Self { error }
24 | }
25 | }
26 |
27 | impl IntoResponse for JsonResponse {
28 | fn into_response(self) -> axum::response::Response {
29 | (
30 | self.error.status_code(),
31 | Json(Container {
32 | error: Payload {
33 | message: self.error.to_string(),
34 | causes: std::iter::successors(self.error.source(), |err| err.source())
35 | .map(|err| err.to_string())
36 | .collect(),
37 | },
38 | }),
39 | )
40 | .into_response()
41 | }
42 | }
43 |
44 | #[derive(Serialize)]
45 | #[serde(rename_all = "camelCase")]
46 | struct Container {
47 | error: Payload,
48 | }
49 |
50 | #[derive(Serialize)]
51 | #[serde(rename_all = "camelCase")]
52 | struct Payload {
53 | message: String,
54 | causes: Vec,
55 | }
56 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/operations/graceful-shutdown.adoc:
--------------------------------------------------------------------------------
1 | = Graceful shutdown
2 |
3 | You can configure the graceful shutdown as described in xref:concepts:operations/graceful_shutdown.adoc[].
4 |
5 | == Servers
6 |
7 | As a default, OPA servers have `2 minutes` to shut down gracefully.
8 |
9 | The OPA server process receives a `SIGTERM` signal when Kubernetes wants to terminate the Pod.
10 | It acknowledges the shutdown as shown in the log below and initiate a graceful shutdown.
11 | After the graceful shutdown timeout runs out, and the process still didn't exit, Kubernetes issues a `SIGKILL` signal.
12 |
13 | [source,text]
14 | ----
15 | {"level":"info","msg":"Shutting down...","time":"2023-11-06T15:16:08Z"}
16 | {"level":"info","msg":"Server shutdown.","time":"2023-11-06T15:16:08Z"}
17 | {"level":"info","msg":"Stopping bundle loader.","name":"stackable","plugin":"bundle","time":"2023-11-06T15:16:08Z"}
18 | ----
19 |
20 | == Implementation
21 |
22 | Once a server Pod is asked to terminate the following timeline occurs:
23 |
24 | 1. The server stops accepting any new queries.
25 | 2. The server waits until all running queries have finished.
26 | 3. If the graceful shutdown doesn't complete quick enough (e.g. a query runs longer than the graceful shutdown period), after ` + 5s safety overhead` the Pod gets killed, regardless if it has shut down gracefully or not. This is achieved by setting `terminationGracePeriodSeconds` on the server Pods. Running queries on the sever will fail.
27 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/OpenTelemetry.adoc:
--------------------------------------------------------------------------------
1 | = OpenTelemetry
2 | :description: Ship OPA traces and logs to OpenTelemetry
3 | :opa-docs: https://v1-4-2--opa-docs.netlify.app/configuration/#distributed-tracing
4 |
5 | Opa supports sending OpenTelemetry traces as stated in {opa-docs}[the documentation].
6 |
7 | As of SDP 25.7, `configOverrides` are (still) not supported, we are tracking the progress in https://github.com/stackabletech/opa-operator/issues/756[this GitHub issue].
8 | To enable traces you need to modify the config and thus xref:opa:usage-guide/operations/cluster-operations.adoc[pause the reconciliation] of your OpaCluster, so that changes to the ConfigMap aren't immediately overridden by the opa-operator.
9 |
10 | WARNING: It's not encouraged to pause the reconciliation more than just temporarily. We recommend disabling it while you debug e.g. performance problems and re-enabling it afterwards. This problem will be solved once we support configOverrides for OPA.
11 |
12 | Afterwards you can edit the `-server-default` ConfigMap and append a `distributed_tracing` section as follows.
13 | Please check the {opa-docs}[OPA documentation] to see what other settings you can configure.
14 |
15 | [source,yaml]
16 | ----
17 | apiVersion: v1
18 | kind: ConfigMap
19 | metadata:
20 | name: opa-server-default
21 | data:
22 | config.json: |-
23 | {
24 | <<< existing JSON >>>
25 | "distributed_tracing": {
26 | "address": "jaeger-collector.default.svc.cluster.local:4317",
27 | "type": "grpc"
28 | }
29 | }
30 | ----
31 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/prepared-bundle-builder-logs.tracing-rs.json:
--------------------------------------------------------------------------------
1 | {"timestamp":"2024-01-01T00:00:00.000000Z","level":"INFO","fields":{"message":"Valid log event","directory":"/stackable/log/prepared-logs"},"target":"TestLogger"}
2 | {"timestamp":"2024-01-01T00:00:00.000000Z","level":"INFO","fields":{"message":"Valid log event with only the message field"},"target":"TestLogger"}
3 | {"timestamp":"2024-01-01T00:00:00.000000Z","level":"INFO","fields":{"message":"Valid log event with a nested field","nested":{"array":[1,2,3]}},"target":"TestLogger"}
4 | {"level":"INFO","fields":{"message":"Invalid log event without a timestamp","directory":"/stackable/log/prepared-logs"},"target":"TestLogger"}
5 | {"timestamp":"unparsable timestamp","level":"INFO","fields":{"message":"Invalid log event with an unparsable timestamp","directory":"/stackable/log/prepared-logs"},"target":"TestLogger"}
6 | {"timestamp":"2024-01-01T00:00:00.000000Z","fields":{"message":"Invalid log event without a level","directory":"/stackable/log/prepared-logs"},"target":"TestLogger"}
7 | {"timestamp":"2024-01-01T00:00:00.000000Z","level":"INFO","fields":{"message":"Invalid log event without a logger","directory":"/stackable/log/prepared-logs"}}
8 | {"timestamp":"2024-01-01T00:00:00.000000Z","level":"CRITICAL","fields":{"message":"Invalid log event with an unknown level","directory":"/stackable/log/prepared-logs"},"target":"TestLogger"}
9 | {"timestamp":"2024-01-01T00:00:00.000000Z","level":"INFO","fields":{"directory":"/stackable/log/prepared-logs"},"target":"TestLogger"}
10 | {"timestamp":"2024-01-01T00:00:00.000000Z","level":"INFO","target":"TestLogger"}
11 | "true"
12 |
--------------------------------------------------------------------------------
/crate-hashes.json:
--------------------------------------------------------------------------------
1 | {
2 | "git+https://github.com/stackabletech/krb5-rs.git?tag=v0.1.0#krb5-sys@0.1.0": "148zr0q04163hpirkrff5q7cbxqgwzzxh0091zr4g23x7l64jh39",
3 | "git+https://github.com/stackabletech/krb5-rs.git?tag=v0.1.0#krb5@0.1.0": "148zr0q04163hpirkrff5q7cbxqgwzzxh0091zr4g23x7l64jh39",
4 | "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.100.1#k8s-version@0.1.3": "1a98klljvifnc168f1wc3d6szcry1lamxgjjdq89plr99p4b953l",
5 | "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.100.1#stackable-operator-derive@0.3.1": "1a98klljvifnc168f1wc3d6szcry1lamxgjjdq89plr99p4b953l",
6 | "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.100.1#stackable-operator@0.100.1": "1a98klljvifnc168f1wc3d6szcry1lamxgjjdq89plr99p4b953l",
7 | "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.100.1#stackable-shared@0.0.3": "1a98klljvifnc168f1wc3d6szcry1lamxgjjdq89plr99p4b953l",
8 | "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.100.1#stackable-telemetry@0.6.1": "1a98klljvifnc168f1wc3d6szcry1lamxgjjdq89plr99p4b953l",
9 | "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.100.1#stackable-versioned-macros@0.8.3": "1a98klljvifnc168f1wc3d6szcry1lamxgjjdq89plr99p4b953l",
10 | "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.100.1#stackable-versioned@0.8.3": "1a98klljvifnc168f1wc3d6szcry1lamxgjjdq89plr99p4b953l",
11 | "git+https://github.com/stackabletech/product-config.git?tag=0.8.0#product-config@0.8.0": "1dz70kapm2wdqcr7ndyjji0lhsl98bsq95gnb2lw487wf6yr7987"
12 | }
--------------------------------------------------------------------------------
/tests/templates/kuttl/resources/10-install-opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: opa.stackable.tech/v1alpha1
3 | kind: OpaCluster
4 | metadata:
5 | name: opa
6 | spec:
7 | image:
8 | {% if test_scenario['values']['opa-latest'].find(",") > 0 %}
9 | custom: "{{ test_scenario['values']['opa-latest'].split(',')[1] }}"
10 | productVersion: "{{ test_scenario['values']['opa-latest'].split(',')[0] }}"
11 | {% else %}
12 | productVersion: "{{ test_scenario['values']['opa-latest'] }}"
13 | {% endif %}
14 | pullPolicy: IfNotPresent
15 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
16 | clusterConfig:
17 | vectorAggregatorConfigMapName: vector-aggregator-discovery
18 | {% endif %}
19 | servers:
20 | config:
21 | resources:
22 | cpu:
23 | min: 50m
24 | max: 110m
25 | memory:
26 | limit: 256Mi
27 | logging:
28 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
29 | roleGroups:
30 | resources-from-role: {}
31 | resources-from-role-group:
32 | config:
33 | resources:
34 | cpu:
35 | min: 60m
36 | max: 130m
37 | memory:
38 | limit: 284Mi
39 | logging:
40 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
41 | resources-from-pod-overrides:
42 | podOverrides:
43 | spec:
44 | containers:
45 | - name: opa
46 | resources:
47 | requests:
48 | cpu: 70m
49 | limits:
50 | cpu: 150m
51 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/prepared-opa-logs.json:
--------------------------------------------------------------------------------
1 | {"level":"info","msg":"Valid server log event","name":"stackable","plugin":"bundle","time":"2024-01-01T00:00:00Z","logger":"server"}
2 | {"bundles":{"stackable":{}},"decision_id":"5b887ec2-80a0-4b44-9009-2e0cae3507c2","labels":{"id":"992b9dd5-f29e-47a7-aa3a-4408218c3825","version":"0.67.1"},"level":"info","metrics":{"counter_server_query_cache_hit":0,"timer_rego_input_parse_ns":5500,"timer_rego_query_compile_ns":143369,"timer_rego_query_eval_ns":41107,"timer_rego_query_parse_ns":95750,"timer_server_handler_ns":422100},"msg":"Valid decision log event","path":"test","req_id":3,"requested_by":"10.244.0.138:33268","time":"2024-01-01T00:00:00Z","timestamp":"2024-01-01T00:00:00.000000000Z","type":"openpolicyagent.org/decision_logs","logger":"decision"}
3 | {"level":"info","msg":"Invalid log event without a timestamp","name":"stackable","plugin":"bundle","logger":"server"}
4 | {"level":"info","msg":"Invalid log event with an unparsable timestamp","name":"stackable","plugin":"bundle","time":"unparsable timestamp","logger":"server"}
5 | {"level":"info","msg":"Invalid log event without a logger","name":"stackable","plugin":"bundle","time":"2024-01-01T00:00:00Z"}
6 | {"level":"critical","msg":"Invalid log event with an unknown level","name":"stackable","plugin":"bundle","time":"2024-01-01T00:00:00Z","logger":"server"}
7 | {"msg":"Invalid log event without a level","name":"stackable","plugin":"bundle","time":"2024-01-01T00:00:00Z","logger":"server"}
8 | {"level":"info","name":"stackable","plugin":"bundle","time":"2024-01-01T00:00:00Z","logger":"server"}
9 | {"level":"info","msg":"Unparsable log event",...
10 | "true"
11 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/30_test-regorule.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import requests
3 | import argparse
4 |
5 |
6 | if __name__ == "__main__":
7 | all_args = argparse.ArgumentParser()
8 | all_args.add_argument("-u", "--url", required=True, help="OPA service url")
9 | args = vars(all_args.parse_args())
10 |
11 | # rego rule to check (compare: 01-install-opa.yaml)
12 | # ---
13 | # package test
14 | #
15 | # hello {
16 | # true
17 | # }
18 | #
19 | # world {
20 | # false
21 | # }
22 | # ---
23 | # We need to query: https://:/v1/data//()+
24 | # In our case https://:8443/v1/data/test
25 | # --> {'result': {'hello': True}}
26 | # or https://:8443/v1/data/test/hello
27 | # --> {'hello': True}
28 | # For HTTP: http://:8081/v1/data/test
29 |
30 | # Determine verification setting based on whether TLS is used
31 | if args["url"].startswith("http://"):
32 | verify = False
33 | protocol = "HTTP"
34 | else:
35 | verify = "/tls/ca.crt"
36 | protocol = "HTTPS"
37 |
38 | response = requests.post(args["url"], json={"input": {}}, verify=verify).json()
39 |
40 | if (
41 | "result" in response
42 | and "hello" in response["result"]
43 | and response["result"]["hello"]
44 | ):
45 | print(f"Regorule test ({protocol}) successful!")
46 | exit(0)
47 | else:
48 | print(
49 | f"Error ({protocol}): received "
50 | + str(response)
51 | + " - expected: {'result': {'hello': True}}"
52 | )
53 | exit(-1)
54 |
--------------------------------------------------------------------------------
/shell.nix:
--------------------------------------------------------------------------------
1 | let
2 | self = import ./. {};
3 | inherit (self) sources pkgs meta;
4 |
5 | beku = pkgs.callPackage (sources."beku.py" + "/beku.nix") {};
6 | cargoDependencySetOfCrate = crate: [ crate ] ++ pkgs.lib.concatMap cargoDependencySetOfCrate (crate.dependencies ++ crate.buildDependencies);
7 | cargoDependencySet = pkgs.lib.unique (pkgs.lib.flatten (pkgs.lib.mapAttrsToList (crateName: crate: cargoDependencySetOfCrate crate.build) self.cargo.workspaceMembers));
8 | in pkgs.mkShell rec {
9 | name = meta.operator.name;
10 |
11 | packages = with pkgs; [
12 | ## cargo et-al
13 | rustup # this breaks pkg-config if it is in the nativeBuildInputs
14 | cargo-udeps
15 |
16 | ## Extra dependencies for use in a pure env (nix-shell --pure)
17 | ## These are mosuly useful for maintainers of this shell.nix
18 | ## to ensure all the dependencies are caught.
19 | # cacert
20 | # vim nvim nano
21 | ];
22 |
23 | # derivation runtime dependencies
24 | buildInputs = pkgs.lib.concatMap (crate: crate.buildInputs) cargoDependencySet;
25 |
26 | # build time dependencies
27 | nativeBuildInputs = pkgs.lib.concatMap (crate: crate.nativeBuildInputs) cargoDependencySet ++ (with pkgs; [
28 | beku
29 | docker
30 | gettext # for the proper envsubst
31 | git
32 | jq
33 | kind
34 | kubectl
35 | kubernetes-helm
36 | kuttl
37 | nix # this is implied, but needed in the pure env
38 | # tilt already defined in default.nix
39 | which
40 | yq-go
41 | ]);
42 |
43 | LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
44 | BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.glibc.dev}/include -I${pkgs.clang}/resource-root/include";
45 | }
46 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = ["rust/*"]
3 | resolver = "2"
4 |
5 | [workspace.package]
6 | version = "0.0.0-dev"
7 | authors = ["Stackable GmbH "]
8 | license = "OSL-3.0"
9 | edition = "2021"
10 | repository = "https://github.com/stackabletech/opa-operator"
11 |
12 | [workspace.dependencies]
13 | product-config = { git = "https://github.com/stackabletech/product-config.git", tag = "0.8.0" }
14 | stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", features = ["telemetry", "versioned"], tag = "stackable-operator-0.100.1" }
15 | krb5 = { git = "https://github.com/stackabletech/krb5-rs.git", tag = "v0.1.0" }
16 |
17 | anyhow = "1.0"
18 | axum = "0.8"
19 | base64 = "0.22"
20 | built = { version = "0.8", features = ["chrono", "git2"] }
21 | byteorder = "1.5"
22 | clap = "4.5"
23 | const_format = "0.2"
24 | flate2 = "1.0"
25 | fnv = "1.0"
26 | futures = { version = "0.3" }
27 | hyper = "1.4"
28 | indoc = "2.0"
29 | ldap3 = { version = "0.12", features = ["gssapi", "tls"] }
30 | moka = { version = "0.12", features = ["future"] }
31 | native-tls = "0.2.12"
32 | pin-project = "1.1"
33 | reqwest = { version = "0.12", features = ["json"] }
34 | rustls-pemfile = "2.1"
35 | semver = "1.0"
36 | serde = { version = "1.0", features = ["derive"] }
37 | serde_json = "1.0"
38 | snafu = "0.8"
39 | strum = { version = "0.27", features = ["derive"] }
40 | tar = "0.4"
41 | tokio = { version = "1.40", features = ["full"] }
42 | tracing = "0.1"
43 | url = "2.5"
44 | uuid = "1.10"
45 |
46 | # [patch."https://github.com/stackabletech/operator-rs.git"]
47 | # stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" }
48 | # stackable-operator = { path = "../operator-rs/crates/stackable-operator" }
49 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/10-install-opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: |
6 | kubectl apply -n $NAMESPACE -f - < 0 %}
30 | custom: "{{ test_scenario['values']['opa-latest'].split(',')[1] }}"
31 | productVersion: "{{ test_scenario['values']['opa-latest'].split(',')[0] }}"
32 | {% else %}
33 | productVersion: "{{ test_scenario['values']['opa-latest'] }}"
34 | {% endif %}
35 | pullPolicy: IfNotPresent
36 | clusterConfig:
37 | userInfo:
38 | backend:
39 | experimentalXfscAas:
40 | hostname: aas.$NAMESPACE.svc.cluster.local
41 | port: 5000
42 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
43 | vectorAggregatorConfigMapName: vector-aggregator-discovery
44 | {% endif %}
45 | servers:
46 | config:
47 | logging:
48 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
49 | roleGroups:
50 | default: {}
51 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | *Please add a description here. This will become the commit message of the merge request later.*
4 |
5 | ## Definition of Done Checklist
6 |
7 | - Not all of these items are applicable to all PRs, the author should update this template to only leave the boxes in that are relevant
8 | - Please make sure all these things are done and tick the boxes
9 |
10 | ### Author
11 |
12 | - [ ] Changes are OpenShift compatible
13 | - [ ] CRD changes approved
14 | - [ ] CRD documentation for all fields, following the [style guide](https://docs.stackable.tech/home/nightly/contributor/docs/style-guide).
15 | - [ ] Helm chart can be installed and deployed operator works
16 | - [ ] Integration tests passed (for non trivial changes)
17 | - [ ] Changes need to be "offline" compatible
18 | - [ ] Links to generated (nightly) docs added
19 | - [ ] Release note snippet added
20 |
21 | ### Reviewer
22 |
23 | - [ ] Code contains useful comments
24 | - [ ] Code contains useful logging statements
25 | - [ ] (Integration-)Test cases added
26 | - [ ] Documentation added or updated. Follows the [style guide](https://docs.stackable.tech/home/nightly/contributor/docs/style-guide).
27 | - [ ] Changelog updated
28 | - [ ] Cargo.toml only contains references to git tags (not specific commits or branches)
29 |
30 | ### Acceptance
31 |
32 | - [ ] Feature Tracker has been updated
33 | - [ ] Proper release label has been added
34 | - [ ] Links to generated (nightly) docs added
35 | - [ ] Release note snippet added
36 | - [ ] Add `type/deprecation` label & add to the [deprecation schedule](https://github.com/orgs/stackabletech/projects/44/views/1)
37 | - [ ] Add `type/experimental` label & add to the [experimental features tracker](https://github.com/orgs/stackabletech/projects/47)
38 |
--------------------------------------------------------------------------------
/nix/sources.json:
--------------------------------------------------------------------------------
1 | {
2 | "beku.py": {
3 | "branch": "0.0.10",
4 | "description": "Test suite expander for Stackable Kuttl tests.",
5 | "homepage": null,
6 | "owner": "stackabletech",
7 | "repo": "beku.py",
8 | "rev": "fc75202a38529a4ac6776dd8a5dfee278d927f58",
9 | "sha256": "152yary0p11h87yabv74jnwkghsal7lx16az0qlzrzdrs6n5v8id",
10 | "type": "tarball",
11 | "url": "https://github.com/stackabletech/beku.py/archive/fc75202a38529a4ac6776dd8a5dfee278d927f58.tar.gz",
12 | "url_template": "https://github.com///archive/.tar.gz"
13 | },
14 | "crate2nix": {
15 | "branch": "master",
16 | "description": "nix build file generator for rust crates",
17 | "homepage": "",
18 | "owner": "kolloch",
19 | "repo": "crate2nix",
20 | "rev": "be31feae9a82c225c0fd1bdf978565dc452a483a",
21 | "sha256": "14d0ymlrwk7dynv35qcw4xn0dylfpwjmf6f8znflbk2l6fk23l12",
22 | "type": "tarball",
23 | "url": "https://github.com/kolloch/crate2nix/archive/be31feae9a82c225c0fd1bdf978565dc452a483a.tar.gz",
24 | "url_template": "https://github.com///archive/.tar.gz"
25 | },
26 | "nixpkgs": {
27 | "branch": "nixpkgs-unstable",
28 | "description": "Nix Packages collection",
29 | "homepage": "",
30 | "owner": "NixOS",
31 | "repo": "nixpkgs",
32 | "rev": "a7fc11be66bdfb5cdde611ee5ce381c183da8386",
33 | "sha256": "0h3gvjbrlkvxhbxpy01n603ixv0pjy19n9kf73rdkchdvqcn70j2",
34 | "type": "tarball",
35 | "url": "https://github.com/NixOS/nixpkgs/archive/a7fc11be66bdfb5cdde611ee5ce381c183da8386.tar.gz",
36 | "url_template": "https://github.com///archive/.tar.gz"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/smoke/20-install-test-opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: |
6 | kubectl apply -n $NAMESPACE -f - < 0 %}
33 | custom: "{{ test_scenario['values']['opa'].split(',')[1] }}"
34 | productVersion: "{{ test_scenario['values']['opa'].split(',')[0] }}"
35 | {% else %}
36 | productVersion: "{{ test_scenario['values']['opa'] }}"
37 | {% endif %}
38 | pullPolicy: IfNotPresent
39 | clusterConfig:
40 | {% if test_scenario['values']['use-tls'] == "true" %}
41 | tls:
42 | serverSecretClass: opa-tls-$NAMESPACE
43 | {% endif %}
44 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
45 | vectorAggregatorConfigMapName: vector-aggregator-discovery
46 | {% endif %}
47 | servers:
48 | config:
49 | logging:
50 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
51 | envOverrides:
52 | SERVER_ROLE_LEVEL_ENV_VAR: "SERVER_ROLE_LEVEL_ENV_VAR"
53 | roleGroups:
54 | default:
55 | envOverrides:
56 | SERVER_ROLE_GROUP_LEVEL_ENV_VAR: "SERVER_ROLE_GROUP_LEVEL_ENV_VAR"
57 | EOF
58 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/implementation-notes.adoc:
--------------------------------------------------------------------------------
1 | = Implementation notes
2 |
3 | These notes may be of use when trying to understand why things are implemented the way that they are,
4 | but should not be required reading for regular use.
5 |
6 | == OPA replica per node
7 |
8 | OPA runs on each Node to avoid requiring network round trips for services making policy queries (which are often chained in serial, and block other tasks in the products).
9 |
10 | Local access is ensured via an https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/[`InternalTrafficPolicy`].
11 | This means that https://kubernetes.io/docs/concepts/workloads/pods/[Pods] accessing OPA via the service discovery are routed to the OPA Pod on the same https://kubernetes.io/docs/concepts/architecture/nodes/[Node] to reduce request latency and network traffic.
12 |
13 | == OPA Bundle Builder
14 |
15 | Users can manage policy rules by creating, updating and deleting ConfigMap resources.
16 |
17 | The responsibility of the https://github.com/stackabletech/opa-bundle-builder[OPA Bundle Builder] is to convert these resources to bundles (`tar.gz` files) and make them available via an HTTP endpoint.
18 | The OPA Bundle Builder runs in a side container of the OPA Pod as a simple HTTP server that OPA is querying regularly
19 | (every 20 to 30 seconds) for updates.
20 |
21 | NOTE: Kubernetes limits the size of ConfigMaps to 1MB.
22 | Users have to take this limit into consideration when managing policy rules.
23 |
24 | Only ConfigMaps labeled with `opa.stackable.tech/bundle: "true"` are considered by the builder when updating bundles. The name of
25 | the `data` entries in the `ConfigMap` are used as file names when storing the rules in the bundle.
26 |
27 | NOTE: Currently, it is the user's responsibility to make sure these names do not collide (as they will override each other).
28 |
--------------------------------------------------------------------------------
/.readme/static/borrowed/Icon_Stackable.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/getting_started/installation.adoc:
--------------------------------------------------------------------------------
1 | = Installation
2 | :description: Install the Stackable OPA operator on Kubernetes using stackablectl or Helm. Deploy OPA, create policies, and query rules easily.
3 |
4 | There are 2 ways to install Stackable operators on a Kubernetes cluster.
5 |
6 | * Using xref:management:stackablectl:index.adoc[] (recommended)
7 | * Using Helm
8 |
9 | [tabs]
10 | ====
11 | stackablectl::
12 | +
13 | --
14 | `stackablectl` is the command line tool to interact with Stackable operators and the recommended way to install operators.
15 | Follow the xref:management:stackablectl:installation.adoc[installation steps] for your platform.
16 |
17 | After you have installed `stackablectl` run the following command to install the OPA operator:
18 |
19 | [source,shell]
20 | ----
21 | include::example$getting_started/getting_started.sh[tag=stackablectl-install-operators]
22 | ----
23 |
24 | The tool prints
25 |
26 | [source]
27 | include::example$getting_started/install_output.txt[]
28 |
29 | TIP: Consult the xref:management:stackablectl:quickstart.adoc[] to learn more about how to use `stackablectl`.
30 | For example, you can use the `--cluster kind` flag to create a Kubernetes cluster with link:https://kind.sigs.k8s.io/[kind].
31 | --
32 |
33 | Helm::
34 | +
35 | --
36 | You can also use Helm to install the operator.
37 |
38 | NOTE: `helm repo` subcommands are not supported for OCI registries. The operators are installed directly, without adding the Helm Chart repository first.
39 |
40 | Install the Stackable OPA operator:
41 |
42 | [source,shell]
43 | ----
44 | include::example$getting_started/getting_started.sh[tag=helm-install-operators]
45 | ----
46 |
47 | Helm deploys the operator in a Kubernetes Deployment and apply the CRDs for the OPA service.
48 | --
49 | ====
50 |
51 | == What's next
52 |
53 | xref:getting_started/first_steps.adoc[Deploy OPA, a policy rule and query it].
54 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/resources/10-assert.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | timeout: 600
5 | commands:
6 | - script: kubectl -n $NAMESPACE rollout status daemonset opa-server-resources-from-role --timeout 600s
7 | - script: kubectl -n $NAMESPACE rollout status daemonset opa-server-resources-from-role-group --timeout 600s
8 | ---
9 | apiVersion: apps/v1
10 | kind: DaemonSet
11 | metadata:
12 | name: opa-server-resources-from-role
13 | spec:
14 | template:
15 | spec:
16 | containers:
17 | - name: opa
18 | resources:
19 | requests:
20 | cpu: 50m
21 | memory: 256Mi
22 | limits:
23 | cpu: 110m
24 | memory: 256Mi
25 | - name: bundle-builder
26 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
27 | - name: vector
28 | {% endif %}
29 | ---
30 | apiVersion: apps/v1
31 | kind: DaemonSet
32 | metadata:
33 | name: opa-server-resources-from-role-group
34 | spec:
35 | template:
36 | spec:
37 | containers:
38 | - name: opa
39 | resources:
40 | requests:
41 | cpu: 60m
42 | memory: 284Mi
43 | limits:
44 | cpu: 130m
45 | memory: 284Mi
46 | - name: bundle-builder
47 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
48 | - name: vector
49 | {% endif %}
50 | ---
51 | apiVersion: apps/v1
52 | kind: DaemonSet
53 | metadata:
54 | name: opa-server-resources-from-pod-overrides
55 | spec:
56 | template:
57 | spec:
58 | containers:
59 | - name: opa
60 | resources:
61 | requests:
62 | cpu: 70m
63 | memory: 256Mi
64 | limits:
65 | cpu: 150m
66 | memory: 256Mi
67 | - name: bundle-builder
68 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
69 | - name: vector
70 | {% endif %}
71 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/test_log_aggregation.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import requests
3 | import time
4 |
5 |
6 | def send_opa_decision_request():
7 | response = requests.post("http://test-opa-server:8081/v1/data/test/world")
8 |
9 | assert response.status_code == 200, "Cannot access the API of the opa cluster."
10 |
11 |
12 | def check_sent_events():
13 | response = requests.post(
14 | "http://opa-vector-aggregator:8686/graphql",
15 | json={
16 | "query": """
17 | {
18 | transforms(first:100) {
19 | nodes {
20 | componentId
21 | metrics {
22 | sentEventsTotal {
23 | sentEventsTotal
24 | }
25 | }
26 | }
27 | }
28 | }
29 | """
30 | },
31 | )
32 |
33 | assert response.status_code == 200, (
34 | "Cannot access the API of the vector aggregator."
35 | )
36 |
37 | result = response.json()
38 |
39 | transforms = result["data"]["transforms"]["nodes"]
40 | for transform in transforms:
41 | sentEvents = transform["metrics"]["sentEventsTotal"]
42 | componentId = transform["componentId"]
43 |
44 | if componentId == "filteredInvalidEvents":
45 | assert sentEvents is None or sentEvents["sentEventsTotal"] == 0, (
46 | "Invalid log events were sent."
47 | )
48 | else:
49 | assert sentEvents is not None and sentEvents["sentEventsTotal"] > 0, (
50 | f'No events were sent in "{componentId}".'
51 | )
52 |
53 |
54 | if __name__ == "__main__":
55 | send_opa_decision_request()
56 | time.sleep(10)
57 | check_sent_events()
58 | print("Test successful!")
59 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/02-bug_report.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: "🐛 Bug Report"
3 | description: "If something isn't working as expected 🤔."
4 | labels: ["type/bug"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible.
9 |
10 | - type: input
11 | attributes:
12 | label: Affected Stackable version
13 | description: Which version of the Stackable Operator do you see this bug in?
14 |
15 | #
16 | - type: input
17 | attributes:
18 | label: Affected OpenPolicyAgent version
19 | description: Which version of OpenPolicyAgent do you see this bug in?
20 | #
21 |
22 | - type: textarea
23 | attributes:
24 | label: Current and expected behavior
25 | description: A clear and concise description of what the operator is doing and what you would expect.
26 | validations:
27 | required: true
28 |
29 | - type: textarea
30 | attributes:
31 | label: Possible solution
32 | description: "If you have suggestions on a fix for the bug."
33 |
34 | - type: textarea
35 | attributes:
36 | label: Additional context
37 | description: "Add any other context about the problem here. Or a screenshot if applicable."
38 |
39 | - type: textarea
40 | attributes:
41 | label: Environment
42 | description: |
43 | What type of kubernetes cluster you are running against (k3s/eks/aks/gke/other) and any other information about your environment?
44 | placeholder: |
45 | Examples:
46 | Output of `kubectl version --short`
47 |
48 | - type: dropdown
49 | attributes:
50 | label: Would you like to work on fixing this bug?
51 | description: |
52 | **NOTE**: Let us know if you would like to submit a PR for this. We are more than happy to help you through the process.
53 | options:
54 | - "yes"
55 | - "no"
56 | - "maybe"
57 |
--------------------------------------------------------------------------------
/tests/test-definition.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | dimensions:
3 | - name: opa
4 | values:
5 | # To use a custom image, add a comma and the full name after the product version
6 | # 0.67.1,oci.stackable.tech/sdp/opa:0.67.1-stackable0.0.0-dev
7 | - 1.4.2
8 | - 1.8.0
9 | - name: opa-latest
10 | values:
11 | # To use a custom image, add a comma and the full name after the product version
12 | # 0.67.1,oci.stackable.tech/sdp/opa:0.67.1-stackable0.0.0-dev
13 | - 1.8.0
14 | - name: keycloak
15 | values:
16 | - 23.0.1
17 | - name: openshift
18 | values:
19 | - "false"
20 | - name: use-tls
21 | values:
22 | - "true"
23 | - "false"
24 | tests:
25 | - name: smoke
26 | dimensions:
27 | - opa
28 | - openshift
29 | - use-tls
30 | - name: resources
31 | dimensions:
32 | - opa-latest
33 | - openshift
34 | - name: logging
35 | dimensions:
36 | - opa
37 | - openshift
38 | - name: cluster-operation
39 | dimensions:
40 | - opa-latest
41 | - openshift
42 | - name: keycloak-user-info
43 | dimensions:
44 | - opa-latest
45 | - keycloak
46 | - openshift
47 | # AD must be initialized (by running ad-init) first,
48 | # and the correct users and groups must be set up (see test-regorule.py)
49 | # name: ad-user-info
50 | # dimensions:
51 | # - opa-latest
52 | # - openshift
53 | - name: aas-user-info
54 | dimensions:
55 | - opa-latest
56 | - openshift
57 | - name: openldap-user-info
58 | dimensions:
59 | - opa-latest
60 | - openshift
61 | suites:
62 | - name: nightly
63 | patch:
64 | - dimensions:
65 | - expr: last
66 | - name: smoke-latest
67 | select:
68 | - smoke
69 | patch:
70 | - dimensions:
71 | - expr: last
72 | - name: openshift
73 | patch:
74 | - dimensions:
75 | - expr: last
76 | - dimensions:
77 | - name: openshift
78 | expr: "true"
79 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/templates/_telemetry.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | Create a list of telemetry related env vars.
3 | */}}
4 | {{- define "telemetry.envVars" -}}
5 | {{- with .Values.telemetry }}
6 | {{- if not .consoleLog.enabled }}
7 | - name: CONSOLE_LOG_DISABLED
8 | value: "true"
9 | {{- end }}
10 | {{- if and .consoleLog.enabled .consoleLog.level }}
11 | - name: CONSOLE_LOG_LEVEL
12 | value: {{ .consoleLog.level }}
13 | {{ end }}
14 | {{- if and .consoleLog.enabled .consoleLog.format }}
15 | - name: CONSOLE_LOG_FORMAT
16 | value: {{ .consoleLog.format }}
17 | {{ end }}
18 | {{- if .fileLog.enabled }}
19 | - name: FILE_LOG_DIRECTORY
20 | value: /stackable/logs/{{ include "operator.appname" $ }}
21 | {{- end }}
22 | {{- if and .fileLog.enabled .fileLog.level }}
23 | - name: FILE_LOG_LEVEL
24 | value: {{ .fileLog.level }}
25 | {{- end }}
26 | {{- if and .fileLog.enabled .fileLog.rotationPeriod }}
27 | - name: FILE_LOG_ROTATION_PERIOD
28 | value: {{ .fileLog.rotationPeriod }}
29 | {{- end }}
30 | {{- if and .fileLog.enabled .fileLog.maxFiles }}
31 | - name: FILE_LOG_MAX_FILES
32 | value: {{ quote .fileLog.maxFiles }}
33 | {{- end }}
34 | {{- if .otelLogExporter.enabled }}
35 | - name: OTEL_LOG_EXPORTER_ENABLED
36 | value: "true"
37 | {{- end }}
38 | {{- if and .otelLogExporter.enabled .otelLogExporter.level }}
39 | - name: OTEL_LOG_EXPORTER_LEVEL
40 | value: {{ .otelLogExporter.level }}
41 | {{- end }}
42 | {{- if and .otelLogExporter.enabled .otelLogExporter.endpoint }}
43 | - name: OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
44 | value: {{ .otelLogExporter.endpoint }}
45 | {{- end }}
46 | {{- if .otelTraceExporter.enabled }}
47 | - name: OTEL_TRACE_EXPORTER_ENABLED
48 | value: "true"
49 | {{- end }}
50 | {{- if and .otelTraceExporter.enabled .otelTraceExporter.level }}
51 | - name: OTEL_TRACE_EXPORTER_LEVEL
52 | value: {{ .otelTraceExporter.level }}
53 | {{- end }}
54 | {{- if and .otelTraceExporter.enabled .otelTraceExporter.endpoint }}
55 | - name: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
56 | value: {{ .otelTraceExporter.endpoint }}
57 | {{- end }}
58 | {{- end }}
59 | {{- end }}
60 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/reference/discovery.adoc:
--------------------------------------------------------------------------------
1 | = Discovery
2 | :description: Discover OPA cluster connection strings for internal access, including URL configuration for querying policies.
3 | :page-aliases: discovery.adoc
4 | :clusterName: simple-opa
5 | :namespace: stackable
6 | :packageName: opa-test
7 | :policyName: allow
8 |
9 | The Stackable operator for OpenPolicyAgent (OPA) publishes a xref:concepts:service-discovery.adoc[discovery ConfigMap], which exposes a client configuration bundle that allows access to the OPA cluster.
10 |
11 | The bundle includes a connection string to access the OPA cluster.
12 | This string may be used by other operators or tools to configure their products with access to OPA.
13 | This is limited to internal cluster access.
14 |
15 | == Example
16 |
17 | Given the following OPA cluster:
18 |
19 | [source,yaml,subs="normal,callouts"]
20 | ----
21 | apiVersion: opa.stackable.tech/v1alpha1
22 | kind: OpaCluster
23 | metadata:
24 | name: {clusterName} # <1>
25 | namespace: {namespace} # <2>
26 | spec:
27 | [...]
28 | ----
29 | <1> The name of the OPA cluster, which is also the name of the created discovery ConfigMap.
30 | <2> The namespace of the discovery ConfigMap.
31 |
32 | The resulting discovery ConfigMap is `{namespace}/{clusterName}`.
33 |
34 | == Contents
35 |
36 | The `{namespace}/{clusterName}` discovery ConfigMap contains the following fields where `{clusterName}` represents the name and `{namespace}` the namespace of the cluster:
37 |
38 | `OPA`::
39 | ====
40 | A connection string for cluster internal OPA requests.
41 | Provided the cluster example above, the connection string is created as follows:
42 |
43 | [subs="attributes"]
44 | http://{clusterName}.{namespace}.svc.cluster.local:8081/
45 |
46 | This connection string points to the base URL (and web UI) of the OPA cluster.
47 | In order to query policies you have to configure your product and its OPA URL as follows, given the bundle package name `{packageName}` and the policy name `{policyName}`:
48 |
49 | [subs="attributes"]
50 | http://{clusterName}.{namespace}.svc.cluster.local:8081/v1/data/{packageName}/{policyName}
51 | ====
52 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/10-install-opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: |
6 | kubectl apply -n $NAMESPACE -f - < 0 %}
30 | custom: "{{ test_scenario['values']['opa-latest'].split(',')[1] }}"
31 | productVersion: "{{ test_scenario['values']['opa-latest'].split(',')[0] }}"
32 | {% else %}
33 | productVersion: "{{ test_scenario['values']['opa-latest'] }}"
34 | {% endif %}
35 | pullPolicy: IfNotPresent
36 | clusterConfig:
37 | userInfo:
38 | backend:
39 | keycloak:
40 | hostname: keycloak.$NAMESPACE.svc.cluster.local
41 | port: 8443
42 | tls:
43 | verification:
44 | server:
45 | caCert:
46 | secretClass: keycloak-tls-$NAMESPACE
47 | clientCredentialsSecret: user-info-fetcher-client-credentials
48 | adminRealm: my-dataspace
49 | userRealm: my-dataspace
50 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
51 | vectorAggregatorConfigMapName: vector-aggregator-discovery
52 | {% endif %}
53 | servers:
54 | config:
55 | logging:
56 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
57 | roleGroups:
58 | default: {}
59 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/logging.adoc:
--------------------------------------------------------------------------------
1 | = Logging
2 | :description: Configure log aggregation for OPA with Vector, enable decision logging, and customize log levels for file and console outputs.
3 |
4 | == Log aggregation
5 |
6 | The logs can be forwarded to a Vector log aggregator by providing a discovery ConfigMap for the aggregator and by enabling the log agent:
7 |
8 | [source,yaml]
9 | ----
10 | spec:
11 | clusterConfig:
12 | vectorAggregatorConfigMapName: vector-aggregator-discovery
13 | servers:
14 | config:
15 | logging:
16 | enableVectorAgent: true
17 | containers:
18 | opa:
19 | console:
20 | level: NONE
21 | file:
22 | level: INFO
23 | ----
24 |
25 | The Stackable operator for OPA only supports automatic log configuration due to the lack of customization for the OPA logging.
26 |
27 | Furthermore, the only customization possible for console output for the `bundle-builder` container is `NONE`.
28 | This deactivates console logging.
29 | Other log levels for console logging in this container are overwritten by the file log level.
30 |
31 | == Decision logging
32 |
33 | The decision logging for OPA can be enabled by setting the log level of the decision logger to any other level than `NONE`, as shown by example here:
34 |
35 | [source,yaml]
36 | ----
37 | spec:
38 | clusterConfig:
39 | vectorAggregatorConfigMapName: vector-aggregator-discovery
40 | servers:
41 | config:
42 | logging:
43 | enableVectorAgent: true
44 | containers:
45 | opa:
46 | console:
47 | level: NONE
48 | file:
49 | level: INFO
50 | loggers:
51 | decision: # <1>
52 | level: INFO
53 | ----
54 | <1> The `decision` logger is configured here.
55 |
56 | The decision logs are still filtered by the log level set for the console and file appenders.
57 | Therefore, the configuration above would result in decision logs being present on file but not on console.
58 |
59 | Further information on how to configure logging, can be found in
60 | xref:concepts:logging.adoc[].
61 |
--------------------------------------------------------------------------------
/deny.toml:
--------------------------------------------------------------------------------
1 | # This file is the source of truth for all our repos!
2 | # This includes repos not templated by operator-templating, please copy/paste the file for this repos.
3 |
4 | # TIP: Use "cargo deny check" to check if everything is fine
5 |
6 | [graph]
7 | targets = [
8 | { triple = "x86_64-unknown-linux-gnu" },
9 | { triple = "aarch64-unknown-linux-gnu" },
10 | { triple = "x86_64-unknown-linux-musl" },
11 | { triple = "aarch64-apple-darwin" },
12 | { triple = "x86_64-apple-darwin" },
13 | ]
14 |
15 | [advisories]
16 | yanked = "deny"
17 | ignore = [
18 | # https://rustsec.org/advisories/RUSTSEC-2023-0071
19 | # "rsa" crate: Marvin Attack: potential key recovery through timing sidechannel
20 | #
21 | # No patch is yet available, however work is underway to migrate to a fully constant-time implementation
22 | # So we need to accept this, as of SDP 25.3 we are not using the rsa crate to create certificates used in production
23 | # setups.
24 | #
25 | # https://github.com/RustCrypto/RSA/issues/19 is the tracking issue
26 | "RUSTSEC-2023-0071",
27 | ]
28 |
29 | [bans]
30 | multiple-versions = "allow"
31 |
32 | [licenses]
33 | unused-allowed-license = "allow"
34 | confidence-threshold = 1.0
35 | allow = [
36 | "Apache-2.0",
37 | "BSD-2-Clause",
38 | "BSD-3-Clause",
39 | "CC0-1.0",
40 | "ISC",
41 | "LicenseRef-ring",
42 | "LicenseRef-webpki",
43 | "MIT",
44 | "MPL-2.0",
45 | "OpenSSL", # Needed for the ring and/or aws-lc-sys crate. See https://github.com/stackabletech/operator-templating/pull/464 for details
46 | "Unicode-3.0",
47 | "Unicode-DFS-2016",
48 | "Zlib",
49 | "Unlicense",
50 | ]
51 | private = { ignore = true }
52 |
53 | [[licenses.clarify]]
54 | name = "ring"
55 | expression = "LicenseRef-ring"
56 | license-files = [
57 | { path = "LICENSE", hash = 0xbd0eed23 },
58 | ]
59 |
60 | [[licenses.clarify]]
61 | name = "webpki"
62 | expression = "LicenseRef-webpki"
63 | license-files = [
64 | { path = "LICENSE", hash = 0x001c7e6c },
65 | ]
66 |
67 | [sources]
68 | unknown-registry = "deny"
69 | unknown-git = "deny"
70 |
71 | [sources.allow-org]
72 | github = ["stackabletech"]
73 |
--------------------------------------------------------------------------------
/Tiltfile:
--------------------------------------------------------------------------------
1 | # If tilt_options.json exists read it and load the default_registry value from it
2 | settings = read_json('tilt_options.json', default={})
3 | registry = settings.get('default_registry', 'oci.stackable.tech/sandbox')
4 |
5 | # Configure default registry either read from config file above, or with default value of "oci.stackable.tech/sandbox"
6 | default_registry(registry)
7 |
8 | meta = read_json('nix/meta.json')
9 | operator_name = meta['operator']['name']
10 |
11 | custom_build(
12 | registry + '/' + operator_name,
13 | 'make regenerate-nix && nix-build . -A docker --argstr dockerName "${EXPECTED_REGISTRY}/' + operator_name + '" && ./result/load-image | docker load',
14 | deps=['rust', 'Cargo.toml', 'Cargo.lock', 'default.nix', "nix", 'build.rs', 'vendor'],
15 | ignore=['*.~undo-tree~'],
16 | # ignore=['result*', 'Cargo.nix', 'target', *.yaml],
17 | outputs_image_ref_to='result/ref',
18 | )
19 |
20 | # Load the latest CRDs from Nix
21 | watch_file('result')
22 | if os.path.exists('result'):
23 | k8s_yaml('result/crds.yaml')
24 |
25 | # We need to set the correct image annotation on the operator Deployment to use e.g.
26 | # oci.stackable.tech/sandbox/opa-operator:7y19m3d8clwxlv34v5q2x4p7v536s00g instead of
27 | # oci.stackable.tech/sandbox/opa-operator:0.0.0-dev (which does not exist)
28 | k8s_kind('Deployment', image_json_path='{.spec.template.metadata.annotations.internal\\.stackable\\.tech/image}')
29 | k8s_kind('DaemonSet', image_json_path='{.spec.template.metadata.annotations.internal\\.stackable\\.tech/image}')
30 |
31 | # Optionally specify a custom Helm values file to be passed to the Helm deployment below.
32 | # This file can for example be used to set custom telemetry options (like log level) which is not
33 | # supported by helm(set).
34 | helm_values = settings.get('helm_values', None)
35 |
36 | helm_override_image_repository = 'image.repository=' + registry + '/' + operator_name
37 |
38 | # Exclude stale CRDs from Helm chart, and apply the rest
39 | helm_crds, helm_non_crds = filter_yaml(
40 | helm(
41 | 'deploy/helm/' + operator_name,
42 | name=operator_name,
43 | namespace="stackable-operators",
44 | set=[
45 | helm_override_image_repository,
46 | ],
47 | values=helm_values,
48 | ),
49 | api_version = "^apiextensions\\.k8s\\.io/.*$",
50 | kind = "^CustomResourceDefinition$",
51 | )
52 | k8s_yaml(helm_non_crds)
53 |
--------------------------------------------------------------------------------
/rust/user-info-fetcher/src/utils/http.rs:
--------------------------------------------------------------------------------
1 | use hyper::StatusCode;
2 | use reqwest::{RequestBuilder, Response};
3 | use serde::de::DeserializeOwned;
4 | use snafu::{ResultExt, Snafu};
5 |
6 | #[derive(Snafu, Debug)]
7 | pub enum Error {
8 | #[snafu(display("failed to execute request"))]
9 | HttpRequest { source: reqwest::Error },
10 |
11 | #[snafu(display("failed to parse json response"))]
12 | ParseJson { source: reqwest::Error },
13 |
14 | #[snafu(display("http response {status:?} for {url:?} with response body {text:?}"))]
15 | HttpErrorResponse {
16 | status: StatusCode,
17 | url: String,
18 | text: String,
19 | },
20 |
21 | #[snafu(display("http response {status:?} for {url:?} with an undecodable response body"))]
22 | HttpErrorResponseUndecodableText {
23 | status: StatusCode,
24 | url: String,
25 | encoding_error: reqwest::Error,
26 | },
27 | }
28 |
29 | pub async fn send_json_request(req: RequestBuilder) -> Result {
30 | // make the request
31 | let response = req.send().await.context(HttpRequestSnafu)?;
32 | // check for client or server errors
33 | let non_error_response = error_for_status(response).await?;
34 | // parse the result
35 | let result = non_error_response.json().await.context(ParseJsonSnafu)?;
36 | Ok(result)
37 | }
38 |
39 | /// Wraps a Response into a Result. If there is an HTTP Client or Server error,
40 | /// extract the HTTP body (if possible) to be used as context in the returned Err.
41 | /// This is done this because the `Response::error_for_status()` method Err variant
42 | /// does not contain this information.
43 | async fn error_for_status(response: Response) -> Result {
44 | let status = response.status();
45 | if status.is_client_error() || status.is_server_error() {
46 | let url = response.url().to_string();
47 | return match response.text().await {
48 | Ok(text) => HttpErrorResponseSnafu {
49 | status,
50 | url,
51 | text: text.trim(),
52 | }
53 | .fail(),
54 | Err(encoding_error) => HttpErrorResponseUndecodableTextSnafu {
55 | status,
56 | url,
57 | encoding_error,
58 | }
59 | .fail(),
60 | };
61 | }
62 | Ok(response)
63 | }
64 |
--------------------------------------------------------------------------------
/.github/workflows/integration-test.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Integration Test
3 |
4 | on:
5 | # schedule:
6 | # # At 00:00 on Sunday. See: https://crontab.guru/#0_0_*_*_0
7 | # - cron: "0 0 * * 0"
8 | workflow_dispatch:
9 | inputs:
10 | test-mode:
11 | description: Test mode
12 | required: true
13 | type: choice
14 | options:
15 | - profile
16 | - custom
17 | test-mode-input:
18 | description: |
19 | The profile or the runner used. Eg: `smoke-latest` or `amd64` (see test/interu.yaml)
20 | required: true
21 | test-suite:
22 | description: Name of the test-suite. Only used if test-mode is `custom`
23 | test:
24 | description: Name of the test. Only used of test-mode is `custom`
25 |
26 | jobs:
27 | test:
28 | name: Run Integration Test
29 | runs-on: ubuntu-latest
30 | # services:
31 | # otel-collector:
32 | # image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-k8s:0.131.1
33 | # volumes:
34 | # - .:/mnt
35 | steps:
36 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
37 | with:
38 | persist-credentials: false
39 | submodules: recursive
40 |
41 | # TODO: Enable the scheduled runs which hard-code what profile to use
42 | - name: Run Integration Test
43 | id: test
44 | uses: stackabletech/actions/run-integration-test@29bea1b451c0c2e994bd495969286f95bf49ed6a # v0.11.0
45 | with:
46 | replicated-api-token: ${{ secrets.REPLICATED_API_TOKEN }}
47 | test-mode-input: ${{ inputs.test-mode-input }}
48 | test-suite: ${{ inputs.test-suite }}
49 | test-mode: ${{ inputs.test-mode }}
50 | test: ${{ inputs.test }}
51 |
52 | - name: Send Notification
53 | if: ${{ failure() || github.run_attempt > 1 }}
54 | uses: stackabletech/actions/send-slack-notification@29bea1b451c0c2e994bd495969286f95bf49ed6a # v0.11.0
55 | with:
56 | slack-token: ${{ secrets.SLACK_INTEGRATION_TEST_TOKEN }}
57 | failed-tests: ${{ steps.test.outputs.failed-tests }}
58 | test-health: ${{ steps.test.outputs.health }}
59 | test-result: ${{ steps.test.conclusion }}
60 | channel-id: C07UYJYSMSN # notifications-integration-tests
61 | type: integration-test
62 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | Expand the name of the chart.
3 | */}}
4 | {{- define "operator.name" -}}
5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-operator" }}
6 | {{- end }}
7 |
8 | {{/*
9 | Expand the name of the chart.
10 | */}}
11 | {{- define "operator.appname" -}}
12 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
13 | {{- end }}
14 |
15 | {{/*
16 | Create a default fully qualified app name.
17 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
18 | If release name contains chart name it will be used as a full name.
19 | */}}
20 | {{- define "operator.fullname" -}}
21 | {{- if .Values.fullnameOverride }}
22 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
23 | {{- else }}
24 | {{- $name := default .Chart.Name .Values.nameOverride }}
25 | {{- if contains $name .Release.Name }}
26 | {{- .Release.Name | trunc 63 | trimSuffix "-" }}
27 | {{- else }}
28 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
29 | {{- end }}
30 | {{- end }}
31 | {{- end }}
32 |
33 | {{/*
34 | Create chart name and version as used by the chart label.
35 | */}}
36 | {{- define "operator.chart" -}}
37 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
38 | {{- end }}
39 |
40 | {{/*
41 | Common labels
42 | */}}
43 | {{- define "operator.labels" -}}
44 | helm.sh/chart: {{ include "operator.chart" . }}
45 | {{ include "operator.selectorLabels" . }}
46 | {{- if .Chart.AppVersion }}
47 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
48 | {{- end }}
49 | app.kubernetes.io/managed-by: {{ .Release.Service }}
50 | {{- end }}
51 |
52 | {{/*
53 | Selector labels
54 | */}}
55 | {{- define "operator.selectorLabels" -}}
56 | app.kubernetes.io/name: {{ include "operator.appname" . }}
57 | app.kubernetes.io/instance: {{ .Release.Name }}
58 | {{- with .Values.labels }}
59 | {{ toYaml . }}
60 | {{- end }}
61 | {{- end }}
62 |
63 | {{/*
64 | Create the name of the service account to use
65 | */}}
66 | {{- define "operator.serviceAccountName" -}}
67 | {{- if .Values.serviceAccount.create }}
68 | {{- default (include "operator.fullname" .) .Values.serviceAccount.name }}
69 | {{- else }}
70 | {{- default "default" .Values.serviceAccount.name }}
71 | {{- end }}
72 | {{- end }}
73 |
74 | {{/*
75 | Labels for Kubernetes objects created by helm test
76 | */}}
77 | {{- define "operator.testLabels" -}}
78 | helm.sh/test: {{ include "operator.chart" . }}
79 | {{- end }}
80 |
--------------------------------------------------------------------------------
/rust/operator-binary/src/product_logging.rs:
--------------------------------------------------------------------------------
1 | use snafu::Snafu;
2 | use stackable_opa_operator::crd::v1alpha1;
3 | use stackable_operator::{
4 | builder::configmap::ConfigMapBuilder,
5 | product_logging::{
6 | self,
7 | spec::{ContainerLogConfig, ContainerLogConfigChoice, LogLevel, Logging},
8 | },
9 | role_utils::RoleGroupRef,
10 | };
11 |
12 | #[derive(Snafu, Debug)]
13 | pub enum Error {
14 | #[snafu(display("object has no namespace"))]
15 | ObjectHasNoNamespace,
16 | #[snafu(display("failed to retrieve the ConfigMap [{cm_name}]"))]
17 | ConfigMapNotFound {
18 | source: stackable_operator::client::Error,
19 | cm_name: String,
20 | },
21 | #[snafu(display("failed to retrieve the entry [{entry}] for ConfigMap [{cm_name}]"))]
22 | MissingConfigMapEntry {
23 | entry: &'static str,
24 | cm_name: String,
25 | },
26 | #[snafu(display("vectorAggregatorConfigMapName must be set"))]
27 | MissingVectorAggregatorAddress,
28 | }
29 |
30 | type Result = std::result::Result;
31 |
32 | #[derive(strum::Display)]
33 | #[strum(serialize_all = "UPPERCASE")]
34 | pub enum BundleBuilderLogLevel {
35 | Trace,
36 | Debug,
37 | Info,
38 | Warn,
39 | Error,
40 | }
41 |
42 | impl From for BundleBuilderLogLevel {
43 | fn from(level: LogLevel) -> Self {
44 | match level {
45 | LogLevel::TRACE => Self::Trace,
46 | LogLevel::DEBUG => Self::Debug,
47 | LogLevel::INFO => Self::Info,
48 | LogLevel::WARN => Self::Warn,
49 | LogLevel::ERROR | LogLevel::FATAL | LogLevel::NONE => Self::Error,
50 | }
51 | }
52 | }
53 |
54 | /// Extend the role group ConfigMap with logging and Vector configurations
55 | pub fn extend_role_group_config_map(
56 | rolegroup: &RoleGroupRef,
57 | logging: &Logging,
58 | cm_builder: &mut ConfigMapBuilder,
59 | ) -> Result<()> {
60 | let vector_log_config = if let Some(ContainerLogConfig {
61 | choice: Some(ContainerLogConfigChoice::Automatic(log_config)),
62 | }) = logging.containers.get(&v1alpha1::Container::Vector)
63 | {
64 | Some(log_config)
65 | } else {
66 | None
67 | };
68 |
69 | if logging.enable_vector_agent {
70 | cm_builder.add_data(
71 | product_logging::framework::VECTOR_CONFIG_FILE,
72 | product_logging::framework::create_vector_config(rolegroup, vector_log_config),
73 | );
74 | }
75 |
76 | Ok(())
77 | }
78 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/usage-guide/tls.adoc:
--------------------------------------------------------------------------------
1 | = Enabling TLS Encryption
2 | :description: Learn how to enable TLS encryption for your OPA cluster to secure client connections.
3 |
4 | TLS encryption for securing client connections to the OPA server can be configured in the `OpaCluster` resource. When enabled, OPA serves requests over HTTPS instead of HTTP.
5 |
6 | == Overview
7 |
8 | TLS encryption in OPA is disabled by default. To enable it, you need to:
9 |
10 | 1. Create a `SecretClass` that provides TLS certificates
11 | 2. Reference the `SecretClass` in your `OpaCluster` custom resource
12 |
13 | The operator integrates with the xref:secret-operator:index.adoc[Secret Operator] to automatically provision and mount TLS certificates into the OPA pods.
14 |
15 | == Configuration
16 |
17 | === Creating a SecretClass
18 |
19 | First, create a `SecretClass` that will provide TLS certificates. Here's an example using xref:secret-operator:secretclass.adoc#backend-autotls[autoTls]:
20 |
21 | [source,yaml]
22 | ----
23 | apiVersion: secrets.stackable.tech/v1alpha1
24 | kind: SecretClass
25 | metadata:
26 | name: opa-tls
27 | spec:
28 | backend:
29 | autoTls:
30 | ca:
31 | autoGenerate: true
32 | secret:
33 | name: opa-tls-ca
34 | namespace: default
35 | ----
36 |
37 | This SecretClass uses the autoTls backend, which automatically generates a Certificate Authority (CA) and signs certificates for your OPA cluster.
38 |
39 | Similarly, you can also use xref:secret-operator:secretclass.adoc#backend[other backends] supported by Secret Operator.
40 |
41 | === Enabling TLS in OpaCluster
42 |
43 | Once you have a SecretClass, enable TLS in your OpaCluster by setting the `.spec.clusterConfig.tls.serverSecretClass` field:
44 |
45 | [source,yaml]
46 | ----
47 | kind: OpaCluster
48 | name: opa-with-tls
49 | spec:
50 | clusterConfig:
51 | tls:
52 | serverSecretClass: opa-tls # <1>
53 | ----
54 | <1> Reference the SecretClass created above
55 |
56 | == Discovery ConfigMap
57 |
58 | The operator automatically creates a discovery ConfigMap, with the same name as the OPA cluster, that contains the connection URL for your cluster. When TLS is enabled, this ConfigMap will contain an HTTPS URL and the SecretClass name:
59 |
60 | [source,yaml]
61 | ----
62 | apiVersion: v1
63 | kind: ConfigMap
64 | metadata:
65 | name: opa-with-tls
66 | data:
67 | OPA: "https://opa-with-tls.default.svc.cluster.local:8443/"
68 | OPA_SECRET_CLASS: "opa-tls"
69 | ----
70 |
71 | Applications can use this ConfigMap to discover and connect to the OPA cluster automatically.
72 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/logging/opa-vector-aggregator-values.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | role: Aggregator
3 | service:
4 | ports:
5 | - name: api
6 | port: 8686
7 | protocol: TCP
8 | targetPort: 8686
9 | - name: vector
10 | port: 6123
11 | protocol: TCP
12 | targetPort: 6000
13 | customConfig:
14 | api:
15 | address: 0.0.0.0:8686
16 | enabled: true
17 | sources:
18 | vector:
19 | address: 0.0.0.0:6000
20 | type: vector
21 | version: "2"
22 | transforms:
23 | validEvents:
24 | type: filter
25 | inputs: [vector]
26 | condition: is_null(.errors)
27 | filteredAutomaticLogConfigServerOpaDecision:
28 | type: filter
29 | inputs: [validEvents]
30 | condition: >-
31 | starts_with(string!(.pod), "test-opa-server-automatic-log-config") &&
32 | .container == "opa" &&
33 | .logger == "decision"
34 | filteredAutomaticLogConfigServerOpa:
35 | type: filter
36 | inputs: [validEvents]
37 | condition: >-
38 | starts_with(string!(.pod), "test-opa-server-automatic-log-config") &&
39 | .container == "opa" &&
40 | .logger != "decision"
41 | filteredAutomaticLogConfigServerBundleBuilder:
42 | type: filter
43 | inputs: [validEvents]
44 | condition: >-
45 | starts_with(string!(.pod), "test-opa-server-automatic-log-config") &&
46 | .container == "bundle-builder"
47 | filteredAutomaticLogConfigServerVector:
48 | type: filter
49 | inputs: [validEvents]
50 | condition: >-
51 | starts_with(string!(.pod), "test-opa-server-automatic-log-config") &&
52 | .container == "vector"
53 | filteredAutomaticLogConfigServerPrepare:
54 | type: filter
55 | inputs: [validEvents]
56 | condition: >-
57 | starts_with(string!(.pod), "test-opa-server-automatic-log-config") &&
58 | .container == "prepare"
59 | filteredInvalidEvents:
60 | type: filter
61 | inputs: [vector]
62 | condition: |-
63 | .timestamp == from_unix_timestamp!(0) ||
64 | is_null(.level) ||
65 | is_null(.logger) ||
66 | is_null(.message)
67 | sinks:
68 | test:
69 | inputs: [filtered*]
70 | type: blackhole
71 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
72 | aggregator:
73 | inputs: [vector]
74 | type: vector
75 | address: {{ lookup('env', 'VECTOR_AGGREGATOR') }}
76 | buffer:
77 | # Avoid back pressure from VECTOR_AGGREGATOR. The test should
78 | # not fail if the aggregator is not available.
79 | when_full: drop_newest
80 | {% endif %}
81 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/ad-user-info/10-install-opa.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestStep
4 | commands:
5 | - script: |
6 | kubectl apply -n $NAMESPACE -f - < 0 %}
30 | custom: "{{ test_scenario['values']['opa-latest'].split(',')[1] }}"
31 | productVersion: "{{ test_scenario['values']['opa-latest'].split(',')[0] }}"
32 | {% else %}
33 | productVersion: "{{ test_scenario['values']['opa-latest'] }}"
34 | {% endif %}
35 | pullPolicy: IfNotPresent
36 | clusterConfig:
37 | userInfo:
38 | backend:
39 | experimentalActiveDirectory:
40 | ldapServer: sble-addc.sble.test
41 | baseDistinguishedName: DC=sble,DC=test
42 | customAttributeMappings:
43 | country: c
44 | kerberosSecretClassName: kerberos-ad
45 | tls:
46 | verification:
47 | server:
48 | caCert:
49 | secretClass: tls-ad
50 | cache: # optional, enabled by default
51 | entryTimeToLive: 60s # optional, defaults to 60s
52 | {% if lookup('env', 'VECTOR_AGGREGATOR') %}
53 | vectorAggregatorConfigMapName: vector-aggregator-discovery
54 | {% endif %}
55 | servers:
56 | config:
57 | logging:
58 | enableVectorAgent: {{ lookup('env', 'VECTOR_AGGREGATOR') | length > 0 }}
59 | roleGroups:
60 | default:
61 | podOverrides:
62 | spec:
63 | containers:
64 | - name: bundle-builder
65 | imagePullPolicy: IfNotPresent
66 | - name: user-info-fetcher
67 | imagePullPolicy: IfNotPresent
68 | env:
69 | - name: CONSOLE_LOG
70 | value: DEBUG
71 | - name: CONSOLE_LOG_LEVEL
72 | value: DEBUG
73 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/test-regorule.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import requests
3 | import argparse
4 | import json
5 |
6 |
7 | def assertions(
8 | username, response, opa_attribute, expected_groups, expected_attributes={}
9 | ):
10 | assert "result" in response
11 | result = response["result"]
12 | assert opa_attribute in result, f"expected {opa_attribute} in {result}"
13 |
14 | # repeated the right hand side for better output on error
15 | assert "customAttributes" in result[opa_attribute]
16 | assert "groups" in result[opa_attribute]
17 | assert "id" in result[opa_attribute]
18 | assert "username" in result[opa_attribute]
19 |
20 | # todo: split out group assertions
21 | print(f"Testing for {username} in groups {expected_groups}")
22 | groups = sorted(result[opa_attribute]["groups"])
23 | expected_groups = sorted(expected_groups)
24 | assert groups == expected_groups, f"got {groups}, expected: {expected_groups}"
25 |
26 | # todo: split out customAttribute assertions
27 | print(f"Testing for {username} with customAttributes {expected_attributes}")
28 | custom_attributes = result[opa_attribute]["customAttributes"]
29 | assert custom_attributes == expected_attributes, (
30 | f"got {custom_attributes}, expected: {expected_attributes}"
31 | )
32 |
33 |
34 | if __name__ == "__main__":
35 | all_args = argparse.ArgumentParser()
36 | all_args.add_argument("-u", "--url", required=True, help="OPA service url")
37 | args = vars(all_args.parse_args())
38 | params = {"strict-builtin-errors": "true"}
39 |
40 | def make_request(payload):
41 | response = requests.post(args["url"], data=json.dumps(payload), params=params)
42 | expected_status_code = 200
43 | assert response.status_code == expected_status_code, (
44 | f"got {response.status_code}, expected: {expected_status_code}"
45 | )
46 | return response.json()
47 |
48 | for subject_id in ["alice", "bob"]:
49 | try:
50 | # todo: try this out locally until it works
51 | # url = 'http://test-opa-svc:8081/v1/data'
52 | payload = {"input": {"id": subject_id}}
53 | response = make_request(payload)
54 | assertions(
55 | subject_id,
56 | response,
57 | "currentUserInfoById",
58 | [],
59 | {"e-mail": f"{subject_id}@example.com", "company": "openid"},
60 | )
61 | except Exception as e:
62 | print(f"exception: {e}")
63 | if response is not None:
64 | print(f"request body: {payload}")
65 | print(f"response body: {response}")
66 | raise e
67 |
68 | print("Test successful!")
69 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/getting_started/first_steps.adoc:
--------------------------------------------------------------------------------
1 | = First steps
2 | :description: Set up the OPA with Stackable operator, deploy your first policy rule, and query it from the command line in Kubernetes.
3 | :docs-policy-language: https://www.openpolicyagent.org/docs/latest/policy-language/
4 |
5 | After you went through the xref:getting_started/installation.adoc[], on this page you deploy OPA, deploy your first rule and query it from the command line.
6 |
7 | == Deploy OPA
8 |
9 | To deploy OPA, you create an OpaCluster resource in Kubernetes and the operator creates the OPA Stacklet.
10 | Create a file called `opa.yaml` with the following contents:
11 |
12 | [source,yaml]
13 | include::example$getting_started/opa.yaml[]
14 |
15 | and apply it:
16 |
17 | [source,bash]
18 | include::example$getting_started/getting_started.sh[tag=apply-opa-cluster]
19 |
20 | This creates an OPA cluster.
21 | The operator deploys a DaemonSet, which means that an OPA Pod is deployed on every Node of the cluster.
22 | This reduces network traffic and improves latency for decision requests, since every other Pod making decision requests only has to make its request to another port on the same Node.
23 |
24 | == Deploy a policy rule
25 |
26 | Now deploy the first {docs-policy-language}[policy rule] to OPA.
27 | Rules are deployed in ConfigMaps.
28 | Create a file `simple-rule.yaml` with the following contents:
29 |
30 | [source,yaml]
31 | ----
32 | include::example$getting_started/simple-rule.yaml[]
33 | ----
34 |
35 | and apply it:
36 |
37 | [source,bash]
38 | include::example$getting_started/getting_started.sh[tag=apply-rule-file]
39 |
40 | The operator reads the rule file, bundles it and publishes the bundle to all OPA Pods in the cluster.
41 |
42 | == Make policy requests
43 |
44 | Now that you have deployed the rule, you can query OPA for it.
45 | First, port-forward the service so you can query it from outside the Kubernetes cluster:
46 |
47 | [source,bash]
48 | include::example$getting_started/getting_started.sh[tag=port-forwarding]
49 |
50 | Then, request the `hello` rule:
51 |
52 | [source,bash]
53 | include::example$getting_started/getting_started.sh[tag=request-hello]
54 |
55 | As it was defined in the rule file, the response should be `true`:
56 |
57 | [source,json]
58 | include::example$getting_started/expected_response_hello.json[]
59 |
60 | You can also request the other rule, `world`:
61 |
62 | [source,bash]
63 | include::example$getting_started/getting_started.sh[tag=request-world]
64 |
65 | And see a different response:
66 |
67 | [source,json]
68 | include::example$getting_started/expected_response_world.json[]
69 |
70 | Great! You've set up OPA, deployed a rule and queried it!
71 |
72 | == What's next
73 |
74 | Have a look at the xref:usage-guide/index.adoc[] page for more configuration options of the operator.
75 |
--------------------------------------------------------------------------------
/tests/README-templating.md:
--------------------------------------------------------------------------------
1 | # Test Scenario Templating
2 |
3 | ## Introduction
4 |
5 | The tests in this directory are designed to be expanded into multiple test scenarios based on test dimensions that can be defined in a dimensions file.
6 |
7 | ## Defining Test Dimensions
8 |
9 | The dimensions file currently has to be named `test-definition.yaml` and reside in the same directory as the `kuttl-test.yaml.jinja2` file.
10 |
11 | An example of a minimal folder structure will be given further down in this file.
12 |
13 | An example of the content for the test definition file is shown here:
14 |
15 | ````yaml
16 | dimensions:
17 | - name: spark
18 | values:
19 | - 3.2.1
20 | - 3.2.2
21 | - 3.2.3
22 | - name: hadoop
23 | values:
24 | - 3.1.0
25 | - 3.2.0
26 | - name: aws
27 | - abc
28 | - xyz
29 | tests:
30 | - name: spark-pi-public-s3
31 | dimensions:
32 | - spark
33 | - hadoop
34 | ````
35 |
36 | This file defines three dimensions for this test to be considered.
37 | It also defines one test case named _spark-pi-public-s3_ and the dimensions that this test case should use.
38 | In this example the test case uses only two of the three dimensions defined, so a run of this test case would be expanded into the following test structure:
39 |
40 | ````text
41 | └── spark-pi-public-s3
42 | ├── spark-3.2.1_hadoop-3.1.0
43 | ├── spark-3.2.1_hadoop-3.2.0
44 | ├── spark-3.2.2_hadoop-3.1.0
45 | ├── spark-3.2.2_hadoop-3.2.0
46 | ├── spark-3.2.3_hadoop-3.1.0
47 | └── spark-3.2.3_hadoop-3.2.0
48 | ````
49 |
50 | The name of a test case defined under `tests` in this file has to refer back to a directory in the `templates/kuttl` directory, which will be used to create the test scenarios.
51 |
52 | Given the example of a test-definition.yaml shown above, the following folder structure would create the test scenarios shown above.
53 |
54 | ````text
55 | tests
56 | ├── kuttl-test.yaml.j2
57 | ├── templates
58 | │ └── kuttl
59 | │ └── spark-pi-public-s3
60 | └── test-definition.yaml
61 | ````
62 |
63 | The `kuttl-test.yaml.jinja2` cannot currently be edited, as it comes from the operator templating and any changes would be overwritten again.
64 | This should be fairly easy to solve and we can look at this as soon as it becomes necessary.
65 |
66 | ## Using
67 |
68 | ### Requirements
69 |
70 | To run tests locally you need the following things installed:
71 |
72 | - python3 (version >= 3.9)
73 | - pyyaml library installed
74 | - jq
75 |
76 | ### Running
77 |
78 | To run tests please execute the following command from the gitroot of the operator repository:
79 |
80 | `scripts/run_tests.sh --parallel 2`
81 |
82 | This will expand the test templates into all defined test scenarios and execute kuttl to test these scenarios. Any arguments are passed on to `kuttl`.
83 |
--------------------------------------------------------------------------------
/docs/modules/opa/pages/reference/environment-variables.adoc:
--------------------------------------------------------------------------------
1 | = Environment variables
2 |
3 | This operator accepts the following environment variables:
4 |
5 | == KUBERNETES_CLUSTER_DOMAIN
6 |
7 | *Default value*: cluster.local
8 |
9 | *Required*: false
10 |
11 | *Multiple values*: false
12 |
13 | This instructs the operator, which value it should use for the Kubernetes `clusterDomain` setting.
14 | Make sure to keep this in sync with whatever setting your cluster uses.
15 | Please see the documentation xref:guides:kubernetes-cluster-domain.adoc[on configuring the Kubernetes cluster domain] for more information on this feature.
16 |
17 | [source]
18 | ----
19 | export KUBERNETES_CLUSTER_DOMAIN=mycluster.local
20 | cargo run -- run
21 | ----
22 |
23 | or via docker:
24 |
25 | [source]
26 | ----
27 | docker run \
28 | --name opa-operator \
29 | --network host \
30 | --env KUBECONFIG=/home/stackable/.kube/config \
31 | --env KUBERNETES_CLUSTER_DOMAIN=mycluster.local \
32 | --mount type=bind,source="$HOME/.kube/config",target="/home/stackable/.kube/config" \
33 | oci.stackable.tech/sdp/opa-operator:0.0.0-dev
34 | ----
35 |
36 | == PRODUCT_CONFIG
37 |
38 | *Default value*: `/etc/stackable/opa-operator/config-spec/properties.yaml`
39 |
40 | *Required*: false
41 |
42 | *Multiple values*: false
43 |
44 | [source]
45 | ----
46 | export PRODUCT_CONFIG=/foo/bar/properties.yaml
47 | stackable-opa-operator run
48 | ----
49 |
50 | or via docker:
51 |
52 | ----
53 | docker run \
54 | --name opa-operator \
55 | --network host \
56 | --env KUBECONFIG=/home/stackable/.kube/config \
57 | --env PRODUCT_CONFIG=/my/product/config.yaml \
58 | --mount type=bind,source="$HOME/.kube/config",target="/home/stackable/.kube/config" \
59 | oci.stackable.tech/sdp/opa-operator:0.0.0-dev
60 | ----
61 |
62 | == WATCH_NAMESPACE
63 |
64 | *Default value*: All namespaces
65 |
66 | *Required*: false
67 |
68 | *Multiple values*: false
69 |
70 | The operator **only** watches for resources in the provided namespace `test`:
71 |
72 | [source]
73 | ----
74 | export WATCH_NAMESPACE=test
75 | stackable-opa-operator run
76 | ----
77 |
78 | or via docker:
79 |
80 | [source]
81 | ----
82 | docker run \
83 | --name opa-operator \
84 | --network host \
85 | --env KUBECONFIG=/home/stackable/.kube/config \
86 | --env WATCH_NAMESPACE=test \
87 | --mount type=bind,source="$HOME/.kube/config",target="/home/stackable/.kube/config" \
88 | oci.stackable.tech/sdp/opa-operator:0.0.0-dev
89 | ----
90 |
91 | == OPA_BUNDLE_BUILDER_CLUSTERROLE
92 |
93 | *Default value*: None.
94 |
95 | *Required*: true
96 |
97 | *Multiple values*: false
98 |
99 | The name of the cluster role to use for the OPA pods.
100 |
101 | [source]
102 | ----
103 | export OPA_BUNDLE_BUILDER_CLUSTERROLE=test
104 | stackable-opa-operator run
105 | ----
106 |
--------------------------------------------------------------------------------
/scripts/ensure_one_trailing_newline.py:
--------------------------------------------------------------------------------
1 | """
2 | Given the location of a file, trims all trailing blank lines and
3 | places a single one. Used as post-processing step for README rendering.
4 | """
5 |
6 | import re
7 | import unittest
8 |
9 | BLANK_LINE_REGEX_PATTERN = r"^\s*$"
10 |
11 |
12 | def has_trailing_newline(line):
13 | return line[-1:] == "\n"
14 |
15 |
16 | def process_lines(lines):
17 | trim_count = 0
18 | # trim trailing blank lines
19 | for line in lines[::-1]:
20 | if re.match(BLANK_LINE_REGEX_PATTERN, line):
21 | trim_count += 1
22 | else:
23 | break
24 |
25 | cutoff_index = len(lines) - trim_count
26 | new_lines = lines[:cutoff_index]
27 |
28 | # maybe add a newline character to the last sensible line
29 | if not has_trailing_newline(new_lines[-1]):
30 | new_lines[-1] = new_lines[-1] + "\n"
31 |
32 | # add a trailing blank line without newline
33 | new_lines.append("")
34 | return new_lines
35 |
36 |
37 | class TestCoreMethods(unittest.TestCase):
38 | def test_trailing_new_line(self):
39 | self.assertTrue(has_trailing_newline("something\n"))
40 | self.assertTrue(has_trailing_newline("\n"))
41 | self.assertFalse(has_trailing_newline("nope"))
42 |
43 | def test_trailing_real_line(self):
44 | lines = ["bla\n", "useful"]
45 | processed_lines = process_lines(lines)
46 | self.assertEqual(len(processed_lines), 3)
47 | self.assertTrue(has_trailing_newline(processed_lines[0]))
48 | self.assertTrue(has_trailing_newline(processed_lines[1]))
49 | self.assertFalse(has_trailing_newline(processed_lines[2]))
50 |
51 | def test_lots_of_empties(self):
52 | lines = ["bla\n", "\n", "\n", "\n", "\n"]
53 | processed_lines = process_lines(lines)
54 | self.assertEqual(len(processed_lines), 2)
55 | self.assertEqual(processed_lines[-1], "")
56 |
57 | def test_one_trailing_new_line(self):
58 | lines = ["bla\n", "\n"]
59 | processed_lines = process_lines(lines)
60 | self.assertEqual(len(processed_lines), 2)
61 | self.assertEqual(processed_lines[-1], "")
62 |
63 | def test_one_trailing_blank_line(self):
64 | lines = ["bla\n", ""]
65 | processed_lines = process_lines(lines)
66 | self.assertEqual(len(processed_lines), 2)
67 | self.assertEqual(processed_lines[-1], "")
68 |
69 |
70 | if __name__ == "__main__":
71 | # to run tests for this script:
72 | # python3 -m unittest ensure_one_trailing_newline.py
73 |
74 | import sys
75 |
76 | if len(sys.argv) != 2:
77 | print("Usage: {} filename_to_trim".format(sys.argv[0]))
78 | exit(1)
79 |
80 | file_name = sys.argv[1]
81 |
82 | lines = []
83 | with open(file_name, "r") as f:
84 | lines = f.readlines()
85 |
86 | lines = process_lines(lines)
87 |
88 | with open(file_name, "w") as f:
89 | f.write("".join(lines))
90 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/test-regorule.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import requests
3 | import argparse
4 | import json
5 |
6 | # todo: make the test more comprehensive to check customAttributes
7 | users_and_groups = {
8 | "alice": ["/superset-admin"],
9 | "bob": [],
10 | }
11 |
12 |
13 | def assertions(
14 | username, response, opa_attribute, expected_groups, expected_attributes={}
15 | ):
16 | assert "result" in response
17 | result = response["result"]
18 | assert opa_attribute in result, f"expected {opa_attribute} in {result}"
19 |
20 | # repeated the right hand side for better output on error
21 | assert "customAttributes" in result[opa_attribute]
22 | assert "groups" in result[opa_attribute]
23 | assert "id" in result[opa_attribute]
24 | assert "username" in result[opa_attribute]
25 |
26 | # todo: split out group assertions
27 | print(f"Testing for {username} in groups {expected_groups}")
28 | groups = sorted(result[opa_attribute]["groups"])
29 | expected_groups = sorted(expected_groups)
30 | assert groups == expected_groups, f"got {groups}, expected: {expected_groups}"
31 |
32 | # todo: split out customAttribute assertions
33 | print(f"Testing for {username} with customAttributes {expected_attributes}")
34 | custom_attributes = result[opa_attribute]["customAttributes"]
35 | assert custom_attributes == expected_attributes, (
36 | f"got {custom_attributes}, expected: {expected_attributes}"
37 | )
38 |
39 |
40 | if __name__ == "__main__":
41 | all_args = argparse.ArgumentParser()
42 | all_args.add_argument("-u", "--url", required=True, help="OPA service url")
43 | args = vars(all_args.parse_args())
44 | params = {"strict-builtin-errors": "true"}
45 |
46 | def make_request(payload):
47 | response = requests.post(args["url"], data=json.dumps(payload), params=params)
48 | expected_status_code = 200
49 | assert response.status_code == expected_status_code, (
50 | f"got {response.status_code}, expected: {expected_status_code}"
51 | )
52 | return response.json()
53 |
54 | for username, groups in users_and_groups.items():
55 | try:
56 | # todo: try this out locally until it works
57 | # url = 'http://test-opa-svc:8081/v1/data'
58 | payload = {"input": {"username": username}}
59 | response = make_request(payload)
60 | assertions(username, response, "currentUserInfoByUsername", groups, {})
61 |
62 | # do the reverse lookup
63 | user_id = response["result"]["currentUserInfoByUsername"]["id"]
64 | payload = {"input": {"id": user_id}}
65 | response = make_request(payload)
66 | assertions(username, response, "currentUserInfoById", groups, {})
67 | except Exception as e:
68 | print(f"exception: {e}")
69 | if response is not None:
70 | print(f"request body: {payload}")
71 | print(f"response body: {response}")
72 | raise e
73 |
74 | print("Test successful!")
75 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # =============
2 | # This file is automatically generated from the templates in stackabletech/operator-templating
3 | # DO NOT MANUALLY EDIT THIS FILE
4 | # =============
5 |
6 | # This script requires https://github.com/mikefarah/yq (not to be confused with https://github.com/kislyuk/yq)
7 | # It is available from Nixpkgs as `yq-go` (`nix shell nixpkgs#yq-go`)
8 | # This script also requires `jq` https://stedolan.github.io/jq/
9 |
10 | .PHONY: build publish
11 |
12 | OPERATOR_NAME := opa-operator
13 | VERSION := $(shell cargo metadata --format-version 1 | jq -r '.packages[] | select(.name=="stackable-${OPERATOR_NAME}") | .version')
14 |
15 | OCI_REGISTRY_HOSTNAME := oci.stackable.tech
16 | OCI_REGISTRY_PROJECT_IMAGES := sdp
17 |
18 | SHELL=/usr/bin/env bash -euo pipefail
19 |
20 | render-readme:
21 | scripts/render_readme.sh
22 |
23 | render-docs:
24 | scripts/docs_templating.sh
25 |
26 | ## Docker related targets
27 | docker-build:
28 | docker build --force-rm --build-arg VERSION=${VERSION} -t "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}-${ARCH}" -f docker/Dockerfile .
29 |
30 | ## Chart related targets
31 | compile-chart: version crds config
32 |
33 | chart-clean:
34 | rm -rf "deploy/helm/${OPERATOR_NAME}/configs"
35 | rm -rf "deploy/helm/${OPERATOR_NAME}/crds"
36 |
37 | version:
38 | cat "deploy/helm/${OPERATOR_NAME}/Chart.yaml" | yq ".version = \"${VERSION}\" | .appVersion = \"${VERSION}\"" > "deploy/helm/${OPERATOR_NAME}/Chart.yaml.new"
39 | mv "deploy/helm/${OPERATOR_NAME}/Chart.yaml.new" "deploy/helm/${OPERATOR_NAME}/Chart.yaml"
40 |
41 | config:
42 | if [ -d "deploy/config-spec/" ]; then\
43 | mkdir -p "deploy/helm/${OPERATOR_NAME}/configs";\
44 | cp -r deploy/config-spec/* "deploy/helm/${OPERATOR_NAME}/configs";\
45 | fi
46 |
47 | crds:
48 | mkdir -p deploy/helm/"${OPERATOR_NAME}"/crds
49 | cargo run --bin stackable-"${OPERATOR_NAME}" -- crd | yq eval '.metadata.annotations["helm.sh/resource-policy"]="keep"' - > "deploy/helm/${OPERATOR_NAME}/crds/crds.yaml"
50 |
51 | chart-lint: compile-chart
52 | docker run -it -v $(shell pwd):/build/helm-charts -w /build/helm-charts quay.io/helmpack/chart-testing:v3.5.0 ct lint --config deploy/helm/ct.yaml
53 |
54 | clean: chart-clean
55 | cargo clean
56 | docker rmi --force '${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}'
57 |
58 | regenerate-charts: chart-clean compile-chart
59 |
60 | regenerate-nix:
61 | nix run --extra-experimental-features "nix-command flakes" -f . regenerateNixLockfiles
62 |
63 | build: regenerate-charts regenerate-nix docker-build
64 |
65 | check-nix:
66 | @which nix || (echo "Error: 'nix' is not installed. Please install it to proceed."; exit 1)
67 |
68 | check-kubernetes:
69 | @kubectl cluster-info > /dev/null 2>&1 || (echo "Error: Kubernetes is not running or kubectl is not properly configured."; exit 1)
70 |
71 | run-dev: check-nix check-kubernetes
72 | kubectl apply -f deploy/stackable-operators-ns.yaml
73 | nix run --extra-experimental-features "nix-command flakes" -f. tilt -- up --port 5439 --namespace stackable-operators
74 |
75 | stop-dev: check-nix check-kubernetes
76 | nix run --extra-experimental-features "nix-command flakes" -f. tilt -- down
77 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/aas-user-info/02-install-aas.yaml.j2:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: ConfigMap
4 | metadata:
5 | name: aas-config
6 | data:
7 | aas.py: |
8 | from http.server import BaseHTTPRequestHandler, HTTPServer
9 | from urllib.parse import urlparse, parse_qs
10 | import json
11 |
12 | class MockServiceHandler(BaseHTTPRequestHandler):
13 | def _set_headers(self, status_code=200):
14 | self.send_response(status_code)
15 | self.send_header('Content-type', 'application/json')
16 | self.end_headers()
17 |
18 | def do_GET(self):
19 | print("received a get request: ", self.path)
20 | parsed_path = urlparse(self.path)
21 | path = parsed_path.path
22 |
23 | if path == '/cip/claims':
24 | query_components = parse_qs(parsed_path.query)
25 | sub = query_components.get('sub', [''])[0]
26 | scope = query_components.get('scope', [''])[0]
27 |
28 | if not sub or not scope:
29 | self._set_headers(400)
30 | self.wfile.write(json.dumps({'error': 'Both "sub" and "scope" parameters are required.'}).encode('utf-8'))
31 | return
32 |
33 | claims = {
34 | 'sub': sub,
35 | 'e-mail': f"{sub}@example.com",
36 | 'company': scope
37 | }
38 |
39 | self._set_headers()
40 | self.wfile.write(json.dumps(claims).encode('utf-8'))
41 | else:
42 | self._set_headers(404)
43 | self.wfile.write(json.dumps({'error': 'Endpoint not found'}).encode('utf-8'))
44 |
45 | def run(server_class=HTTPServer, handler_class=MockServiceHandler, port=5000):
46 | server_address = ('', port)
47 | httpd = server_class(server_address, handler_class)
48 | print(f"Starting mock server on port {port}...")
49 | httpd.serve_forever()
50 |
51 | if __name__ == "__main__":
52 | run()
53 | ---
54 | apiVersion: apps/v1
55 | kind: Deployment
56 | metadata:
57 | name: aas
58 | spec:
59 | replicas: 1
60 | selector:
61 | matchLabels:
62 | app: aas
63 | template:
64 | metadata:
65 | labels:
66 | app: aas
67 | spec:
68 | serviceAccountName: test-sa
69 | containers:
70 | - name: python
71 | image: python:3.9
72 | command: ["python", "/aas.py"]
73 | ports:
74 | - containerPort: 5000
75 | resources:
76 | limits:
77 | cpu: 1
78 | memory: 512Mi
79 | requests:
80 | cpu: 500m
81 | memory: 512Mi
82 | volumeMounts:
83 | - name: app-volume
84 | mountPath: /aas.py
85 | subPath: aas.py
86 | volumes:
87 | - name: app-volume
88 | configMap:
89 | name: aas-config
90 | ---
91 | apiVersion: v1
92 | kind: Service
93 | metadata:
94 | name: aas
95 | spec:
96 | selector:
97 | app: aas
98 | ports:
99 | - protocol: TCP
100 | port: 5000
101 | targetPort: 5000
102 | type: ClusterIP
103 |
--------------------------------------------------------------------------------
/rust/user-info-fetcher/src/utils/tls.rs:
--------------------------------------------------------------------------------
1 | use std::{io::Cursor, path::Path};
2 |
3 | use snafu::{ResultExt as _, Snafu};
4 | use stackable_operator::commons::tls_verification::TlsClientDetails;
5 | use tokio::{fs::File, io::AsyncReadExt};
6 |
7 | #[derive(Debug, Snafu)]
8 | pub enum Error {
9 | #[snafu(display("failed to read ca certificates"))]
10 | ReadCaBundle { source: std::io::Error },
11 |
12 | #[snafu(display("failed to parse ca certificates (via reqwest)"))]
13 | ParseCaBundleReqwest { source: reqwest::Error },
14 |
15 | #[snafu(display("failed to split ca certificate bundle"))]
16 | SplitCaBundle { source: std::io::Error },
17 |
18 | #[snafu(display("failed to parse ca certificate (via native_tls)"))]
19 | ParseCaCertNativeTls { source: native_tls::Error },
20 |
21 | #[snafu(display("failed to build native_tls connector"))]
22 | BuildNativeTlsConnector { source: native_tls::Error },
23 | }
24 |
25 | /// Configures a [`reqwest`] client according to the specified TLS configuration
26 | // NOTE: MUST be kept in sync with all configure_* functions
27 | pub async fn configure_reqwest(
28 | tls: &TlsClientDetails,
29 | builder: reqwest::ClientBuilder,
30 | ) -> Result {
31 | Ok(if tls.uses_tls() && !tls.uses_tls_verification() {
32 | builder.danger_accept_invalid_certs(true)
33 | } else if let Some(tls_ca_cert_mount_path) = tls.tls_ca_cert_mount_path() {
34 | reqwest::Certificate::from_pem_bundle(
35 | &read_file(&tls_ca_cert_mount_path)
36 | .await
37 | .context(ReadCaBundleSnafu)?,
38 | )
39 | .context(ParseCaBundleReqwestSnafu)?
40 | .into_iter()
41 | .fold(
42 | builder.tls_built_in_root_certs(false),
43 | reqwest::ClientBuilder::add_root_certificate,
44 | )
45 | } else {
46 | builder
47 | })
48 | }
49 |
50 | /// Configures a [`native_tls`] connector according to the specified TLS configuration
51 | // NOTE: MUST be kept in sync with all configure_* functions
52 | pub async fn configure_native_tls(
53 | tls: &TlsClientDetails,
54 | ) -> Result {
55 | let mut builder = native_tls::TlsConnector::builder();
56 | if tls.uses_tls() && !tls.uses_tls_verification() {
57 | builder.danger_accept_invalid_certs(true);
58 | } else if let Some(tls_ca_cert_mount_path) = tls.tls_ca_cert_mount_path() {
59 | builder.disable_built_in_roots(true);
60 | // native-tls doesn't support parsing CA *bundles*, so split them using rustls first
61 | for ca_cert in rustls_pemfile::certs(&mut Cursor::new(
62 | read_file(&tls_ca_cert_mount_path)
63 | .await
64 | .context(ReadCaBundleSnafu)?,
65 | )) {
66 | builder.add_root_certificate(
67 | native_tls::Certificate::from_der(&ca_cert.context(SplitCaBundleSnafu)?)
68 | .context(ParseCaCertNativeTlsSnafu)?,
69 | );
70 | }
71 | }
72 | builder.build().context(BuildNativeTlsConnectorSnafu)
73 | }
74 |
75 | async fn read_file(path: &impl AsRef) -> Result, std::io::Error> {
76 | let mut buf = Vec::::new();
77 | File::open(path).await?.read_to_end(&mut buf).await?;
78 | Ok(buf)
79 | }
80 |
--------------------------------------------------------------------------------
/deploy/helm/opa-operator/templates/roles.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: {{ include "operator.fullname" . }}-clusterrole
6 | labels:
7 | {{- include "operator.labels" . | nindent 4 }}
8 | rules:
9 | - apiGroups:
10 | - ""
11 | resources:
12 | - nodes
13 | verbs:
14 | - list
15 | - watch
16 | # For automatic cluster domain detection
17 | - apiGroups:
18 | - ""
19 | resources:
20 | - nodes/proxy
21 | verbs:
22 | - get
23 | - apiGroups:
24 | - ""
25 | resources:
26 | - pods
27 | - configmaps
28 | - secrets
29 | - services
30 | - endpoints
31 | - serviceaccounts
32 | verbs:
33 | - create
34 | - delete
35 | - get
36 | - list
37 | - patch
38 | - update
39 | - watch
40 | - apiGroups:
41 | - rbac.authorization.k8s.io
42 | resources:
43 | - rolebindings
44 | verbs:
45 | - create
46 | - delete
47 | - get
48 | - list
49 | - patch
50 | - update
51 | - watch
52 | - apiGroups:
53 | - apps
54 | resources:
55 | - daemonsets
56 | verbs:
57 | - get
58 | - create
59 | - delete
60 | - list
61 | - patch
62 | - update
63 | - watch
64 | - apiGroups:
65 | - batch
66 | resources:
67 | - jobs
68 | verbs:
69 | - create
70 | - get
71 | - list
72 | - patch
73 | - update
74 | - watch
75 | - apiGroups:
76 | - apiextensions.k8s.io
77 | resources:
78 | - customresourcedefinitions
79 | verbs:
80 | - get
81 | - apiGroups:
82 | - events.k8s.io
83 | resources:
84 | - events
85 | verbs:
86 | - create
87 | - patch
88 | - apiGroups:
89 | - {{ include "operator.name" . }}.stackable.tech
90 | resources:
91 | - {{ include "operator.name" . }}clusters
92 | verbs:
93 | - get
94 | - list
95 | - patch
96 | - watch
97 | - apiGroups:
98 | - {{ include "operator.name" . }}.stackable.tech
99 | resources:
100 | - {{ include "operator.name" . }}clusters/status
101 | verbs:
102 | - patch
103 | - apiGroups:
104 | - rbac.authorization.k8s.io
105 | resources:
106 | - clusterroles
107 | verbs:
108 | - bind
109 | resourceNames:
110 | - {{ include "operator.name" . }}-clusterrole
111 |
112 | ---
113 | apiVersion: rbac.authorization.k8s.io/v1
114 | kind: ClusterRole
115 | metadata:
116 | name: {{ include "operator.name" . }}-clusterrole
117 | labels:
118 | {{- include "operator.labels" . | nindent 4 }}
119 | rules:
120 | - apiGroups:
121 | - ""
122 | resources:
123 | - configmaps
124 | - secrets
125 | - serviceaccounts
126 | verbs:
127 | - get
128 | - list
129 | - watch
130 | - apiGroups:
131 | - events.k8s.io
132 | resources:
133 | - events
134 | verbs:
135 | - create
136 | - patch
137 | {{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }}
138 | - apiGroups:
139 | - security.openshift.io
140 | resources:
141 | - securitycontextconstraints
142 | resourceNames:
143 | - nonroot-v2
144 | verbs:
145 | - use
146 | {{ end }}
147 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/getting_started.sh:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 | set -euo pipefail
3 |
4 | # DO NOT EDIT THE SCRIPT
5 | # Instead, update the j2 template, and regenerate it for dev with `make render-docs`.
6 |
7 | # This script contains all the code snippets from the guide, as well as some assert tests
8 | # to test if the instructions in the guide work. The user *could* use it, but it is intended
9 | # for testing only.
10 | # The script will install the operator, create an OPA instance and briefly open a port
11 | # forward and query the OPA.
12 | # No running processes are left behind (i.e. the port-forwarding is closed at the end)
13 |
14 | if [ $# -eq 0 ]
15 | then
16 | echo "Installation method argument ('helm' or 'stackablectl') required."
17 | exit 1
18 | fi
19 |
20 | case "$1" in
21 | "helm")
22 | echo "Installing operators with Helm"
23 | # tag::helm-install-operators[]
24 | helm install --wait opa-operator oci://oci.stackable.tech/sdp-charts/opa-operator --version 0.0.0-dev
25 | # end::helm-install-operators[]
26 | ;;
27 | "stackablectl")
28 | echo "installing operators with stackablectl"
29 | # tag::stackablectl-install-operators[]
30 | stackablectl operator install opa=0.0.0-dev
31 | # end::stackablectl-install-operators[]
32 | ;;
33 | *)
34 | echo "Need to provide 'helm' or 'stackablectl' as an argument for which installation method to use!"
35 | exit 1
36 | ;;
37 | esac
38 |
39 | echo "Creating OPA cluster"
40 | # tag::apply-opa-cluster[]
41 | kubectl apply -f opa.yaml
42 | # end::apply-opa-cluster[]
43 |
44 | sleep 15
45 |
46 | echo "Waiting on rollout ..."
47 | kubectl rollout status --watch --timeout=5m daemonset/simple-opa-server-default
48 |
49 | echo "Applying the rule file"
50 | # tag::apply-rule-file[]
51 | kubectl apply -f simple-rule.yaml
52 | # end::apply-rule-file[]
53 |
54 | # The bundle builder will update the bundle almost immediately, but OPA can take up to
55 | # max_delay_seconds: 20 (see ConfigMap)
56 | # to poll the bundle
57 | sleep 21
58 |
59 | echo "Starting port-forwarding of port 8081"
60 | # tag::port-forwarding[]
61 | kubectl port-forward svc/simple-opa-server 8081 > /dev/null 2>&1 &
62 | # end::port-forwarding[]
63 | PORT_FORWARD_PID=$!
64 | # shellcheck disable=SC2064
65 | trap "kill $PORT_FORWARD_PID" EXIT
66 | sleep 5
67 |
68 | request_hello() {
69 | # tag::request-hello[]
70 | curl -s http://localhost:8081/v1/data/test/hello -d '{"input": {}}'
71 | # end::request-hello[]
72 | }
73 |
74 | echo "Checking policy decision for 'hello' rule ..."
75 | test_hello=$(request_hello)
76 | if [ "$test_hello" == "$(cat expected_response_hello.json)" ]; then
77 | echo "The 'hello' rule returned the correct response!"
78 | else
79 | echo "The 'hello' rule returned an incorrect response."
80 | echo "Received: $test_hello"
81 | echo "Expected: $(cat expected_response_hello.json)"
82 | exit 1
83 | fi
84 |
85 | request_world() {
86 | # tag::request-world[]
87 | curl -s http://localhost:8081/v1/data/test/world -d '{"input": {}}'
88 | # end::request-world[]
89 | }
90 |
91 | echo "Checking policy decision for 'world' rule ..."
92 | test_world=$(request_world)
93 | if [ "$test_world" == "$(cat expected_response_world.json)" ]; then
94 | echo "The 'world' rule returned the correct response!"
95 | else
96 | echo "The 'world' rule returned an incorrect response."
97 | echo "Received: $test_world"
98 | echo "Expected: $(cat expected_response_world.json)"
99 | exit 1
100 | fi
101 |
--------------------------------------------------------------------------------
/docs/modules/opa/examples/getting_started/getting_started.sh.j2:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 | set -euo pipefail
3 |
4 | # DO NOT EDIT THE SCRIPT
5 | # Instead, update the j2 template, and regenerate it for dev with `make render-docs`.
6 |
7 | # This script contains all the code snippets from the guide, as well as some assert tests
8 | # to test if the instructions in the guide work. The user *could* use it, but it is intended
9 | # for testing only.
10 | # The script will install the operator, create an OPA instance and briefly open a port
11 | # forward and query the OPA.
12 | # No running processes are left behind (i.e. the port-forwarding is closed at the end)
13 |
14 | if [ $# -eq 0 ]
15 | then
16 | echo "Installation method argument ('helm' or 'stackablectl') required."
17 | exit 1
18 | fi
19 |
20 | case "$1" in
21 | "helm")
22 | echo "Installing operators with Helm"
23 | # tag::helm-install-operators[]
24 | helm install --wait opa-operator oci://{{ helm.repo_url }}/{{ helm.repo_name }}/opa-operator --version {{ versions.opa }}
25 | # end::helm-install-operators[]
26 | ;;
27 | "stackablectl")
28 | echo "installing operators with stackablectl"
29 | # tag::stackablectl-install-operators[]
30 | stackablectl operator install opa={{ versions.opa }}
31 | # end::stackablectl-install-operators[]
32 | ;;
33 | *)
34 | echo "Need to provide 'helm' or 'stackablectl' as an argument for which installation method to use!"
35 | exit 1
36 | ;;
37 | esac
38 |
39 | echo "Creating OPA cluster"
40 | # tag::apply-opa-cluster[]
41 | kubectl apply -f opa.yaml
42 | # end::apply-opa-cluster[]
43 |
44 | sleep 15
45 |
46 | echo "Waiting on rollout ..."
47 | kubectl rollout status --watch --timeout=5m daemonset/simple-opa-server-default
48 |
49 | echo "Applying the rule file"
50 | # tag::apply-rule-file[]
51 | kubectl apply -f simple-rule.yaml
52 | # end::apply-rule-file[]
53 |
54 | # The bundle builder will update the bundle almost immediately, but OPA can take up to
55 | # max_delay_seconds: 20 (see ConfigMap)
56 | # to poll the bundle
57 | sleep 21
58 |
59 | echo "Starting port-forwarding of port 8081"
60 | # tag::port-forwarding[]
61 | kubectl port-forward svc/simple-opa-server 8081 > /dev/null 2>&1 &
62 | # end::port-forwarding[]
63 | PORT_FORWARD_PID=$!
64 | # shellcheck disable=SC2064
65 | trap "kill $PORT_FORWARD_PID" EXIT
66 | sleep 5
67 |
68 | request_hello() {
69 | # tag::request-hello[]
70 | curl -s http://localhost:8081/v1/data/test/hello -d '{"input": {}}'
71 | # end::request-hello[]
72 | }
73 |
74 | echo "Checking policy decision for 'hello' rule ..."
75 | test_hello=$(request_hello)
76 | if [ "$test_hello" == "$(cat expected_response_hello.json)" ]; then
77 | echo "The 'hello' rule returned the correct response!"
78 | else
79 | echo "The 'hello' rule returned an incorrect response."
80 | echo "Received: $test_hello"
81 | echo "Expected: $(cat expected_response_hello.json)"
82 | exit 1
83 | fi
84 |
85 | request_world() {
86 | # tag::request-world[]
87 | curl -s http://localhost:8081/v1/data/test/world -d '{"input": {}}'
88 | # end::request-world[]
89 | }
90 |
91 | echo "Checking policy decision for 'world' rule ..."
92 | test_world=$(request_world)
93 | if [ "$test_world" == "$(cat expected_response_world.json)" ]; then
94 | echo "The 'world' rule returned the correct response!"
95 | else
96 | echo "The 'world' rule returned an incorrect response."
97 | echo "Received: $test_world"
98 | echo "Expected: $(cat expected_response_world.json)"
99 | exit 1
100 | fi
101 |
--------------------------------------------------------------------------------
/tests/templates/kuttl/keycloak-user-info/04-keycloak-realm-cm.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Secret
4 | metadata:
5 | name: user-info-fetcher-client-credentials
6 | stringData:
7 | clientId: user-info-fetcher
8 | clientSecret: user-info-fetcher-client-secret
9 | ---
10 | apiVersion: v1
11 | kind: ConfigMap
12 | metadata:
13 | name: keycloak-my-dataspace-realm
14 | data:
15 | realm.json: |
16 | {
17 | "realm" : "my-dataspace",
18 | "enabled" : true,
19 | "groups" : [ {
20 | "name" : "superset-admin",
21 | "path" : "/superset-admin"
22 | } ],
23 | "users" : [ {
24 | "username" : "alice",
25 | "enabled" : true,
26 | "emailVerified" : true,
27 | "firstName" : "Alice",
28 | "lastName" : "Adams",
29 | "email" : "alice@example.org",
30 | "credentials" : [ {
31 | "type" : "password",
32 | "userLabel" : "My password",
33 | "secretData" : "{\"value\":\"hogrQRLTAPBws9RgxZF/d3+EvPvUc7AN1egnmMnuWBQ=\",\"salt\":\"DYkgbXwZ2uhvJ+k94Xr7lg==\",\"additionalParameters\":{}}",
34 | "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
35 | } ],
36 | "realmRoles" : [ "default-roles-my-dataspace" ],
37 | "groups" : [ "/superset-admin" ]
38 | }, {
39 | "username" : "bob",
40 | "enabled" : true,
41 | "emailVerified" : true,
42 | "firstName" : "Bob",
43 | "lastName" : "Bush",
44 | "email" : "bob@example.org",
45 | "credentials" : [ {
46 | "type" : "password",
47 | "userLabel" : "My password",
48 | "secretData" : "{\"value\":\"FC3TRP//H5izxRRQsYnBDucCI65OVxMy4GgG3qyl/Ek=\",\"salt\":\"kcwgkKFSJ83xlwDtOACoZQ==\",\"additionalParameters\":{}}",
49 | "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
50 | } ],
51 | "realmRoles" : [ "default-roles-my-dataspace" ],
52 | "groups" : [ ]
53 | }, {
54 | "username" : "service-account-user-info-fetcher",
55 | "enabled" : true,
56 | "totp" : false,
57 | "emailVerified" : false,
58 | "serviceAccountClientId" : "user-info-fetcher",
59 | "credentials" : [ ],
60 | "disableableCredentialTypes" : [ ],
61 | "requiredActions" : [ ],
62 | "realmRoles" : [ "default-roles-my-dataspace" ],
63 | "clientRoles" : {
64 | "realm-management" : [
65 | "view-users"
66 | ]
67 | },
68 | "notBefore" : 0,
69 | "groups" : [ ]
70 | } ],
71 | "clients" : [ {
72 | "clientId" : "${USER_INFO_FETCHER_CLIENT_ID}",
73 | "surrogateAuthRequired" : false,
74 | "enabled" : true,
75 | "alwaysDisplayInConsole" : false,
76 | "clientAuthenticatorType" : "client-secret",
77 | "secret" : "${USER_INFO_FETCHER_CLIENT_SECRET}",
78 | "redirectUris" : [ "/*" ],
79 | "webOrigins" : [ "/*" ],
80 | "notBefore" : 0,
81 | "bearerOnly" : false,
82 | "serviceAccountsEnabled" : true,
83 | "publicClient" : false,
84 | "frontchannelLogout" : true,
85 | "protocol" : "openid-connect",
86 | "attributes" : {
87 | "oidc.ciba.grant.enabled" : "true",
88 | "oauth2.device.authorization.grant.enabled" : "false"
89 | },
90 | "authenticationFlowBindingOverrides" : { },
91 | "fullScopeAllowed" : true
92 | } ]
93 | }
94 |
--------------------------------------------------------------------------------
/.readme/partials/borrowed/footer.md.j2:
--------------------------------------------------------------------------------
1 |
2 | ## About The Stackable Data Platform
3 |
4 | This operator is written and maintained by [Stackable](https://stackable.tech) and it is part of a larger data platform.
5 |
6 | 
7 |
8 | Stackable makes it easy to operate data applications in any Kubernetes cluster.
9 |
10 | The data platform offers many operators, new ones being added continuously. All our operators are designed and built to be easily interconnected and to be consistent to work with.
11 |
12 | The [Stackable GmbH](https://stackable.tech/) is the company behind the Stackable Data Platform. Offering professional services, paid support plans and custom development.
13 |
14 | We love open-source!
15 |
16 | ## Supported Platforms
17 |
18 | We develop and test our operators on the following cloud platforms:
19 |
20 | * AKS on Microsoft Azure
21 | * EKS on Amazon Web Services (AWS)
22 | * GKE on Google Cloud Platform (GCP)
23 | * [IONOS Cloud Managed Kubernetes](https://cloud.ionos.com/managed/kubernetes)
24 | * K3s
25 | * Kubernetes (for an up to date list of supported versions please check the release notes in our [docs](https://docs.stackable.tech))
26 | * Red Hat OpenShift
27 |
28 | ## Other Operators
29 |
30 | These are the operators that are currently part of the Stackable Data Platform:
31 |
32 | * [Stackable Operator for Apache Airflow](https://github.com/stackabletech/airflow-operator)
33 | * [Stackable Operator for Apache Druid](https://github.com/stackabletech/druid-operator)
34 | * [Stackable Operator for Apache HBase](https://github.com/stackabletech/hbase-operator)
35 | * [Stackable Operator for Apache Hadoop HDFS](https://github.com/stackabletech/hdfs-operator)
36 | * [Stackable Operator for Apache Hive](https://github.com/stackabletech/hive-operator)
37 | * [Stackable Operator for Apache Kafka](https://github.com/stackabletech/kafka-operator)
38 | * [Stackable Operator for Apache NiFi](https://github.com/stackabletech/nifi-operator)
39 | * [Stackable Operator for OpenSearch](https://github.com/stackabletech/opensearch-operator)
40 | * [Stackable Operator for Apache Spark](https://github.com/stackabletech/spark-k8s-operator)
41 | * [Stackable Operator for Apache Superset](https://github.com/stackabletech/superset-operator)
42 | * [Stackable Operator for Trino](https://github.com/stackabletech/trino-operator)
43 | * [Stackable Operator for Apache ZooKeeper](https://github.com/stackabletech/zookeeper-operator)
44 |
45 | And our internal operators:
46 |
47 | * [Commons Operator](https://github.com/stackabletech/commons-operator)
48 | * [Listener Operator](https://github.com/stackabletech/listener-operator)
49 | * [OpenPolicyAgent Operator](https://github.com/stackabletech/opa-operator)
50 | * [Secret Operator](https://github.com/stackabletech/secret-operator)
51 |
52 | ## Contributing
53 |
54 | Contributions are welcome.
55 | Follow our [Contributors Guide](https://docs.stackable.tech/home/stable/contributor/index.html) to learn how you can contribute.
56 | All contributors will have to sign a [Contributor License Agreement](https://github.com/stackabletech/.github/blob/main/cla.md).
57 | This is enforced automatically when you submit a Pull Request where a bot will guide you through the process.
58 |
59 | ## License
60 |
61 | [Open Software License version 3.0](./LICENSE).
62 |
63 | ## Support
64 |
65 | Get started with the community edition! If you want professional support, [we offer subscription plans and custom licensing](https://stackable.tech/en/plans/).
66 |
67 | ## Sponsor
68 |
69 | If you want to support our work but don't need professional support please consider [sponsoring](https://github.com/sponsors/stackabletech) our work.
70 |
--------------------------------------------------------------------------------