├── .prow_ci.env
├── test
├── kuttl
│ ├── tests
│ │ ├── keystone_tls
│ │ │ ├── 00-assert.yaml
│ │ │ ├── 00-tls_ca_bundle.yaml
│ │ │ ├── 02-cleanup-keystone.yaml
│ │ │ ├── 02-errors.yaml
│ │ │ ├── 01-keystoneapi_tls_deploy.yaml
│ │ │ ├── 00-tls_cert_keystone-public-svc.yaml
│ │ │ ├── 00-tls_cert_keystone-internal-svc.yaml
│ │ │ └── 01-assert.yaml
│ │ ├── fernet_rotation
│ │ │ ├── 05-errors.yaml
│ │ │ ├── 00-assert.yaml
│ │ │ ├── 00-deploy_keystone.yaml
│ │ │ ├── 05-cleanup-keystone.yaml
│ │ │ ├── 03-rotate_keys_until_invalidate.yaml
│ │ │ ├── 04-cleanup-openstackclient.yaml
│ │ │ ├── 02-rotate_keys.yaml
│ │ │ ├── 01-assert.yaml
│ │ │ ├── 02-assert.yaml
│ │ │ ├── 03-assert.yaml
│ │ │ └── 01-deploy_openstackclient.yaml
│ │ ├── keystone_scale
│ │ │ ├── 00-assert.yaml
│ │ │ ├── 04-errors.yaml
│ │ │ ├── 00-deploy_keystone.yaml
│ │ │ ├── 04-cleanup-keystone.yaml
│ │ │ ├── 03-errors.yaml
│ │ │ ├── 01-scale-keystoneapi.yaml
│ │ │ ├── 02-scale-down-keystoneapi.yaml
│ │ │ ├── 03-scale-down-zero-keystoneapi.yaml
│ │ │ ├── 02-assert.yaml
│ │ │ ├── 01-assert.yaml
│ │ │ └── 03-assert.yaml
│ │ ├── keystone_resources
│ │ │ ├── 00-assert.yaml
│ │ │ ├── 05-errors.yaml
│ │ │ ├── 00-deploy_keystone.yaml
│ │ │ ├── 05-cleanup-keystone.yaml
│ │ │ ├── 01-deploy-keystoneservice.yaml
│ │ │ ├── 02-deploy-keystoneendpoint.yaml
│ │ │ ├── 03-cleanup-keystoneendpoint.yaml
│ │ │ ├── 04-cleanup-keystoneservice.yaml
│ │ │ ├── 03-errors.yaml
│ │ │ ├── 04-errors.yaml
│ │ │ ├── 04-assert.yaml
│ │ │ ├── 03-assert.yaml
│ │ │ ├── 01-assert.yaml
│ │ │ └── 02-assert.yaml
│ │ └── change_keystone_config
│ │ │ ├── 00-assert.yaml
│ │ │ ├── 03-cleanup-keystone.yaml
│ │ │ ├── 03-errors.yaml
│ │ │ ├── 00-deploy_keystone.yaml
│ │ │ ├── 02-assert.yaml
│ │ │ ├── 01-assert.yaml
│ │ │ └── 02-change_keystone_config.yaml
│ └── common
│ │ ├── keystoneapi_deploy.yaml
│ │ ├── keystoneapi_tls_deploy.yaml
│ │ ├── cleanup-keystone.yaml
│ │ ├── scripts
│ │ ├── create_test_token.sh
│ │ ├── test_invalid_token.sh
│ │ ├── check_debug_in_keystone_pod_logs.sh
│ │ ├── rotate_token.sh
│ │ └── validate_test_token.sh
│ │ ├── patch_keystone_deploy.yaml
│ │ ├── assert_tls_cert.yaml
│ │ ├── errors_cleanup_keystone.yaml
│ │ ├── tls_ca_bundle.yaml
│ │ ├── tls_cert_keystone-public-svc.yaml
│ │ ├── tls_cert_keystone-internal-svc.yaml
│ │ └── assert_sample_deployment.yaml
└── functional
│ └── base_test.go
├── config
├── network-policy
│ ├── kustomization.yaml
│ ├── allow-metrics-traffic.yaml
│ └── allow-webhook-traffic.yaml
├── webhook
│ ├── kustomization.yaml
│ ├── service.yaml
│ ├── kustomizeconfig.yaml
│ └── manifests.yaml
├── certmanager
│ ├── kustomization.yaml
│ ├── kustomizeconfig.yaml
│ ├── issuer.yaml
│ ├── certificate-webhook.yaml
│ └── certificate-metrics.yaml
├── scorecard
│ ├── bases
│ │ └── config.yaml
│ ├── patches
│ │ ├── basic.config.yaml
│ │ └── olm.config.yaml
│ └── kustomization.yaml
├── default
│ ├── manager_metrics_patch.yaml
│ ├── metrics_service.yaml
│ ├── cert_metrics_manager_patch.yaml
│ └── manager_webhook_patch.yaml
├── rbac
│ ├── metrics_reader_role.yaml
│ ├── service_account.yaml
│ ├── metrics_auth_role_binding.yaml
│ ├── metrics_auth_role.yaml
│ ├── role_binding.yaml
│ ├── leader_election_role_binding.yaml
│ ├── leader_election_role.yaml
│ ├── keystoneapi_admin_role.yaml
│ ├── keystoneendpoint_admin_role.yaml
│ ├── keystoneservice_admin_role.yaml
│ ├── keystoneapi_viewer_role.yaml
│ ├── keystoneservice_viewer_role.yaml
│ ├── keystoneendpoint_viewer_role.yaml
│ ├── keystoneapi_editor_role.yaml
│ ├── keystoneservice_editor_role.yaml
│ ├── keystoneendpoint_editor_role.yaml
│ ├── kustomization.yaml
│ └── role.yaml
├── manager
│ ├── kustomization.yaml
│ └── manager.yaml
├── samples
│ ├── kustomization.yaml
│ ├── keystone_v1beta1_keystoneservice.yaml
│ ├── keystone_v1beta1_keystoneendpoint.yaml
│ ├── keystone_v1beta1_keystoneapi.yaml
│ └── keystone_v1beta1_keystoneapi_tls.yaml
├── prometheus
│ ├── kustomization.yaml
│ ├── monitor_tls_patch.yaml
│ └── monitor.yaml
├── crd
│ ├── kustomizeconfig.yaml
│ ├── kustomization.yaml
│ └── bases
│ │ └── keystone.openstack.org_keystoneservices.yaml
└── manifests
│ ├── kustomization.yaml
│ └── bases
│ └── keystone-operator.clusterserviceversion.yaml
├── .ci-operator.yaml
├── .dockerignore
├── hack
├── clean_local_webhook.sh
├── build-crd-schema-checker.sh
├── boilerplate.go.txt
├── crd-schema-checker.sh
└── run_with_local_webhook.sh
├── OWNERS
├── .github
└── workflows
│ ├── release-branch-sync.yaml
│ ├── lints.yaml
│ ├── force-bump-pr-manual.yaml
│ ├── force-bump-pr-scheduled.yaml
│ ├── build-keystone-operator.yaml
│ └── release-keystone-operator.yaml
├── .golangci.yaml
├── OWNERS_ALIASES
├── zuul.d
├── projects.yaml
└── jobs.yaml
├── kuttl-test.yaml
├── .gitignore
├── renovate.json
├── templates
└── keystoneapi
│ └── config
│ ├── ssl.conf
│ ├── keystone.conf
│ ├── httpd.conf
│ └── keystone-api-config.json
├── internal
├── keystone
│ ├── fernet.go
│ ├── funcs.go
│ ├── federation.go
│ ├── cloudconfig.go
│ ├── const.go
│ ├── dbsync.go
│ ├── cronjob.go
│ ├── volumes.go
│ └── bootstrap.go
└── webhook
│ └── v1beta1
│ └── keystoneapi_webhook.go
├── api
├── v1beta1
│ ├── groupversion_info.go
│ ├── keystoneservice_types.go
│ ├── keystoneendpoint_types.go
│ ├── conditions.go
│ └── keystoneservice.go
├── go.mod
└── bases
│ ├── keystone.openstack.org_keystoneservices.yaml
│ └── keystone.openstack.org_keystoneendpoints.yaml
├── PROJECT
├── .pull_request_pipeline
├── .pre-commit-config.yaml
├── Dockerfile
└── README.md
/.prow_ci.env:
--------------------------------------------------------------------------------
1 | export USE_IMAGE_DIGESTS=true
2 | export FAIL_FIPS_CHECK=true
3 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_tls/00-assert.yaml:
--------------------------------------------------------------------------------
1 | ../../common/assert_tls_cert.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_tls/00-tls_ca_bundle.yaml:
--------------------------------------------------------------------------------
1 | ../../common/tls_ca_bundle.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/05-errors.yaml:
--------------------------------------------------------------------------------
1 | ../../common/errors_cleanup_keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/00-assert.yaml:
--------------------------------------------------------------------------------
1 | ../../common/assert_sample_deployment.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/04-errors.yaml:
--------------------------------------------------------------------------------
1 | ../../common/errors_cleanup_keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_tls/02-cleanup-keystone.yaml:
--------------------------------------------------------------------------------
1 | ../../common/cleanup-keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_tls/02-errors.yaml:
--------------------------------------------------------------------------------
1 | ../../common/errors_cleanup_keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/00-assert.yaml:
--------------------------------------------------------------------------------
1 | ../../common/assert_sample_deployment.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/00-deploy_keystone.yaml:
--------------------------------------------------------------------------------
1 | ../../common/keystoneapi_deploy.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/05-cleanup-keystone.yaml:
--------------------------------------------------------------------------------
1 | ../../common/cleanup-keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/00-assert.yaml:
--------------------------------------------------------------------------------
1 | ../../common/assert_sample_deployment.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/05-errors.yaml:
--------------------------------------------------------------------------------
1 | ../../common/errors_cleanup_keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/00-deploy_keystone.yaml:
--------------------------------------------------------------------------------
1 | ../../common/keystoneapi_deploy.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/04-cleanup-keystone.yaml:
--------------------------------------------------------------------------------
1 | ../../common/cleanup-keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/common/keystoneapi_deploy.yaml:
--------------------------------------------------------------------------------
1 | ../../../config/samples/keystone_v1beta1_keystoneapi.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/change_keystone_config/00-assert.yaml:
--------------------------------------------------------------------------------
1 | ../../common/assert_sample_deployment.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/change_keystone_config/03-cleanup-keystone.yaml:
--------------------------------------------------------------------------------
1 | ../../common/cleanup-keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/change_keystone_config/03-errors.yaml:
--------------------------------------------------------------------------------
1 | ../../common/errors_cleanup_keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/00-deploy_keystone.yaml:
--------------------------------------------------------------------------------
1 | ../../common/keystoneapi_deploy.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/05-cleanup-keystone.yaml:
--------------------------------------------------------------------------------
1 | ../../common/cleanup-keystone.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/change_keystone_config/00-deploy_keystone.yaml:
--------------------------------------------------------------------------------
1 | ../../common/keystoneapi_deploy.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_tls/01-keystoneapi_tls_deploy.yaml:
--------------------------------------------------------------------------------
1 | ../../common/keystoneapi_tls_deploy.yaml
--------------------------------------------------------------------------------
/test/kuttl/common/keystoneapi_tls_deploy.yaml:
--------------------------------------------------------------------------------
1 | ../../../config/samples/keystone_v1beta1_keystoneapi_tls.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_tls/00-tls_cert_keystone-public-svc.yaml:
--------------------------------------------------------------------------------
1 | ../../common/tls_cert_keystone-public-svc.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_tls/00-tls_cert_keystone-internal-svc.yaml:
--------------------------------------------------------------------------------
1 | ../../common/tls_cert_keystone-internal-svc.yaml
--------------------------------------------------------------------------------
/config/network-policy/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - allow-webhook-traffic.yaml
3 | - allow-metrics-traffic.yaml
4 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/01-deploy-keystoneservice.yaml:
--------------------------------------------------------------------------------
1 | ../../../../config/samples/keystone_v1beta1_keystoneservice.yaml
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/02-deploy-keystoneendpoint.yaml:
--------------------------------------------------------------------------------
1 | ../../../../config/samples/keystone_v1beta1_keystoneendpoint.yaml
--------------------------------------------------------------------------------
/config/webhook/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - manifests.yaml
3 | - service.yaml
4 |
5 | configurations:
6 | - kustomizeconfig.yaml
7 |
--------------------------------------------------------------------------------
/.ci-operator.yaml:
--------------------------------------------------------------------------------
1 | build_root_image:
2 | name: tools
3 | namespace: openstack-k8s-operators
4 | tag: ci-build-root-golang-1.24-sdk-1.41.1
5 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
2 | # Ignore build and test binaries.
3 | bin/
4 | testbin/
5 |
--------------------------------------------------------------------------------
/config/certmanager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - issuer.yaml
3 | - certificate-webhook.yaml
4 | - certificate-metrics.yaml
5 |
6 | configurations:
7 | - kustomizeconfig.yaml
8 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/03-errors.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | annotations:
5 | openshift.io/scc: anyuid
6 | labels:
7 | service: keystone
8 |
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/03-rotate_keys_until_invalidate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | commands:
4 | - script: ../../common/scripts/rotate_token.sh
5 |
--------------------------------------------------------------------------------
/config/scorecard/bases/config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: scorecard.operatorframework.io/v1alpha3
2 | kind: Configuration
3 | metadata:
4 | name: config
5 | stages:
6 | - parallel: true
7 | tests: []
8 |
--------------------------------------------------------------------------------
/test/kuttl/common/cleanup-keystone.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | delete:
4 | - apiVersion: keystone.openstack.org/v1beta1
5 | kind: KeystoneAPI
6 | name: keystone
7 |
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/04-cleanup-openstackclient.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | delete:
4 | - apiVersion: v1
5 | kind: Pod
6 | name: openstackclient
7 |
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/02-rotate_keys.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: keystone
5 | annotations:
6 | keystone.openstack.org/rotatedat: "2009-11-10T23:00:00Z"
7 |
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/01-assert.yaml:
--------------------------------------------------------------------------------
1 | # create a test token, save it in /tmp/
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | commands:
5 | - script: ../../common/scripts/create_test_token.sh
6 |
--------------------------------------------------------------------------------
/hack/clean_local_webhook.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ex
3 |
4 | oc delete validatingwebhookconfiguration/vkeystoneapi.kb.io --ignore-not-found
5 | oc delete mutatingwebhookconfiguration/mkeystoneapi.kb.io --ignore-not-found
6 |
--------------------------------------------------------------------------------
/config/default/manager_metrics_patch.yaml:
--------------------------------------------------------------------------------
1 | # This patch adds the args to allow exposing the metrics endpoint using HTTPS
2 | - op: add
3 | path: /spec/template/spec/containers/0/args/0
4 | value: --metrics-bind-address=:8443
5 |
--------------------------------------------------------------------------------
/config/rbac/metrics_reader_role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: metrics-reader
5 | rules:
6 | - nonResourceURLs:
7 | - "/metrics"
8 | verbs:
9 | - get
10 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/03-cleanup-keystoneendpoint.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | delete:
4 | - apiVersion: keystone.openstack.org/v1beta1
5 | kind: KeystoneEndpoint
6 | name: placement
7 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/04-cleanup-keystoneservice.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | delete:
4 | - apiVersion: keystone.openstack.org/v1beta1
5 | kind: KeystoneService
6 | name: placement
7 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/03-errors.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - No KeystoneEndpoint CR
5 | #
6 | apiVersion: keystone.openstack.org/v1beta1
7 | kind: KeystoneEndpoint
8 | metadata:
9 | name: placement
10 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/04-errors.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - No KeystoneService CR
5 | #
6 | apiVersion: keystone.openstack.org/v1beta1
7 | kind: KeystoneService
8 | metadata:
9 | name: placement
10 |
--------------------------------------------------------------------------------
/test/kuttl/tests/change_keystone_config/02-assert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestAssert
3 | namespaced: true
4 | commands:
5 | - script: $KEYSTONE_KUTTL_DIR/../common/scripts/check_debug_in_keystone_pod_logs.sh --reverse
6 |
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/02-assert.yaml:
--------------------------------------------------------------------------------
1 | # validate that token saved in previous step works
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | namespaced: true
5 | commands:
6 | - script: ../../common/scripts/validate_test_token.sh
7 |
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/03-assert.yaml:
--------------------------------------------------------------------------------
1 | # validate that token saved in previous step works
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | namespaced: true
5 | commands:
6 | - script: ../../common/scripts/test_invalid_token.sh
7 |
--------------------------------------------------------------------------------
/OWNERS:
--------------------------------------------------------------------------------
1 | # See the OWNERS docs at https://go.k8s.io/owners
2 | approvers:
3 | - ci-approvers
4 | - openstack-approvers
5 | - security-approvers
6 |
7 | reviewers:
8 | - ci-approvers
9 | - openstack-approvers
10 | - security-approvers
11 |
--------------------------------------------------------------------------------
/config/rbac/service_account.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: keystone-operator
6 | app.kubernetes.io/managed-by: kustomize
7 | name: controller-manager
8 | namespace: system
9 |
--------------------------------------------------------------------------------
/config/manager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - manager.yaml
3 | apiVersion: kustomize.config.k8s.io/v1beta1
4 | kind: Kustomization
5 | images:
6 | - name: controller
7 | newName: quay.io/openstack-k8s-operators/keystone-operator
8 | newTag: latest
9 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/01-scale-keystoneapi.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | commands:
4 | - script: |
5 | oc patch keystoneapi -n $NAMESPACE keystone --type='json' -p='[{"op": "replace", "path": "/spec/replicas", "value":3}]'
6 |
--------------------------------------------------------------------------------
/test/kuttl/tests/change_keystone_config/01-assert.yaml:
--------------------------------------------------------------------------------
1 | # check that by default, debug is not set in keystone config
2 | apiVersion: kuttl.dev/v1beta1
3 | kind: TestAssert
4 | commands:
5 | - script: $KEYSTONE_KUTTL_DIR/../common/scripts/check_debug_in_keystone_pod_logs.sh
6 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/04-assert.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 1 KeystoneAPI CR
5 | #
6 | apiVersion: keystone.openstack.org/v1beta1
7 | kind: KeystoneAPI
8 | metadata:
9 | finalizers:
10 | - openstack.org/keystoneapi
11 | name: keystone
12 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/02-scale-down-keystoneapi.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | commands:
4 | - script: |
5 | oc patch keystoneapi -n $NAMESPACE keystone --type='json' -p='[{"op": "replace", "path": "/spec/replicas", "value":1}]'
6 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/03-scale-down-zero-keystoneapi.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | commands:
4 | - script: |
5 | oc patch keystoneapi -n $NAMESPACE keystone --type='json' -p='[{"op": "replace", "path": "/spec/replicas", "value":0}]'
6 |
--------------------------------------------------------------------------------
/config/certmanager/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # This configuration is for teaching kustomize how to update name ref substitution
2 | nameReference:
3 | - kind: Issuer
4 | group: cert-manager.io
5 | fieldSpecs:
6 | - kind: Certificate
7 | group: cert-manager.io
8 | path: spec/issuerRef/name
9 |
--------------------------------------------------------------------------------
/config/samples/kustomization.yaml:
--------------------------------------------------------------------------------
1 | ## Append samples you want in your CSV to this file as resources ##
2 | resources:
3 | - keystone_v1beta1_keystoneapi.yaml
4 | - keystone_v1beta1_keystoneservice.yaml
5 | - keystone_v1beta1_keystoneendpoint.yaml
6 | #+kubebuilder:scaffold:manifestskustomizesamples
7 |
--------------------------------------------------------------------------------
/test/kuttl/tests/change_keystone_config/02-change_keystone_config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | commands:
4 | - script: |
5 | oc patch keystoneapi -n $NAMESPACE keystone --type='json' -p='[{"op": "replace", "path": "/spec/customServiceConfig", "value": "[DEFAULT]\ndebug = false"}]'
6 |
--------------------------------------------------------------------------------
/config/scorecard/patches/basic.config.yaml:
--------------------------------------------------------------------------------
1 | - op: add
2 | path: /stages/0/tests/-
3 | value:
4 | entrypoint:
5 | - scorecard-test
6 | - basic-check-spec
7 | image: quay.io/operator-framework/scorecard-test:v1.41.1
8 | labels:
9 | suite: basic
10 | test: basic-check-spec-test
11 |
--------------------------------------------------------------------------------
/.github/workflows/release-branch-sync.yaml:
--------------------------------------------------------------------------------
1 | name: Release Branch sync
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 | schedule:
8 | - cron: '0 * * * *'
9 |
10 | jobs:
11 | call-build-workflow:
12 | uses: openstack-k8s-operators/openstack-k8s-operators-ci/.github/workflows/release-branch-sync.yaml@main
13 |
--------------------------------------------------------------------------------
/test/kuttl/common/scripts/create_test_token.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euxo pipefail
3 |
4 | oc wait --for=condition=ready pod openstackclient --timeout=30s -n $NAMESPACE
5 |
6 | alias openstack="oc exec -tn $NAMESPACE openstackclient -- openstack"
7 |
8 | export OS_TOKEN=$(openstack token issue -f value -c id)
9 |
10 | echo $OS_TOKEN > /tmp/temporary_test_token
11 |
--------------------------------------------------------------------------------
/config/rbac/metrics_auth_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: metrics-auth-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: ClusterRole
8 | name: metrics-auth-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/.golangci.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | linters:
4 | # Enable specific linter
5 | # https://golangci-lint.run/usage/linters/#enabled-by-default
6 | enable:
7 | - errorlint
8 | - revive
9 | - ginkgolinter
10 | - govet
11 | - gosec
12 | - errname
13 | - err113
14 |
15 | formatters:
16 | enable:
17 | - gofmt
18 |
19 | run:
20 | timeout: 5m
21 |
--------------------------------------------------------------------------------
/config/rbac/metrics_auth_role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: metrics-auth-role
5 | rules:
6 | - apiGroups:
7 | - authentication.k8s.io
8 | resources:
9 | - tokenreviews
10 | verbs:
11 | - create
12 | - apiGroups:
13 | - authorization.k8s.io
14 | resources:
15 | - subjectaccessreviews
16 | verbs:
17 | - create
18 |
--------------------------------------------------------------------------------
/config/samples/keystone_v1beta1_keystoneservice.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: keystone.openstack.org/v1beta1
2 | kind: KeystoneService
3 | metadata:
4 | name: placement
5 | spec:
6 | serviceUser: placement
7 | enabled: true
8 | serviceDescription: "Placement service"
9 | serviceName: placement
10 | serviceType: placement
11 | secret: osp-secret
12 | passwordSelector: PlacementPassword
13 |
--------------------------------------------------------------------------------
/OWNERS_ALIASES:
--------------------------------------------------------------------------------
1 | # See the OWNERS_ALIASES docs: https://git.k8s.io/community/contributors/guide/owners.md#owners_aliases
2 |
3 | aliases:
4 | ci-approvers:
5 | - viroel
6 | openstack-approvers:
7 | - abays
8 | - dprince
9 | - olliewalsh
10 | - stuggi
11 | security-approvers:
12 | - afaranha
13 | - d34dh0r53
14 | - dmendiza
15 | - mauricioharley
16 | - vakwetu
17 | - xek
18 |
--------------------------------------------------------------------------------
/test/kuttl/common/patch_keystone_deploy.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kuttl.dev/v1beta1
2 | kind: TestStep
3 | commands:
4 | - script: |
5 | oc patch keystoneapi -n $NAMESPACE keystone --type='json' -p='[{"op": "replace", "path": "/spec/secret", "value":"osp-secret"}]'
6 | oc patch keystoneapi -n $NAMESPACE keystone --type='json' -p='[{"op": "replace", "path": "/spec/storageClass", "value":"local-storage"}]'
7 |
--------------------------------------------------------------------------------
/test/kuttl/common/assert_tls_cert.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 2 tls cert secrets
5 | # - 1 tls ca bundle secrets
6 |
7 | apiVersion: v1
8 | kind: Secret
9 | metadata:
10 | name: cert-keystone-internal-svc
11 | ---
12 | apiVersion: v1
13 | kind: Secret
14 | metadata:
15 | name: cert-keystone-public-svc
16 | ---
17 | apiVersion: v1
18 | kind: Secret
19 | metadata:
20 | name: combined-ca-bundle
21 |
--------------------------------------------------------------------------------
/config/samples/keystone_v1beta1_keystoneendpoint.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: keystone.openstack.org/v1beta1
2 | kind: KeystoneEndpoint
3 | metadata:
4 | name: placement
5 | spec:
6 | serviceName: placement
7 | endpoints:
8 | admin: http://placement-admin-openstack.apps-crc.testing
9 | internal: http://placement-internal-openstack.apps-crc.testing
10 | public: http://placement-public-openstack.apps-crc.testing
11 |
--------------------------------------------------------------------------------
/config/webhook/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: keystone-operator
6 | app.kubernetes.io/managed-by: kustomize
7 | name: webhook-service
8 | namespace: system
9 | spec:
10 | ports:
11 | - port: 443
12 | protocol: TCP
13 | targetPort: 9443
14 | selector:
15 | control-plane: controller-manager
16 | app.kubernetes.io/name: keystone-operator
17 |
--------------------------------------------------------------------------------
/config/rbac/role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: keystone-operator
6 | app.kubernetes.io/managed-by: kustomize
7 | name: manager-rolebinding
8 | roleRef:
9 | apiGroup: rbac.authorization.k8s.io
10 | kind: ClusterRole
11 | name: manager-role
12 | subjects:
13 | - kind: ServiceAccount
14 | name: controller-manager
15 | namespace: system
16 |
--------------------------------------------------------------------------------
/hack/build-crd-schema-checker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euxo pipefail
3 |
4 | if [ -f "$INSTALL_DIR/crd-schema-checker" ]; then
5 | exit 0
6 | fi
7 |
8 | mkdir -p "$INSTALL_DIR/git-tmp"
9 | git clone https://github.com/openshift/crd-schema-checker.git \
10 | -b "$CRD_SCHEMA_CHECKER_VERSION" "$INSTALL_DIR/git-tmp"
11 | pushd "$INSTALL_DIR/git-tmp"
12 | GOWORK=off make
13 | cp crd-schema-checker "$INSTALL_DIR/"
14 | popd
15 | rm -rf "$INSTALL_DIR/git-tmp"
16 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: keystone-operator
6 | app.kubernetes.io/managed-by: kustomize
7 | name: leader-election-rolebinding
8 | roleRef:
9 | apiGroup: rbac.authorization.k8s.io
10 | kind: Role
11 | name: leader-election-role
12 | subjects:
13 | - kind: ServiceAccount
14 | name: controller-manager
15 | namespace: system
16 |
--------------------------------------------------------------------------------
/config/samples/keystone_v1beta1_keystoneapi.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: keystone.openstack.org/v1beta1
2 | kind: KeystoneAPI
3 | metadata:
4 | name: keystone
5 | spec:
6 | adminProject: admin
7 | adminUser: admin
8 | customServiceConfig: |
9 | [DEFAULT]
10 | debug = true
11 | databaseInstance: openstack
12 | databaseAccount: keystone
13 | preserveJobs: false
14 | region: regionOne
15 | secret: osp-secret
16 | resources:
17 | requests:
18 | memory: "500Mi"
19 | cpu: "1.0"
20 |
--------------------------------------------------------------------------------
/.github/workflows/lints.yaml:
--------------------------------------------------------------------------------
1 | name: Lints
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | check-go-mod-replace-lines:
7 | name: check for replace lines in go.mod files
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout project code
11 | uses: actions/checkout@v3
12 | - name: check for replace lines in go.mod files
13 | run: |
14 | ! egrep --invert-match -e '^replace.*/api => \./api|^replace.*//allow-merging$' `find . -name 'go.mod'` | egrep -e 'go.mod:replace'
15 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/03-assert.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 1 KeystoneAPI CR
5 | # - 1 KeystoneService CR
6 | #
7 | apiVersion: keystone.openstack.org/v1beta1
8 | kind: KeystoneAPI
9 | metadata:
10 | finalizers:
11 | - openstack.org/keystoneapi
12 | - openstack.org/keystoneservice-placement
13 | name: keystone
14 | ---
15 | apiVersion: keystone.openstack.org/v1beta1
16 | kind: KeystoneService
17 | metadata:
18 | finalizers:
19 | - openstack.org/keystoneservice
20 | name: placement
21 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/02-assert.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 1 KeystoneAPI CR
5 | # - 1 Pods for KeystoneAPI CR
6 | #
7 |
8 | apiVersion: keystone.openstack.org/v1beta1
9 | kind: KeystoneAPI
10 | metadata:
11 | finalizers:
12 | - openstack.org/keystoneapi
13 | name: keystone
14 | spec:
15 | replicas: 1
16 | status:
17 | readyCount: 1
18 | ---
19 | apiVersion: apps/v1
20 | kind: Deployment
21 | metadata:
22 | name: keystone
23 | spec:
24 | replicas: 1
25 | status:
26 | availableReplicas: 1
27 |
--------------------------------------------------------------------------------
/config/default/metrics_service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | control-plane: controller-manager
6 | app.kubernetes.io/name: keystone-operator
7 | app.kubernetes.io/managed-by: kustomize
8 | name: controller-manager-metrics-service
9 | namespace: system
10 | spec:
11 | ports:
12 | - name: https
13 | port: 8443
14 | protocol: TCP
15 | targetPort: 8443
16 | selector:
17 | control-plane: controller-manager
18 | app.kubernetes.io/name: keystone-operator
19 |
--------------------------------------------------------------------------------
/config/scorecard/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - bases/config.yaml
3 | apiVersion: kustomize.config.k8s.io/v1beta1
4 | kind: Kustomization
5 | patches:
6 | - path: patches/basic.config.yaml
7 | target:
8 | group: scorecard.operatorframework.io
9 | kind: Configuration
10 | name: config
11 | version: v1alpha3
12 | - path: patches/olm.config.yaml
13 | target:
14 | group: scorecard.operatorframework.io
15 | kind: Configuration
16 | name: config
17 | version: v1alpha3
18 | # +kubebuilder:scaffold:patches
19 |
--------------------------------------------------------------------------------
/config/certmanager/issuer.yaml:
--------------------------------------------------------------------------------
1 | # The following manifest contains a self-signed issuer CR.
2 | # More information can be found at https://docs.cert-manager.io
3 | # WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes.
4 | apiVersion: cert-manager.io/v1
5 | kind: Issuer
6 | metadata:
7 | labels:
8 | app.kubernetes.io/name: keystone-operator
9 | app.kubernetes.io/managed-by: kustomize
10 | name: selfsigned-issuer
11 | namespace: system
12 | spec:
13 | selfSigned: {}
14 |
--------------------------------------------------------------------------------
/config/prometheus/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - monitor.yaml
3 |
4 | # [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus
5 | # to securely reference certificates created and managed by cert-manager.
6 | # Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml
7 | # to mount the "metrics-server-cert" secret in the Manager Deployment.
8 | #patches:
9 | # - path: monitor_tls_patch.yaml
10 | # target:
11 | # kind: ServiceMonitor
12 |
--------------------------------------------------------------------------------
/test/kuttl/common/scripts/test_invalid_token.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -x
3 |
4 | export OS_TOKEN=$(cat /tmp/temporary_test_token)
5 |
6 | output=$(oc exec -tn $NAMESPACE openstackclient -- env -u OS_CLOUD - OS_AUTH_URL=http://keystone-public.keystone-kuttl-tests.svc:5000 OS_AUTH_TYPE=token OS_TOKEN=$OS_TOKEN openstack endpoint list 2>&1)
7 |
8 | filtered_output=$(echo "$output" | grep -i "Could not recognize Fernet token")
9 |
10 | if echo "$filtered_output" | grep -q "Could not recognize Fernet token"; then
11 | exit 0
12 | else
13 | exit 1
14 | fi
15 |
--------------------------------------------------------------------------------
/test/kuttl/common/scripts/check_debug_in_keystone_pod_logs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | found=0
4 | not_found=1
5 | if [ "$1" = "--reverse" ];then
6 | # sometimes we want to check that there is no DEBUG logs
7 | found=1
8 | not_found=0
9 | fi
10 |
11 | pod=$(oc get pods -n $NAMESPACE -l service=keystone -o name)
12 | # in the case the logs have DEBUG lines, keep only one to avoid cluttering the
13 | # test output
14 | debug=$(oc logs -n $NAMESPACE "$pod" | grep "DEBUG" | head -n 1)
15 |
16 | if [ -n "$debug" ]; then
17 | exit $found
18 | else
19 | exit $not_found
20 | fi
21 |
--------------------------------------------------------------------------------
/.github/workflows/force-bump-pr-manual.yaml:
--------------------------------------------------------------------------------
1 | name: Manually Trigger a Force Bump PR
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | call-build-workflow:
8 | uses: openstack-k8s-operators/openstack-k8s-operators-ci/.github/workflows/force-bump-pull-request.yaml@main
9 | with:
10 | operator_name: keystone
11 | branch_name: ${{ github.ref_name }}
12 | custom_image: quay.io/openstack-k8s-operators/openstack-k8s-operators-ci-build-tools:golang-1.24-sdk-1.41.1
13 | secrets:
14 | FORCE_BUMP_PULL_REQUEST_PAT: ${{ secrets.FORCE_BUMP_PULL_REQUEST_PAT }}
15 |
--------------------------------------------------------------------------------
/zuul.d/projects.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | - project:
3 | name: openstack-k8s-operators/keystone-operator
4 | github-check:
5 | jobs:
6 | - openstack-k8s-operators-content-provider:
7 | vars:
8 | cifmw_install_yamls_sdk_version: v1.41.1
9 | - keystone-operator-kuttl:
10 | dependencies:
11 | - openstack-k8s-operators-content-provider
12 | voting: false
13 | - keystone-operator-tempest:
14 | dependencies:
15 | - openstack-k8s-operators-content-provider
16 | voting: false
17 |
--------------------------------------------------------------------------------
/config/crd/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD
2 | nameReference:
3 | - kind: Service
4 | version: v1
5 | fieldSpecs:
6 | - kind: CustomResourceDefinition
7 | version: v1
8 | group: apiextensions.k8s.io
9 | path: spec/conversion/webhook/clientConfig/service/name
10 |
11 | namespace:
12 | - kind: CustomResourceDefinition
13 | version: v1
14 | group: apiextensions.k8s.io
15 | path: spec/conversion/webhook/clientConfig/service/namespace
16 | create: false
17 |
18 | varReference:
19 | - path: metadata/annotations
20 |
--------------------------------------------------------------------------------
/hack/boilerplate.go.txt:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
--------------------------------------------------------------------------------
/kuttl-test.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # EXECUTION (from install_yamls repo root):
3 | #
4 | # make keystone_kuttl
5 | #
6 | # ASSUMPTIONS:
7 | #
8 | # 1. Latest version of kuttl is installed at /usr/local/bin/kubectl-kuttl
9 | # 2. An OCP 4.10+ CRC cluster with Podified Operators has been deployed
10 | # 3. CLI user has access to $KUBECONFIG
11 |
12 | apiVersion: kuttl.dev/v1beta1
13 | kind: TestSuite
14 | reportFormat: xml
15 | reportName: kuttl-report-keystone
16 | reportGranularity: test
17 | timeout: 800
18 | namespace: keystone-kuttl-tests
19 | parallel: 1
20 | suppress:
21 | - events # Remove spammy event logs
22 |
--------------------------------------------------------------------------------
/.github/workflows/force-bump-pr-scheduled.yaml:
--------------------------------------------------------------------------------
1 | name: Scheduled Force Bump PR
2 |
3 | on:
4 | schedule:
5 | - cron: '0 3 * * 6' # 3AM UTC Saturday
6 |
7 | jobs:
8 | call-build-workflow:
9 | if: github.ref == 'refs/heads/main' && github.repository_owner == 'openstack-k8s-operators'
10 | uses: openstack-k8s-operators/openstack-k8s-operators-ci/.github/workflows/force-bump-branches.yaml@main
11 | with:
12 | operator_name: keystone
13 | custom_image: quay.io/openstack-k8s-operators/openstack-k8s-operators-ci-build-tools:golang-1.24-sdk-1.41.1
14 | secrets:
15 | FORCE_BUMP_PULL_REQUEST_PAT: ${{ secrets.FORCE_BUMP_PULL_REQUEST_PAT }}
16 |
--------------------------------------------------------------------------------
/config/prometheus/monitor_tls_patch.yaml:
--------------------------------------------------------------------------------
1 | # Patch for Prometheus ServiceMonitor to enable secure TLS configuration
2 | # using certificates managed by cert-manager
3 | - op: replace
4 | path: /spec/endpoints/0/tlsConfig
5 | value:
6 | # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
7 | serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc
8 | insecureSkipVerify: false
9 | ca:
10 | secret:
11 | name: metrics-server-cert
12 | key: ca.crt
13 | cert:
14 | secret:
15 | name: metrics-server-cert
16 | key: tls.crt
17 | keySecret:
18 | name: metrics-server-cert
19 | key: tls.key
20 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/01-assert.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 1 KeystoneAPI CR
5 | # - 3 Pods for KeystoneAPI CR
6 | # - 1 CronJob for KeystoneAPI CR
7 | #
8 |
9 | apiVersion: keystone.openstack.org/v1beta1
10 | kind: KeystoneAPI
11 | metadata:
12 | finalizers:
13 | - openstack.org/keystoneapi
14 | name: keystone
15 | spec:
16 | replicas: 3
17 | status:
18 | readyCount: 3
19 | ---
20 | apiVersion: apps/v1
21 | kind: Deployment
22 | metadata:
23 | name: keystone
24 | spec:
25 | replicas: 3
26 | status:
27 | availableReplicas: 3
28 | ---
29 | apiVersion: batch/v1
30 | kind: CronJob
31 | metadata:
32 | name: keystone-cron
33 | spec:
34 | schedule: 1 * * * *
35 | suspend: false
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.so
7 | *.dylib
8 | /bin
9 | testbin/*
10 |
11 | # Test binary, build with `go test -c`
12 | *.test
13 |
14 | # Output of the go coverage tool, specifically when used with LiteIDE
15 | *.out
16 |
17 | # Kubernetes Generated files - skip generated files, except for vendored files
18 |
19 | !vendor/**/zz_generated.*
20 |
21 | #Operator SDK generated files
22 | /bundle/
23 | bundle.Dockerfile
24 | config/manager/kustomization.yaml
25 |
26 | # editor and IDE paraphernalia
27 | .idea
28 | *.swp
29 | *.swo
30 | *~
31 |
32 | # Common CI tools repository
33 | CI_TOOLS_REPO
34 |
35 | # generated workspace file
36 | go.work
37 | go.work.sum
38 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "github>openstack-k8s-operators/renovate-config:default.json5"
4 | ],
5 | "baseBranchPatterns": [
6 | "main"
7 | ],
8 | "useBaseBranchConfig": "merge",
9 | "packageRules": [
10 | {
11 | "matchPackageNames": [
12 | "github.com/openstack-k8s-operators/keystone-operator/api"
13 | ],
14 | "enabled": false
15 | }
16 | ],
17 | "postUpgradeTasks": {
18 | "commands": [
19 | "make gowork",
20 | "make tidy",
21 | "make manifests generate"
22 | ],
23 | "fileFilters": [
24 | "**/go.mod",
25 | "**/go.sum",
26 | "**/*.go",
27 | "**/*.yaml"
28 | ],
29 | "executionMode": "update"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/01-assert.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 1 KeystoneAPI CR
5 | # - 1 KeystoneService CR
6 | #
7 | apiVersion: keystone.openstack.org/v1beta1
8 | kind: KeystoneAPI
9 | metadata:
10 | finalizers:
11 | - openstack.org/keystoneapi
12 | - openstack.org/keystoneservice-placement
13 | name: keystone
14 | ---
15 | apiVersion: keystone.openstack.org/v1beta1
16 | kind: KeystoneService
17 | metadata:
18 | finalizers:
19 | - openstack.org/keystoneservice
20 | name: placement
21 | spec:
22 | enabled: true
23 | passwordSelector: PlacementPassword
24 | secret: osp-secret
25 | serviceDescription: Placement service
26 | serviceName: placement
27 | serviceType: placement
28 | serviceUser: placement
29 |
--------------------------------------------------------------------------------
/templates/keystoneapi/config/ssl.conf:
--------------------------------------------------------------------------------
1 |
2 | SSLRandomSeed startup builtin
3 | SSLRandomSeed startup file:/dev/urandom 512
4 | SSLRandomSeed connect builtin
5 | SSLRandomSeed connect file:/dev/urandom 512
6 |
7 | AddType application/x-x509-ca-cert .crt
8 | AddType application/x-pkcs7-crl .crl
9 |
10 | SSLPassPhraseDialog builtin
11 | SSLSessionCache "shmcb:/var/cache/mod_ssl/scache(512000)"
12 | SSLSessionCacheTimeout 300
13 | Mutex default
14 | SSLCryptoDevice builtin
15 | SSLHonorCipherOrder On
16 | SSLUseStapling Off
17 | SSLStaplingCache "shmcb:/run/httpd/ssl_stapling(32768)"
18 | SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!RC4:!3DES
19 | SSLProtocol all -SSLv2 -SSLv3 -TLSv1
20 | SSLOptions StdEnvVars
21 |
22 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions to do leader election.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: Role
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: keystone-operator
7 | app.kubernetes.io/managed-by: kustomize
8 | name: leader-election-role
9 | rules:
10 | - apiGroups:
11 | - ""
12 | resources:
13 | - configmaps
14 | verbs:
15 | - get
16 | - list
17 | - watch
18 | - create
19 | - update
20 | - patch
21 | - delete
22 | - apiGroups:
23 | - coordination.k8s.io
24 | resources:
25 | - leases
26 | verbs:
27 | - get
28 | - list
29 | - watch
30 | - create
31 | - update
32 | - patch
33 | - delete
34 | - apiGroups:
35 | - ""
36 | resources:
37 | - events
38 | verbs:
39 | - create
40 | - patch
41 |
--------------------------------------------------------------------------------
/zuul.d/jobs.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | - job:
3 | name: keystone-operator-kuttl
4 | parent: cifmw-base-multinode-kuttl
5 | attempts: 1
6 | required-projects:
7 | - github.com/openstack-k8s-operators/keystone-operator
8 | irrelevant-files:
9 | - .*/*.md
10 | - ^\..*$
11 | - ^LICENSE$
12 | - ^OWNERS$
13 | - ^OWNERS_ALIASES$
14 | - ^PROJECT$
15 | - ^README.md$
16 | - tests?\/functional
17 | - ^renovate.json$
18 | vars:
19 | cifmw_kuttl_tests_operator_list:
20 | - keystone
21 |
22 | - job:
23 | name: keystone-operator-tempest
24 | parent: podified-multinode-edpm-deployment-crc-2comp
25 | vars:
26 | cifmw_test_operator_tempest_concurrency: 3
27 | cifmw_test_operator_tempest_include_list: |
28 | ^tempest.api.identity.
29 |
--------------------------------------------------------------------------------
/config/certmanager/certificate-webhook.yaml:
--------------------------------------------------------------------------------
1 | # The following manifests contain a self-signed issuer CR and a certificate CR.
2 | # More document can be found at https://docs.cert-manager.io
3 | apiVersion: cert-manager.io/v1
4 | kind: Certificate
5 | metadata:
6 | labels:
7 | app.kubernetes.io/name: keystone-operator
8 | app.kubernetes.io/managed-by: kustomize
9 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml
10 | namespace: system
11 | spec:
12 | # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
13 | # replacements in the config/default/kustomization.yaml file.
14 | dnsNames:
15 | - SERVICE_NAME.SERVICE_NAMESPACE.svc
16 | - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local
17 | issuerRef:
18 | kind: Issuer
19 | name: selfsigned-issuer
20 | secretName: webhook-server-cert
21 |
--------------------------------------------------------------------------------
/.github/workflows/build-keystone-operator.yaml:
--------------------------------------------------------------------------------
1 | name: keystone operator image builder
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 |
8 | env:
9 | imageregistry: 'quay.io'
10 | imagenamespace: ${{ secrets.IMAGENAMESPACE || secrets.QUAY_USERNAME }}
11 | latesttag: latest
12 |
13 | jobs:
14 | call-build-workflow:
15 | uses: openstack-k8s-operators/openstack-k8s-operators-ci/.github/workflows/reusable-build-operator.yaml@main
16 | with:
17 | operator_name: keystone
18 | go_version: 1.24.x
19 | operator_sdk_version: 1.41.1
20 | secrets:
21 | IMAGENAMESPACE: ${{ secrets.IMAGENAMESPACE }}
22 | QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
23 | QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
24 | REDHATIO_USERNAME: ${{ secrets.REDHATIO_USERNAME }}
25 | REDHATIO_PASSWORD: ${{ secrets.REDHATIO_PASSWORD }}
26 |
--------------------------------------------------------------------------------
/config/certmanager/certificate-metrics.yaml:
--------------------------------------------------------------------------------
1 | # The following manifests contain a self-signed issuer CR and a metrics certificate CR.
2 | # More document can be found at https://docs.cert-manager.io
3 | apiVersion: cert-manager.io/v1
4 | kind: Certificate
5 | metadata:
6 | labels:
7 | app.kubernetes.io/name: keystone-operator
8 | app.kubernetes.io/managed-by: kustomize
9 | name: metrics-certs # this name should match the one appeared in kustomizeconfig.yaml
10 | namespace: system
11 | spec:
12 | dnsNames:
13 | # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
14 | # replacements in the config/default/kustomization.yaml file.
15 | - SERVICE_NAME.SERVICE_NAMESPACE.svc
16 | - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local
17 | issuerRef:
18 | kind: Issuer
19 | name: selfsigned-issuer
20 | secretName: metrics-server-cert
21 |
--------------------------------------------------------------------------------
/config/webhook/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # the following config is for teaching kustomize where to look at when substituting nameReference.
2 | # It requires kustomize v2.1.0 or newer to work properly.
3 | nameReference:
4 | - kind: Service
5 | version: v1
6 | fieldSpecs:
7 | - kind: MutatingWebhookConfiguration
8 | group: admissionregistration.k8s.io
9 | path: webhooks/clientConfig/service/name
10 | - kind: ValidatingWebhookConfiguration
11 | group: admissionregistration.k8s.io
12 | path: webhooks/clientConfig/service/name
13 |
14 | namespace:
15 | - kind: MutatingWebhookConfiguration
16 | group: admissionregistration.k8s.io
17 | path: webhooks/clientConfig/service/namespace
18 | create: true
19 | - kind: ValidatingWebhookConfiguration
20 | group: admissionregistration.k8s.io
21 | path: webhooks/clientConfig/service/namespace
22 | create: true
23 |
--------------------------------------------------------------------------------
/config/crd/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # This kustomization.yaml is not intended to be run by itself,
2 | # since it depends on service name and namespace that are out of this kustomize package.
3 | # It should be run by config/default
4 | resources:
5 | - bases/keystone.openstack.org_keystoneapis.yaml
6 | - bases/keystone.openstack.org_keystoneservices.yaml
7 | - bases/keystone.openstack.org_keystoneendpoints.yaml
8 | # +kubebuilder:scaffold:crdkustomizeresource
9 |
10 | patches:
11 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
12 | # patches here are for enabling the conversion webhook for each CRD
13 | # +kubebuilder:scaffold:crdkustomizewebhookpatch
14 |
15 | # [WEBHOOK] To enable webhook, uncomment the following section
16 | # the following config is for teaching kustomize how to do kustomization for CRDs.
17 | #configurations:
18 | #- kustomizeconfig.yaml
19 |
--------------------------------------------------------------------------------
/config/rbac/keystoneapi_admin_role.yaml:
--------------------------------------------------------------------------------
1 | # This rule is not used by the project keystone-operator itself.
2 | # It is provided to allow the cluster admin to help manage permissions for users.
3 | #
4 | # Grants full permissions ('*') over keystone.openstack.org.
5 | # This role is intended for users authorized to modify roles and bindings within the cluster,
6 | # enabling them to delegate specific permissions to other users or groups as needed.
7 |
8 | apiVersion: rbac.authorization.k8s.io/v1
9 | kind: ClusterRole
10 | metadata:
11 | labels:
12 | app.kubernetes.io/name: keystone-operator
13 | app.kubernetes.io/managed-by: kustomize
14 | name: keystoneapi-admin-role
15 | rules:
16 | - apiGroups:
17 | - keystone.openstack.org
18 | resources:
19 | - keystoneapis
20 | verbs:
21 | - '*'
22 | - apiGroups:
23 | - keystone.openstack.org
24 | resources:
25 | - keystoneapis/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_scale/03-assert.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 0 KeystoneAPI CR
5 | # - Keystone Deployment with 0 Pods
6 | #
7 |
8 | apiVersion: keystone.openstack.org/v1beta1
9 | kind: KeystoneAPI
10 | metadata:
11 | finalizers:
12 | - openstack.org/keystoneapi
13 | name: keystone
14 | spec:
15 | adminProject: admin
16 | adminUser: admin
17 | customServiceConfig: |
18 | [DEFAULT]
19 | debug = true
20 | databaseInstance: openstack
21 | databaseAccount: keystone
22 | passwordSelectors:
23 | admin: AdminPassword
24 | preserveJobs: false
25 | region: regionOne
26 | replicas: 0
27 | resources:
28 | requests:
29 | cpu: "1"
30 | memory: 500Mi
31 | secret: osp-secret
32 | status:
33 | databaseHostname: openstack.keystone-kuttl-tests.svc
34 | ---
35 | apiVersion: apps/v1
36 | kind: Deployment
37 | metadata:
38 | name: keystone
39 | spec:
40 | replicas: 0
41 |
--------------------------------------------------------------------------------
/hack/crd-schema-checker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euxo pipefail
3 |
4 | CHECKER=$INSTALL_DIR/crd-schema-checker
5 | DISABLED_VALIDATORS=NoMaps,NoBools,ListsMustHaveSSATags # TODO: https://issues.redhat.com/browse/OSPRH-12254
6 |
7 | CHECKER_ARGS=""
8 | if [[ ${DISABLED_VALIDATORS:+x} ]]; then
9 | CHECKER_ARGS="$CHECKER_ARGS "
10 | for check in ${DISABLED_VALIDATORS//,/ }; do
11 | CHECKER_ARGS+=" --disabled-validators $check"
12 | done
13 | fi
14 |
15 | TMP_DIR=$(mktemp -d)
16 |
17 | function cleanup {
18 | rm -rf "$TMP_DIR"
19 | }
20 |
21 | trap cleanup EXIT
22 |
23 |
24 | for crd in config/crd/bases/*.yaml; do
25 | mkdir -p "$(dirname "$TMP_DIR/$crd")"
26 | if git show "$BASE_REF:$crd" > "$TMP_DIR/$crd"; then
27 | $CHECKER check-manifests \
28 | $CHECKER_ARGS \
29 | --existing-crd-filename="$TMP_DIR/$crd" \
30 | --new-crd-filename="$crd"
31 | fi
32 | done
33 |
--------------------------------------------------------------------------------
/config/rbac/keystoneendpoint_admin_role.yaml:
--------------------------------------------------------------------------------
1 | # This rule is not used by the project keystone-operator itself.
2 | # It is provided to allow the cluster admin to help manage permissions for users.
3 | #
4 | # Grants full permissions ('*') over keystone.openstack.org.
5 | # This role is intended for users authorized to modify roles and bindings within the cluster,
6 | # enabling them to delegate specific permissions to other users or groups as needed.
7 |
8 | apiVersion: rbac.authorization.k8s.io/v1
9 | kind: ClusterRole
10 | metadata:
11 | labels:
12 | app.kubernetes.io/name: keystone-operator
13 | app.kubernetes.io/managed-by: kustomize
14 | name: keystoneendpoint-admin-role
15 | rules:
16 | - apiGroups:
17 | - keystone.openstack.org
18 | resources:
19 | - keystoneendpoints
20 | verbs:
21 | - '*'
22 | - apiGroups:
23 | - keystone.openstack.org
24 | resources:
25 | - keystoneendpoints/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/config/rbac/keystoneservice_admin_role.yaml:
--------------------------------------------------------------------------------
1 | # This rule is not used by the project keystone-operator itself.
2 | # It is provided to allow the cluster admin to help manage permissions for users.
3 | #
4 | # Grants full permissions ('*') over keystone.openstack.org.
5 | # This role is intended for users authorized to modify roles and bindings within the cluster,
6 | # enabling them to delegate specific permissions to other users or groups as needed.
7 |
8 | apiVersion: rbac.authorization.k8s.io/v1
9 | kind: ClusterRole
10 | metadata:
11 | labels:
12 | app.kubernetes.io/name: keystone-operator
13 | app.kubernetes.io/managed-by: kustomize
14 | name: keystoneservice-admin-role
15 | rules:
16 | - apiGroups:
17 | - keystone.openstack.org
18 | resources:
19 | - keystoneservices
20 | verbs:
21 | - '*'
22 | - apiGroups:
23 | - keystone.openstack.org
24 | resources:
25 | - keystoneservices/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/config/rbac/keystoneapi_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # This rule is not used by the project keystone-operator itself.
2 | # It is provided to allow the cluster admin to help manage permissions for users.
3 | #
4 | # Grants read-only access to keystone.openstack.org resources.
5 | # This role is intended for users who need visibility into these resources
6 | # without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
7 |
8 | apiVersion: rbac.authorization.k8s.io/v1
9 | kind: ClusterRole
10 | metadata:
11 | labels:
12 | app.kubernetes.io/name: keystone-operator
13 | app.kubernetes.io/managed-by: kustomize
14 | name: keystoneapi-viewer-role
15 | rules:
16 | - apiGroups:
17 | - keystone.openstack.org
18 | resources:
19 | - keystoneapis
20 | verbs:
21 | - get
22 | - list
23 | - watch
24 | - apiGroups:
25 | - keystone.openstack.org
26 | resources:
27 | - keystoneapis/status
28 | verbs:
29 | - get
30 |
--------------------------------------------------------------------------------
/config/samples/keystone_v1beta1_keystoneapi_tls.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: keystone.openstack.org/v1beta1
2 | kind: KeystoneAPI
3 | metadata:
4 | name: keystone
5 | spec:
6 | adminProject: admin
7 | adminUser: admin
8 | customServiceConfig: |
9 | [DEFAULT]
10 | debug = true
11 | databaseInstance: openstack
12 | databaseAccount: keystone
13 | preserveJobs: false
14 | region: regionOne
15 | secret: osp-secret
16 | resources:
17 | requests:
18 | memory: "500Mi"
19 | cpu: "1.0"
20 | tls:
21 | api:
22 | # secret holding tls.crt and tls.key for the APIs internal k8s service
23 | internal:
24 | secretName: cert-keystone-internal-svc
25 | # secret holding tls.crt and tls.key for the APIs public k8s service
26 | public:
27 | secretName: cert-keystone-public-svc
28 | # secret holding the tls-ca-bundle.pem to be used as a deploymend env CA bundle
29 | caBundleSecretName: combined-ca-bundle
30 |
--------------------------------------------------------------------------------
/config/rbac/keystoneservice_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # This rule is not used by the project keystone-operator itself.
2 | # It is provided to allow the cluster admin to help manage permissions for users.
3 | #
4 | # Grants read-only access to keystone.openstack.org resources.
5 | # This role is intended for users who need visibility into these resources
6 | # without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
7 |
8 | apiVersion: rbac.authorization.k8s.io/v1
9 | kind: ClusterRole
10 | metadata:
11 | labels:
12 | app.kubernetes.io/name: keystone-operator
13 | app.kubernetes.io/managed-by: kustomize
14 | name: keystoneservice-viewer-role
15 | rules:
16 | - apiGroups:
17 | - keystone.openstack.org
18 | resources:
19 | - keystoneservices
20 | verbs:
21 | - get
22 | - list
23 | - watch
24 | - apiGroups:
25 | - keystone.openstack.org
26 | resources:
27 | - keystoneservices/status
28 | verbs:
29 | - get
30 |
--------------------------------------------------------------------------------
/config/rbac/keystoneendpoint_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # This rule is not used by the project keystone-operator itself.
2 | # It is provided to allow the cluster admin to help manage permissions for users.
3 | #
4 | # Grants read-only access to keystone.openstack.org resources.
5 | # This role is intended for users who need visibility into these resources
6 | # without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
7 |
8 | apiVersion: rbac.authorization.k8s.io/v1
9 | kind: ClusterRole
10 | metadata:
11 | labels:
12 | app.kubernetes.io/name: keystone-operator
13 | app.kubernetes.io/managed-by: kustomize
14 | name: keystoneendpoint-viewer-role
15 | rules:
16 | - apiGroups:
17 | - keystone.openstack.org
18 | resources:
19 | - keystoneendpoints
20 | verbs:
21 | - get
22 | - list
23 | - watch
24 | - apiGroups:
25 | - keystone.openstack.org
26 | resources:
27 | - keystoneendpoints/status
28 | verbs:
29 | - get
30 |
--------------------------------------------------------------------------------
/test/kuttl/common/scripts/rotate_token.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -x
3 |
4 | TMP_SECRET_FILE="/tmp/keystone-secret.yaml"
5 |
6 | generate_secret_yaml() {
7 | cat < $TMP_SECRET_FILE
8 | apiVersion: v1
9 | kind: Secret
10 | metadata:
11 | name: keystone
12 | namespace: keystone-kuttl-tests
13 | annotations:
14 | keystone.openstack.org/rotatedat: "2009-11-10T23:00:00Z"
15 | EOF
16 | }
17 |
18 | for rotation in {1..5}; do
19 | echo "Starting rotation $rotation..."
20 |
21 | # Apply new secret to trigger rotation
22 | generate_secret_yaml
23 | if ! oc apply -f $TMP_SECRET_FILE; then
24 | echo "Failed to apply the secret!"
25 | rm -f $TMP_SECRET_FILE
26 | exit 1
27 | fi
28 |
29 | sleep 100
30 |
31 | # Note: keystone is not being restarted
32 |
33 | echo "Rotation $rotation completed successfully."
34 | done
35 |
36 | rm -f $TMP_SECRET_FILE
37 | echo "All rotations completed successfully."
38 | exit 0
39 |
--------------------------------------------------------------------------------
/config/network-policy/allow-metrics-traffic.yaml:
--------------------------------------------------------------------------------
1 | # This NetworkPolicy allows ingress traffic
2 | # with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those
3 | # namespaces are able to gather data from the metrics endpoint.
4 | apiVersion: networking.k8s.io/v1
5 | kind: NetworkPolicy
6 | metadata:
7 | labels:
8 | app.kubernetes.io/name: keystone-operator
9 | app.kubernetes.io/managed-by: kustomize
10 | name: allow-metrics-traffic
11 | namespace: system
12 | spec:
13 | podSelector:
14 | matchLabels:
15 | control-plane: controller-manager
16 | app.kubernetes.io/name: keystone-operator
17 | policyTypes:
18 | - Ingress
19 | ingress:
20 | # This allows ingress traffic from any namespace with the label metrics: enabled
21 | - from:
22 | - namespaceSelector:
23 | matchLabels:
24 | metrics: enabled # Only from namespaces with this label
25 | ports:
26 | - port: 8443
27 | protocol: TCP
28 |
--------------------------------------------------------------------------------
/test/kuttl/common/errors_cleanup_keystone.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # No KeystoneAPI CR
5 | # No Deployment for KeystoneAPI CR
6 | # No Pods in keystone Deployment
7 | # No Keystone Services
8 | #
9 | apiVersion: keystone.openstack.org/v1beta1
10 | kind: KeystoneAPI
11 | metadata:
12 | finalizers:
13 | - openstack.org/keystoneapi
14 | name: keystone
15 | ---
16 | apiVersion: apps/v1
17 | kind: Deployment
18 | metadata:
19 | name: keystone
20 | ---
21 | # the openshift annotations can't be checked through the deployment above
22 | apiVersion: v1
23 | kind: Pod
24 | metadata:
25 | annotations:
26 | openshift.io/scc: anyuid
27 | labels:
28 | service: keystone
29 | ---
30 | apiVersion: v1
31 | kind: Service
32 | metadata:
33 | labels:
34 | endpoint: internal
35 | service: keystone
36 | name: keystone-internal
37 | ---
38 | apiVersion: v1
39 | kind: Service
40 | metadata:
41 | labels:
42 | endpoint: public
43 | service: keystone
44 | name: keystone-public
45 |
--------------------------------------------------------------------------------
/config/rbac/keystoneapi_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # This rule is not used by the project keystone-operator itself.
2 | # It is provided to allow the cluster admin to help manage permissions for users.
3 | #
4 | # Grants permissions to create, update, and delete resources within the keystone.openstack.org.
5 | # This role is intended for users who need to manage these resources
6 | # but should not control RBAC or manage permissions for others.
7 |
8 | apiVersion: rbac.authorization.k8s.io/v1
9 | kind: ClusterRole
10 | metadata:
11 | labels:
12 | app.kubernetes.io/name: keystone-operator
13 | app.kubernetes.io/managed-by: kustomize
14 | name: keystoneapi-editor-role
15 | rules:
16 | - apiGroups:
17 | - keystone.openstack.org
18 | resources:
19 | - keystoneapis
20 | verbs:
21 | - create
22 | - delete
23 | - get
24 | - list
25 | - patch
26 | - update
27 | - watch
28 | - apiGroups:
29 | - keystone.openstack.org
30 | resources:
31 | - keystoneapis/status
32 | verbs:
33 | - get
34 |
--------------------------------------------------------------------------------
/test/kuttl/common/scripts/validate_test_token.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -euxo pipefail
3 |
4 | seconds=1
5 | while [ $seconds -le 30 ]; do
6 | rotatedat=$(oc get secret keystone -n $NAMESPACE -o jsonpath="{.metadata.annotations['keystone\.openstack\.org/rotatedat']}")
7 | if [ "$rotatedat" != "2009-11-10T23:00:00Z" ]; then
8 | break
9 | fi
10 | sleep 1
11 | seconds=$(( seconds + 1 ))
12 | done
13 |
14 | # Wait for secret propagation to pods instead of waiting for rollout
15 | # Since pods no longer restart during key rotation, keys are updated via Kubernetes secret propagation
16 | sleep 60
17 |
18 | export OS_TOKEN=$(cat /tmp/temporary_test_token)
19 |
20 | output=$(oc exec -tn $NAMESPACE openstackclient -- env -u OS_CLOUD - OS_AUTH_URL=http://keystone-public.keystone-kuttl-tests.svc:5000 OS_AUTH_TYPE=token OS_TOKEN=$OS_TOKEN openstack endpoint list 2>&1 || true)
21 |
22 | if echo "$output" | grep -qi "Could not recognize Fernet token"; then
23 | exit 1
24 | else
25 | exit 0
26 | fi
27 |
--------------------------------------------------------------------------------
/config/rbac/keystoneservice_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # This rule is not used by the project keystone-operator itself.
2 | # It is provided to allow the cluster admin to help manage permissions for users.
3 | #
4 | # Grants permissions to create, update, and delete resources within the keystone.openstack.org.
5 | # This role is intended for users who need to manage these resources
6 | # but should not control RBAC or manage permissions for others.
7 |
8 | apiVersion: rbac.authorization.k8s.io/v1
9 | kind: ClusterRole
10 | metadata:
11 | labels:
12 | app.kubernetes.io/name: keystone-operator
13 | app.kubernetes.io/managed-by: kustomize
14 | name: keystoneservice-editor-role
15 | rules:
16 | - apiGroups:
17 | - keystone.openstack.org
18 | resources:
19 | - keystoneservices
20 | verbs:
21 | - create
22 | - delete
23 | - get
24 | - list
25 | - patch
26 | - update
27 | - watch
28 | - apiGroups:
29 | - keystone.openstack.org
30 | resources:
31 | - keystoneservices/status
32 | verbs:
33 | - get
34 |
--------------------------------------------------------------------------------
/config/rbac/keystoneendpoint_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # This rule is not used by the project keystone-operator itself.
2 | # It is provided to allow the cluster admin to help manage permissions for users.
3 | #
4 | # Grants permissions to create, update, and delete resources within the keystone.openstack.org.
5 | # This role is intended for users who need to manage these resources
6 | # but should not control RBAC or manage permissions for others.
7 |
8 | apiVersion: rbac.authorization.k8s.io/v1
9 | kind: ClusterRole
10 | metadata:
11 | labels:
12 | app.kubernetes.io/name: keystone-operator
13 | app.kubernetes.io/managed-by: kustomize
14 | name: keystoneendpoint-editor-role
15 | rules:
16 | - apiGroups:
17 | - keystone.openstack.org
18 | resources:
19 | - keystoneendpoints
20 | verbs:
21 | - create
22 | - delete
23 | - get
24 | - list
25 | - patch
26 | - update
27 | - watch
28 | - apiGroups:
29 | - keystone.openstack.org
30 | resources:
31 | - keystoneendpoints/status
32 | verbs:
33 | - get
34 |
--------------------------------------------------------------------------------
/config/default/cert_metrics_manager_patch.yaml:
--------------------------------------------------------------------------------
1 | # This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs.
2 |
3 | # Add the volumeMount for the metrics-server certs
4 | - op: add
5 | path: /spec/template/spec/containers/0/volumeMounts/-
6 | value:
7 | mountPath: /tmp/k8s-metrics-server/metrics-certs
8 | name: metrics-certs
9 | readOnly: true
10 |
11 | # Add the --metrics-cert-path argument for the metrics server
12 | - op: add
13 | path: /spec/template/spec/containers/0/args/-
14 | value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs
15 |
16 | # Add the metrics-server certs volume configuration
17 | - op: add
18 | path: /spec/template/spec/volumes/-
19 | value:
20 | name: metrics-certs
21 | secret:
22 | secretName: metrics-server-cert
23 | optional: false
24 | items:
25 | - key: ca.crt
26 | path: ca.crt
27 | - key: tls.crt
28 | path: tls.crt
29 | - key: tls.key
30 | path: tls.key
31 |
--------------------------------------------------------------------------------
/config/network-policy/allow-webhook-traffic.yaml:
--------------------------------------------------------------------------------
1 | # This NetworkPolicy allows ingress traffic to your webhook server running
2 | # as part of the controller-manager from specific namespaces and pods. CR(s) which uses webhooks
3 | # will only work when applied in namespaces labeled with 'webhook: enabled'
4 | apiVersion: networking.k8s.io/v1
5 | kind: NetworkPolicy
6 | metadata:
7 | labels:
8 | app.kubernetes.io/name: keystone-operator
9 | app.kubernetes.io/managed-by: kustomize
10 | name: allow-webhook-traffic
11 | namespace: system
12 | spec:
13 | podSelector:
14 | matchLabels:
15 | control-plane: controller-manager
16 | app.kubernetes.io/name: keystone-operator
17 | policyTypes:
18 | - Ingress
19 | ingress:
20 | # This allows ingress traffic from any namespace with the label webhook: enabled
21 | - from:
22 | - namespaceSelector:
23 | matchLabels:
24 | webhook: enabled # Only from namespaces with this label
25 | ports:
26 | - port: 443
27 | protocol: TCP
28 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_resources/02-assert.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 1 KeystoneAPI CR
5 | # - 1 KeystoneService CR
6 | # - 1 KeystoneEndpoint CR
7 | #
8 | apiVersion: keystone.openstack.org/v1beta1
9 | kind: KeystoneAPI
10 | metadata:
11 | finalizers:
12 | - openstack.org/keystoneapi
13 | - openstack.org/keystoneservice-placement
14 | - openstack.org/keystoneendpoint-placement
15 | name: keystone
16 | ---
17 | apiVersion: keystone.openstack.org/v1beta1
18 | kind: KeystoneService
19 | metadata:
20 | finalizers:
21 | - openstack.org/keystoneservice
22 | - openstack.org/keystoneendpoint-placement
23 | name: placement
24 | ---
25 | apiVersion: keystone.openstack.org/v1beta1
26 | kind: KeystoneEndpoint
27 | metadata:
28 | finalizers:
29 | - openstack.org/keystoneendpoint
30 | name: placement
31 | spec:
32 | endpoints:
33 | admin: http://placement-admin-openstack.apps-crc.testing
34 | internal: http://placement-internal-openstack.apps-crc.testing
35 | public: http://placement-public-openstack.apps-crc.testing
36 | serviceName: placement
37 |
--------------------------------------------------------------------------------
/config/default/manager_webhook_patch.yaml:
--------------------------------------------------------------------------------
1 | # This patch ensures the webhook certificates are properly mounted in the manager container.
2 | # It configures the necessary arguments, volumes, volume mounts, and container ports.
3 |
4 | # Add the --webhook-cert-path argument for configuring the webhook certificate path
5 | - op: add
6 | path: /spec/template/spec/containers/0/args/-
7 | value: --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs
8 |
9 | # Add the volumeMount for the webhook certificates
10 | - op: add
11 | path: /spec/template/spec/containers/0/volumeMounts/-
12 | value:
13 | mountPath: /tmp/k8s-webhook-server/serving-certs
14 | name: webhook-certs
15 | readOnly: true
16 |
17 | # Add the port configuration for the webhook server
18 | - op: add
19 | path: /spec/template/spec/containers/0/ports/-
20 | value:
21 | containerPort: 9443
22 | name: webhook-server
23 | protocol: TCP
24 |
25 | # Add the volume configuration for the webhook certificates
26 | - op: add
27 | path: /spec/template/spec/volumes/-
28 | value:
29 | name: webhook-certs
30 | secret:
31 | secretName: webhook-server-cert
32 |
--------------------------------------------------------------------------------
/internal/keystone/fernet.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package keystone
17 |
18 | import (
19 | "crypto/rand"
20 | "encoding/base64"
21 |
22 | "github.com/go-logr/logr"
23 | )
24 |
25 | // GenerateFernetKey - returns a base64-encoded, 32-byte key using cryptographically secure random generation
26 | func GenerateFernetKey(logger logr.Logger) string {
27 | data := make([]byte, 32)
28 | _, err := rand.Read(data)
29 | if err != nil {
30 | logger.Error(err, "failed to read random bytes for Fernet key generation")
31 | return ""
32 | }
33 |
34 | return base64.StdEncoding.EncodeToString(data)
35 | }
36 |
--------------------------------------------------------------------------------
/config/manifests/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # These resources constitute the fully configured set of manifests
2 | # used to generate the 'manifests/' directory in a bundle.
3 | resources:
4 | - bases/keystone-operator.clusterserviceversion.yaml
5 | - ../default
6 | - ../samples
7 | - ../scorecard
8 |
9 | # [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix.
10 | # Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager.
11 | # These patches remove the unnecessary "cert" volume and its manager container volumeMount.
12 | #patches:
13 | #- target:
14 | # group: apps
15 | # version: v1
16 | # kind: Deployment
17 | # name: controller-manager
18 | # namespace: system
19 | # patch: |-
20 | # # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs.
21 | # # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment.
22 | # - op: remove
23 |
24 | # path: /spec/template/spec/containers/0/volumeMounts/0
25 | # # Remove the "cert" volume, since OLM will create and mount a set of certs.
26 | # # Update the indices in this path if adding or removing volumes in the manager's Deployment.
27 | # - op: remove
28 | # path: /spec/template/spec/volumes/0
29 |
--------------------------------------------------------------------------------
/templates/keystoneapi/config/keystone.conf:
--------------------------------------------------------------------------------
1 | [DEFAULT]
2 | use_stderr=true
3 |
4 | [cache]
5 | {{if .MemcachedTLS}}
6 | backend = oslo_cache.memcache_pool
7 | memcache_servers={{ .MemcachedServers }}
8 | memcache_socket_timeout = 0.5
9 | memcache_pool_connection_get_timeout = 1
10 | {{if (index . "MemcachedAuthCert")}}
11 | tls_certfile={{ .MemcachedAuthCert }}
12 | tls_keyfile={{ .MemcachedAuthKey }}
13 | tls_cafile={{ .MemcachedAuthCa }}
14 | {{end}}
15 | {{else}}
16 | backend = dogpile.cache.memcached
17 | memcache_servers={{ .MemcachedServersWithInet }}
18 | {{end}}
19 | memcache_dead_retry = 30
20 | enabled=true
21 | tls_enabled={{ .MemcachedTLS }}
22 |
23 | [database]
24 | max_retries=-1
25 | db_max_retries=-1
26 | connection={{ .DatabaseConnection }}
27 |
28 | [oslo_policy]
29 | enforce_new_defaults = {{ .EnableSecureRBAC }}
30 | enforce_scope = {{ .EnableSecureRBAC }}
31 |
32 | [fernet_tokens]
33 | key_repository=/etc/keystone/fernet-keys
34 | max_active_keys={{ .FernetMaxActiveKeys }}
35 |
36 | {{ if (index . "TransportURL") }}
37 | [oslo_messaging_notifications]
38 | driver=messagingv2
39 | transport_url={{ .TransportURL }}
40 | topics = barbican_notifications
41 | {{ if (index . "QuorumQueues") -}}
42 | [oslo_messaging_rabbit]
43 | rabbit_quorum_queue=true
44 | rabbit_transient_quorum_queue=true
45 | amqp_durable_queues=true
46 | {{- end -}}
47 | {{ end }}
48 |
--------------------------------------------------------------------------------
/config/prometheus/monitor.yaml:
--------------------------------------------------------------------------------
1 | # Prometheus Monitor Service (Metrics)
2 | apiVersion: monitoring.coreos.com/v1
3 | kind: ServiceMonitor
4 | metadata:
5 | labels:
6 | control-plane: controller-manager
7 | app.kubernetes.io/name: keystone-operator
8 | app.kubernetes.io/managed-by: kustomize
9 | name: controller-manager-metrics-monitor
10 | namespace: system
11 | spec:
12 | endpoints:
13 | - path: /metrics
14 | port: https # Ensure this is the name of the port that exposes HTTPS metrics
15 | scheme: https
16 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
17 | tlsConfig:
18 | # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables
19 | # certificate verification, exposing the system to potential man-in-the-middle attacks.
20 | # For production environments, it is recommended to use cert-manager for automatic TLS certificate management.
21 | # To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml,
22 | # which securely references the certificate from the 'metrics-server-cert' secret.
23 | insecureSkipVerify: true
24 | selector:
25 | matchLabels:
26 | control-plane: controller-manager
27 | app.kubernetes.io/name: keystone-operator
28 |
--------------------------------------------------------------------------------
/config/webhook/manifests.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: admissionregistration.k8s.io/v1
3 | kind: MutatingWebhookConfiguration
4 | metadata:
5 | name: mutating-webhook-configuration
6 | webhooks:
7 | - admissionReviewVersions:
8 | - v1
9 | clientConfig:
10 | service:
11 | name: webhook-service
12 | namespace: system
13 | path: /mutate-keystone-openstack-org-v1beta1-keystoneapi
14 | failurePolicy: Fail
15 | name: mkeystoneapi-v1beta1.kb.io
16 | rules:
17 | - apiGroups:
18 | - keystone.openstack.org
19 | apiVersions:
20 | - v1beta1
21 | operations:
22 | - CREATE
23 | - UPDATE
24 | resources:
25 | - keystoneapis
26 | sideEffects: None
27 | ---
28 | apiVersion: admissionregistration.k8s.io/v1
29 | kind: ValidatingWebhookConfiguration
30 | metadata:
31 | name: validating-webhook-configuration
32 | webhooks:
33 | - admissionReviewVersions:
34 | - v1
35 | clientConfig:
36 | service:
37 | name: webhook-service
38 | namespace: system
39 | path: /validate-keystone-openstack-org-v1beta1-keystoneapi
40 | failurePolicy: Fail
41 | name: vkeystoneapi-v1beta1.kb.io
42 | rules:
43 | - apiGroups:
44 | - keystone.openstack.org
45 | apiVersions:
46 | - v1beta1
47 | operations:
48 | - CREATE
49 | - UPDATE
50 | resources:
51 | - keystoneapis
52 | sideEffects: None
53 |
--------------------------------------------------------------------------------
/api/v1beta1/groupversion_info.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package v1beta1 contains API Schema definitions for the keystone v1beta1 API group
18 | // +kubebuilder:object:generate=true
19 | // +groupName=keystone.openstack.org
20 | package v1beta1
21 |
22 | import (
23 | "k8s.io/apimachinery/pkg/runtime/schema"
24 | "sigs.k8s.io/controller-runtime/pkg/scheme"
25 | )
26 |
27 | var (
28 | // GroupVersion is group version used to register these objects
29 | GroupVersion = schema.GroupVersion{Group: "keystone.openstack.org", Version: "v1beta1"}
30 |
31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme
32 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
33 |
34 | // AddToScheme adds the types in this group-version to the given scheme.
35 | AddToScheme = SchemeBuilder.AddToScheme
36 | )
37 |
--------------------------------------------------------------------------------
/PROJECT:
--------------------------------------------------------------------------------
1 | # Code generated by tool. DO NOT EDIT.
2 | # This file is used to track the info used to scaffold your project
3 | # and allow the plugins properly work.
4 | # More info: https://book.kubebuilder.io/reference/project-config.html
5 | domain: openstack.org
6 | layout:
7 | - go.kubebuilder.io/v4
8 | plugins:
9 | manifests.sdk.operatorframework.io/v2: {}
10 | scorecard.sdk.operatorframework.io/v2: {}
11 | projectName: keystone-operator
12 | repo: github.com/openstack-k8s-operators/keystone-operator
13 | resources:
14 | - api:
15 | crdVersion: v1
16 | namespaced: true
17 | controller: true
18 | domain: openstack.org
19 | group: keystone
20 | kind: KeystoneAPI
21 | path: github.com/openstack-k8s-operators/keystone-operator/api/v1beta1
22 | version: v1beta1
23 | webhooks:
24 | defaulting: true
25 | validation: true
26 | webhookVersion: v1
27 | - api:
28 | crdVersion: v1
29 | namespaced: true
30 | controller: true
31 | domain: openstack.org
32 | group: keystone
33 | kind: KeystoneService
34 | path: github.com/openstack-k8s-operators/keystone-operator/api/v1beta1
35 | version: v1beta1
36 | - api:
37 | crdVersion: v1
38 | namespaced: true
39 | controller: true
40 | domain: openstack.org
41 | group: keystone
42 | kind: KeystoneEndpoint
43 | path: github.com/openstack-k8s-operators/keystone-operator/api/v1beta1
44 | version: v1beta1
45 | version: "3"
46 |
--------------------------------------------------------------------------------
/config/scorecard/patches/olm.config.yaml:
--------------------------------------------------------------------------------
1 | - op: add
2 | path: /stages/0/tests/-
3 | value:
4 | entrypoint:
5 | - scorecard-test
6 | - olm-bundle-validation
7 | image: quay.io/operator-framework/scorecard-test:v1.41.1
8 | labels:
9 | suite: olm
10 | test: olm-bundle-validation-test
11 | - op: add
12 | path: /stages/0/tests/-
13 | value:
14 | entrypoint:
15 | - scorecard-test
16 | - olm-crds-have-validation
17 | image: quay.io/operator-framework/scorecard-test:v1.41.1
18 | labels:
19 | suite: olm
20 | test: olm-crds-have-validation-test
21 | - op: add
22 | path: /stages/0/tests/-
23 | value:
24 | entrypoint:
25 | - scorecard-test
26 | - olm-crds-have-resources
27 | image: quay.io/operator-framework/scorecard-test:v1.41.1
28 | labels:
29 | suite: olm
30 | test: olm-crds-have-resources-test
31 | - op: add
32 | path: /stages/0/tests/-
33 | value:
34 | entrypoint:
35 | - scorecard-test
36 | - olm-spec-descriptors
37 | image: quay.io/operator-framework/scorecard-test:v1.41.1
38 | labels:
39 | suite: olm
40 | test: olm-spec-descriptors-test
41 | - op: add
42 | path: /stages/0/tests/-
43 | value:
44 | entrypoint:
45 | - scorecard-test
46 | - olm-status-descriptors
47 | image: quay.io/operator-framework/scorecard-test:v1.41.1
48 | labels:
49 | suite: olm
50 | test: olm-status-descriptors-test
51 |
--------------------------------------------------------------------------------
/internal/keystone/funcs.go:
--------------------------------------------------------------------------------
1 | package keystone
2 |
3 | import (
4 | corev1 "k8s.io/api/core/v1"
5 | "k8s.io/utils/ptr"
6 | )
7 |
8 | // baseSecurityContext - currently used to make sure we don't run cronJob and Log
9 | // Pods as root user, and we drop privileges and Capabilities we don't need
10 | func baseSecurityContext() *corev1.SecurityContext {
11 | return &corev1.SecurityContext{
12 | RunAsUser: ptr.To(KeystoneUID),
13 | RunAsGroup: ptr.To(KeystoneUID),
14 | RunAsNonRoot: ptr.To(true),
15 | AllowPrivilegeEscalation: ptr.To(false),
16 | Capabilities: &corev1.Capabilities{
17 | Drop: []corev1.Capability{
18 | "ALL",
19 | },
20 | },
21 | }
22 | }
23 |
24 | // dbSyncSecurityContext - currently used to make sure we don't run db-sync as
25 | // root user
26 | func dbSyncSecurityContext() *corev1.SecurityContext {
27 | return &corev1.SecurityContext{
28 | RunAsUser: ptr.To(KeystoneUID),
29 | RunAsGroup: ptr.To(KeystoneUID),
30 | Capabilities: &corev1.Capabilities{
31 | Drop: []corev1.Capability{
32 | "MKNOD",
33 | },
34 | },
35 | }
36 | }
37 |
38 | // httpdSecurityContext -
39 | func httpdSecurityContext() *corev1.SecurityContext {
40 | return &corev1.SecurityContext{
41 | Capabilities: &corev1.Capabilities{
42 | Drop: []corev1.Capability{
43 | "MKNOD",
44 | },
45 | },
46 | RunAsUser: ptr.To(KeystoneUID),
47 | RunAsGroup: ptr.To(KeystoneUID),
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/.pull_request_pipeline:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent none
3 | stages {
4 | stage('Pull request proposed jobs') {
5 | when {
6 | branch 'PR-*'
7 | }
8 | parallel {
9 | stage('DFG-converged-openstack-k8s-keystone-operator-distgit-pr') {
10 | steps {
11 | build job: 'DFG-converged-openstack-k8s-keystone-operator-distgit-pr',
12 | parameters: [
13 | string(name: 'GITHUB_PULL_REQUEST_ID', value: String.valueOf(CHANGE_ID))
14 | ]
15 | }
16 | }
17 | stage('DFG-converged-openstack-k8s-keystone-operator-buildah') {
18 | steps {
19 | build job: 'DFG-converged-openstack-k8s-keystone-operator-buildah',
20 | parameters: [
21 | string(name: 'GITHUB_PULL_REQUEST_ID', value: String.valueOf(CHANGE_ID))
22 | ]
23 | }
24 | }
25 | }
26 | }
27 | stage('Pull request merged jobs') {
28 | when {
29 | allOf {
30 | environment name: 'CHANGE_ID', value: ''
31 | branch 'master'
32 | }
33 | }
34 | steps {
35 | build job: 'DFG-converged-openstack-k8s-keystone-operator-merge'
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/config/rbac/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | # All RBAC will be applied under this service account in
3 | # the deployment namespace. You may comment out this resource
4 | # if your manager will use a service account that exists at
5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding
6 | # subjects if changing service account names.
7 | - service_account.yaml
8 | - role.yaml
9 | - role_binding.yaml
10 | - leader_election_role.yaml
11 | - leader_election_role_binding.yaml
12 | # The following RBAC configurations are used to protect
13 | # the metrics endpoint with authn/authz. These configurations
14 | # ensure that only authorized users and service accounts
15 | # can access the metrics endpoint. Comment the following
16 | # permissions if you want to disable this protection.
17 | # More info: https://book.kubebuilder.io/reference/metrics.html
18 | - metrics_auth_role.yaml
19 | - metrics_auth_role_binding.yaml
20 | - metrics_reader_role.yaml
21 | # For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by
22 | # default, aiding admins in cluster management. Those roles are
23 | # not used by the keystone-operator itself. You can comment the following lines
24 | # if you do not want those helpers be installed with your Project.
25 | - keystoneendpoint_admin_role.yaml
26 | - keystoneendpoint_editor_role.yaml
27 | - keystoneendpoint_viewer_role.yaml
28 | - keystoneservice_admin_role.yaml
29 | - keystoneservice_editor_role.yaml
30 | - keystoneservice_viewer_role.yaml
31 | - keystoneapi_admin_role.yaml
32 | - keystoneapi_editor_role.yaml
33 | - keystoneapi_viewer_role.yaml
34 |
--------------------------------------------------------------------------------
/test/kuttl/tests/fernet_rotation/01-deploy_openstackclient.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | annotations:
5 | openshift.io/scc: anyuid
6 | labels:
7 | app: openstackclient
8 | name: openstackclient
9 | spec:
10 | containers:
11 | - args:
12 | - --single-child
13 | - --
14 | - /bin/bash
15 | - -c
16 | - /bin/sleep infinity
17 | command:
18 | - /bin/dumb-init
19 | env:
20 | - name: OS_CLOUD
21 | value: default
22 | image: quay.io/podified-antelope-centos9/openstack-openstackclient:current-podified
23 | imagePullPolicy: IfNotPresent
24 | name: openstackclient
25 | resources: {}
26 | securityContext:
27 | capabilities:
28 | drop:
29 | - ALL
30 | runAsGroup: 42401
31 | runAsNonRoot: true
32 | runAsUser: 42401
33 | allowPrivilegeEscalation: false
34 | seccompProfile:
35 | type: RuntimeDefault
36 | terminationMessagePath: /dev/termination-log
37 | terminationMessagePolicy: File
38 | volumeMounts:
39 | - mountPath: /etc/openstack/clouds.yaml
40 | name: openstack-config
41 | subPath: clouds.yaml
42 | - mountPath: /etc/openstack/secure.yaml
43 | name: openstack-config-secret
44 | subPath: secure.yaml
45 | dnsPolicy: ClusterFirst
46 | enableServiceLinks: true
47 | volumes:
48 | - configMap:
49 | defaultMode: 420
50 | name: openstack-config
51 | name: openstack-config
52 | - name: openstack-config-secret
53 | secret:
54 | defaultMode: 420
55 | secretName: openstack-config-secret
56 |
--------------------------------------------------------------------------------
/.github/workflows/release-keystone-operator.yaml:
--------------------------------------------------------------------------------
1 | name: Release Keystone Operator
2 |
3 | on:
4 | release:
5 | types:
6 | - released
7 | - prereleased
8 |
9 | env:
10 | imageregistry: 'quay.io'
11 | imagenamespace: ${{ secrets.IMAGENAMESPACE || secrets.QUAY_USERNAME }}
12 |
13 | jobs:
14 | release:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v2
19 |
20 | - name: Tag image
21 | uses: tinact/docker.image-retag@1.0.2
22 | with:
23 | image_name: ${{ env.imagenamespace }}/
24 | image_old_tag: ${{ github.sha }}
25 | image_new_tag: ${{ github.event.release.tag_name }}
26 | registry: ${{ env.imageregistry }}
27 | registry_username: ${{ secrets.QUAY_USERNAME }}
28 | registry_password: ${{ secrets.QUAY_PASSWORD }}
29 |
30 | - name: Tag -bundle image
31 | uses: tinact/docker.image-retag@1.0.2
32 | with:
33 | image_name: ${{ env.imagenamespace }}/-bundle
34 | image_old_tag: ${{ github.sha }}
35 | image_new_tag: ${{ github.event.release.tag_name }}
36 | registry: ${{ env.imageregistry }}
37 | registry_username: ${{ secrets.QUAY_USERNAME }}
38 | registry_password: ${{ secrets.QUAY_PASSWORD }}
39 |
40 | - name: Tag -index image
41 | uses: tinact/docker.image-retag@1.0.2
42 | with:
43 | image_name: ${{ env.imagenamespace }}/-index
44 | image_old_tag: ${{ github.sha }}
45 | image_new_tag: ${{ github.event.release.tag_name }}
46 | registry: ${{ env.imageregistry }}
47 | registry_username: ${{ secrets.QUAY_USERNAME }}
48 | registry_password: ${{ secrets.QUAY_PASSWORD }}
49 |
--------------------------------------------------------------------------------
/internal/keystone/federation.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package keystone
17 |
18 | import (
19 | "path/filepath"
20 | "strconv"
21 |
22 | corev1 "k8s.io/api/core/v1"
23 | )
24 |
25 | // getFederationVolumeMounts - get federation mountpoints
26 | func getFederationVolumeMounts(
27 | federationMountPath string,
28 | federationFilenames []string,
29 | ) []corev1.VolumeMount {
30 | vms := []corev1.VolumeMount{}
31 |
32 | for index, filename := range federationFilenames {
33 | vm := corev1.VolumeMount{
34 | Name: "federation-realm-volume" + strconv.Itoa(index),
35 | MountPath: filepath.Join(federationMountPath, filename),
36 | SubPath: strconv.Itoa(index),
37 | ReadOnly: true,
38 | }
39 | vms = append(vms, vm)
40 | }
41 |
42 | return vms
43 | }
44 |
45 | // getFederationVolumes - get federation volumes
46 | func getFederationVolumes(
47 | federationFilenames []string,
48 | ) []corev1.Volume {
49 | var config0640AccessMode int32 = 0644
50 | vols := []corev1.Volume{}
51 |
52 | for index := range federationFilenames {
53 | vol := corev1.Volume{
54 | Name: "federation-realm-volume" + strconv.Itoa(index),
55 | VolumeSource: corev1.VolumeSource{
56 | Secret: &corev1.SecretVolumeSource{
57 | DefaultMode: &config0640AccessMode,
58 | SecretName: FederationMultiRealmSecret,
59 | },
60 | },
61 | }
62 | vols = append(vols, vol)
63 | }
64 | return vols
65 | }
66 |
--------------------------------------------------------------------------------
/templates/keystoneapi/config/httpd.conf:
--------------------------------------------------------------------------------
1 | ServerTokens Prod
2 | ServerSignature Off
3 | TraceEnable Off
4 | PidFile run/httpd.pid
5 | ServerRoot "/etc/httpd"
6 | ServerName "localhost.localdomain"
7 |
8 | User apache
9 | Group apache
10 |
11 | Listen 5000
12 |
13 | TypesConfig /etc/mime.types
14 |
15 | Include conf.modules.d/*.conf
16 | Include conf.d/*.conf
17 |
18 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
19 | LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy
20 |
21 | SetEnvIf X-Forwarded-For "^.*\..*\..*\..*" forwarded
22 | CustomLog /dev/stdout combined env=!forwarded
23 | CustomLog /dev/stdout proxy env=forwarded
24 |
25 | {{ range $endpt, $vhost := .VHosts }}
26 | # {{ $endpt }} vhost {{ $vhost.ServerName }} configuration
27 |
28 | ServerName {{ $vhost.ServerName }}
29 | TimeOut {{ $.TimeOut }}
30 |
31 | ## Vhost docroot
32 | DocumentRoot "/var/www/cgi-bin/keystone"
33 |
34 | ## Directories, there should at least be a declaration for /var/www/cgi-bin/keystone
35 |
36 | Options -Indexes +FollowSymLinks +MultiViews
37 | AllowOverride None
38 | Require all granted
39 |
40 |
41 | ## Logging
42 | ErrorLog /dev/stdout
43 | ServerSignature Off
44 | CustomLog /dev/stdout combined
45 |
46 | {{- if $vhost.TLS }}
47 | SetEnvIf X-Forwarded-Proto https HTTPS=1
48 |
49 | ## SSL directives
50 | SSLEngine on
51 | SSLCertificateFile "{{ $vhost.SSLCertificateFile }}"
52 | SSLCertificateKeyFile "{{ $vhost.SSLCertificateKeyFile }}"
53 | {{- end }}
54 |
55 | ## WSGI configuration
56 | WSGIApplicationGroup %{GLOBAL}
57 | WSGIDaemonProcess {{ $endpt }} display-name={{ $endpt }} group=keystone processes={{ $.ProcessNumber }} threads=1 user=keystone
58 | WSGIProcessGroup {{ $endpt }}
59 | WSGIScriptAlias / "/usr/bin/keystone-wsgi-public"
60 | WSGIPassAuthorization On
61 |
62 | {{- if $vhost.Override }}
63 | Include conf/httpd_custom_{{ $endpt }}_*
64 | {{- end }}
65 |
66 |
67 | {{ end }}
68 |
--------------------------------------------------------------------------------
/internal/keystone/cloudconfig.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package keystone
18 |
19 | import "fmt"
20 |
21 | // OpenStackConfig type
22 | type OpenStackConfig struct {
23 | Clouds struct {
24 | Default struct {
25 | Auth struct {
26 | AuthURL string `yaml:"auth_url"`
27 | ProjectName string `yaml:"project_name"`
28 | UserName string `yaml:"username"`
29 | UserDomainName string `yaml:"user_domain_name"`
30 | ProjectDomainName string `yaml:"project_domain_name"`
31 | } `yaml:"auth"`
32 | RegionName string `yaml:"region_name"`
33 | } `yaml:"default"`
34 | }
35 | }
36 |
37 | // OpenStackConfigSecret type
38 | type OpenStackConfigSecret struct {
39 | Clouds struct {
40 | Default struct {
41 | Auth struct {
42 | Password string `yaml:"password"`
43 | }
44 | }
45 | }
46 | }
47 |
48 | // GenerateCloudrc generates file contents of a cloudrc file for the clients
49 | // until there is parity with openstackclient.
50 | func GenerateCloudrc(secret *OpenStackConfigSecret, config *OpenStackConfig) string {
51 | auth := config.Clouds.Default.Auth
52 | val := fmt.Sprintf(
53 | "export OS_AUTH_URL=%s"+
54 | "\nexport OS_USERNAME=%s"+
55 | "\nexport OS_PROJECT_NAME=%s"+
56 | "\nexport OS_PROJECT_DOMAIN_NAME=%s"+
57 | "\nexport OS_USER_DOMAIN_NAME=%s"+
58 | "\nexport OS_PASSWORD=%s",
59 | auth.AuthURL,
60 | auth.UserName,
61 | auth.ProjectName,
62 | auth.ProjectDomainName,
63 | auth.UserDomainName,
64 | secret.Clouds.Default.Auth.Password)
65 | return val
66 | }
67 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: local
3 | hooks:
4 | - id: gotidy
5 | name: gotidy
6 | language: system
7 | entry: make
8 | args: ["tidy"]
9 | pass_filenames: false
10 | - id: make-manifests
11 | name: make-manifests
12 | language: system
13 | entry: make
14 | args: ['manifests']
15 | pass_filenames: false
16 | - id: make-generate
17 | name: make-generate
18 | language: system
19 | entry: make
20 | args: ['generate']
21 | pass_filenames: false
22 | - id: make-operator-lint
23 | name: make-operator-lint
24 | language: system
25 | entry: make
26 | args: ['operator-lint']
27 | pass_filenames: false
28 | - id: make-crd-schema-check
29 | name: make-crd-schema-check
30 | language: system
31 | entry: make
32 | args: ['crd-schema-check']
33 | pass_filenames: false
34 |
35 | - repo: https://github.com/pre-commit/pre-commit-hooks
36 | rev: v4.4.0
37 | hooks:
38 | - id: check-added-large-files
39 | - id: fix-byte-order-marker
40 | - id: check-case-conflict
41 | - id: check-executables-have-shebangs
42 | exclude: ^vendor
43 | - id: check-shebang-scripts-are-executable
44 | exclude: ^vendor
45 | - id: check-merge-conflict
46 | - id: check-symlinks
47 | - id: destroyed-symlinks
48 | - id: check-yaml
49 | args: [-m]
50 | - id: check-json
51 | - id: detect-private-key
52 | - id: end-of-file-fixer
53 | exclude: ^vendor
54 | - id: no-commit-to-branch
55 | - id: trailing-whitespace
56 | exclude: ^vendor
57 |
58 | - repo: https://github.com/openstack/bashate.git
59 | rev: 2.1.1
60 | hooks:
61 | - id: bashate
62 | entry: bashate --error . --ignore=E006,E040,E011,E020,E012
63 |
64 | - repo: https://github.com/golangci/golangci-lint
65 | rev: v2.4.0
66 | hooks:
67 | - id: golangci-lint-full
68 | args: ["-v"]
69 |
70 | - repo: https://github.com/openstack-k8s-operators/openstack-k8s-operators-ci
71 | # NOTE(gibi): we cannot automatically track main here
72 | # see https://pre-commit.com/#using-the-latest-version-for-a-repository
73 | rev: e30d72fcbced0ab8a7b6d23be1dee129e2a7b849
74 | hooks:
75 | - id: kuttl-single-test-assert
76 | args: ["tests/kuttl"]
77 |
--------------------------------------------------------------------------------
/config/manifests/bases/keystone-operator.clusterserviceversion.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: operators.coreos.com/v1alpha1
2 | kind: ClusterServiceVersion
3 | metadata:
4 | annotations:
5 | alm-examples: '[]'
6 | capabilities: Basic Install
7 | features.operators.openshift.io/disconnected: "true"
8 | features.operators.openshift.io/fips-compliant: "true"
9 | features.operators.openshift.io/proxy-aware: "false"
10 | features.operators.openshift.io/tls-profiles: "false"
11 | features.operators.openshift.io/token-auth-aws: "false"
12 | features.operators.openshift.io/token-auth-azure: "false"
13 | features.operators.openshift.io/token-auth-gcp: "false"
14 | operatorframework.io/suggested-namespace: openstack
15 | operators.operatorframework.io/operator-type: non-standalone
16 | name: keystone-operator.v0.0.0
17 | namespace: placeholder
18 | spec:
19 | apiservicedefinitions: {}
20 | customresourcedefinitions:
21 | owned:
22 | - description: KeystoneAPI is the Schema for the keystoneapis API
23 | displayName: Keystone API
24 | kind: KeystoneAPI
25 | name: keystoneapis.keystone.openstack.org
26 | specDescriptors:
27 | - description: TLS - Parameters related to the TLS
28 | displayName: TLS
29 | path: tls
30 | version: v1beta1
31 | - description: KeystoneEndpoint is the Schema for the keystoneendpoints API
32 | displayName: Keystone Endpoint
33 | kind: KeystoneEndpoint
34 | name: keystoneendpoints.keystone.openstack.org
35 | version: v1beta1
36 | - description: KeystoneService is the Schema for the keystoneservices API
37 | displayName: Keystone Service
38 | kind: KeystoneService
39 | name: keystoneservices.keystone.openstack.org
40 | version: v1beta1
41 | description: Keystone Operator
42 | displayName: Keystone Operator
43 | install:
44 | spec:
45 | deployments: null
46 | strategy: ""
47 | installModes:
48 | - supported: true
49 | type: OwnNamespace
50 | - supported: true
51 | type: SingleNamespace
52 | - supported: false
53 | type: MultiNamespace
54 | - supported: true
55 | type: AllNamespaces
56 | keywords:
57 | - OpenStack
58 | - Identity
59 | - Keystone
60 | links:
61 | - name: Keystone Operator
62 | url: https://github.com/openstack-k8s-operators/keystone-operator
63 | maturity: beta
64 | provider:
65 | name: Red Hat Inc.
66 | url: https://redhat.com/
67 | version: 0.0.0
68 | minKubeVersion: 0.0.0
69 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG GOLANG_BUILDER=registry.access.redhat.com/ubi9/go-toolset:1.24
2 | ARG OPERATOR_BASE_IMAGE=registry.access.redhat.com/ubi9/ubi-minimal:latest
3 |
4 | # Build the manager binary
5 | FROM $GOLANG_BUILDER AS builder
6 |
7 | #Arguments required by OSBS build system
8 | ARG CACHITO_ENV_FILE=/remote-source/cachito.env
9 |
10 | ARG REMOTE_SOURCE=.
11 | ARG REMOTE_SOURCE_DIR=/remote-source
12 | ARG REMOTE_SOURCE_SUBDIR=
13 | ARG DEST_ROOT=/dest-root
14 |
15 | ARG GO_BUILD_EXTRA_ARGS="-tags strictfipsruntime"
16 | ARG GO_BUILD_EXTRA_ENV_ARGS="CGO_ENABLED=1 GO111MODULE=on"
17 |
18 | COPY $REMOTE_SOURCE $REMOTE_SOURCE_DIR
19 | WORKDIR $REMOTE_SOURCE_DIR/$REMOTE_SOURCE_SUBDIR
20 |
21 | USER root
22 | RUN mkdir -p ${DEST_ROOT}/usr/local/bin/
23 |
24 | # cache deps before building and copying source so that we don't need to re-download as much
25 | # and so that source changes don't invalidate our downloaded layer
26 | RUN if [ ! -f $CACHITO_ENV_FILE ]; then go mod download ; fi
27 |
28 | # Build manager
29 | RUN if [ -f $CACHITO_ENV_FILE ] ; then source $CACHITO_ENV_FILE ; fi ; env ${GO_BUILD_EXTRA_ENV_ARGS} go build ${GO_BUILD_EXTRA_ARGS} -a -o ${DEST_ROOT}/manager cmd/main.go
30 |
31 | RUN cp -r templates ${DEST_ROOT}/templates
32 |
33 | # Use distroless as minimal base image to package the manager binary
34 | # Refer to https://github.com/GoogleContainerTools/distroless for more details
35 | FROM $OPERATOR_BASE_IMAGE
36 |
37 | ARG DEST_ROOT=/dest-root
38 | # NONROOT default id https://github.com/GoogleContainerTools/distroless/blob/main/base/base.bzl#L8=
39 | ARG USER_ID=65532
40 |
41 | ARG IMAGE_COMPONENT="keystone-operator-container"
42 | ARG IMAGE_NAME="keystone-operator"
43 | ARG IMAGE_VERSION="1.0.0"
44 | ARG IMAGE_SUMMARY="Keystone Operator"
45 | ARG IMAGE_DESC="This image includes the keystone-operator"
46 | ARG IMAGE_TAGS="cn-openstack openstack"
47 |
48 | ### DO NOT EDIT LINES BELOW
49 | # Auto generated using CI tools from
50 | # https://github.com/openstack-k8s-operators/openstack-k8s-operators-ci
51 |
52 | # Labels required by upstream and osbs build system
53 | LABEL com.redhat.component="${IMAGE_COMPONENT}" \
54 | name="${IMAGE_NAME}" \
55 | version="${IMAGE_VERSION}" \
56 | summary="${IMAGE_SUMMARY}" \
57 | io.k8s.name="${IMAGE_NAME}" \
58 | io.k8s.description="${IMAGE_DESC}" \
59 | io.openshift.tags="${IMAGE_TAGS}"
60 | ### DO NOT EDIT LINES ABOVE
61 |
62 | ENV USER_UID=$USER_ID \
63 | OPERATOR_TEMPLATES=/usr/share/keystone-operator/templates/ \
64 | WATCH_NAMESPACE=openstack,openshift-machine-api,openshift-sriov-network-operator
65 |
66 | WORKDIR /
67 |
68 | # Install operator binary to WORKDIR
69 | COPY --from=builder ${DEST_ROOT}/manager .
70 |
71 | # Install templates
72 | COPY --from=builder ${DEST_ROOT}/templates ${OPERATOR_TEMPLATES}
73 |
74 | USER $USER_ID
75 |
76 | ENV PATH="/:${PATH}"
77 |
78 | ENTRYPOINT ["/manager"]
79 |
--------------------------------------------------------------------------------
/internal/keystone/const.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package keystone
17 |
18 | import (
19 | "github.com/openstack-k8s-operators/lib-common/modules/storage"
20 | )
21 |
22 | const (
23 | // ServiceName -
24 | ServiceName = "keystone"
25 | // DatabaseName -
26 | DatabaseName = "keystone"
27 | // DatabaseCRName -
28 | DatabaseCRName = "keystone"
29 | // DatabaseUsernamePrefix -
30 | DatabaseUsernamePrefix = "keystone"
31 | // KeystonePublicPort -
32 | KeystonePublicPort int32 = 5000
33 | // KeystoneInternalPort -
34 | KeystoneInternalPort int32 = 5000
35 | // KeystoneUID is based on kolla
36 | // https://github.com/openstack/kolla/blob/master/kolla/common/users.py
37 | KeystoneUID int64 = 42425
38 | // DefaultFernetMaxActiveKeys -
39 | DefaultFernetMaxActiveKeys = 5
40 | // DefaultFernetRotationDays -
41 | DefaultFernetRotationDays = 1
42 | // DBSyncCommand -
43 | DBSyncCommand = "keystone-manage db_sync"
44 | // Keystone is the global ServiceType
45 | Keystone storage.PropagationType = "Keystone"
46 | // KeystoneCronJob is the CronJob ServiceType
47 | KeystoneCronJob storage.PropagationType = "KeystoneCron"
48 | // KeystoneBootstrap is the bootstrap service
49 | KeystoneBootstrap storage.PropagationType = "KeystoneBootstrap"
50 | // FederationConfigKey - key for multi-realm federation config secret
51 | FederationConfigKey = "federation-config.json"
52 | // FederationMultiRealmSecret - secret to store processed multirealm data
53 | FederationMultiRealmSecret = "keystone-multirealm-federation-secret"
54 | // FederationDefaultMountPath - if user doesn't specify otherwise, this location is used
55 | FederationDefaultMountPath = "/var/lib/config-data/default/multirealm-federation"
56 | )
57 |
58 | // KeystonePropagation is the definition of the Keystone propagation service
59 | var KeystonePropagation = []storage.PropagationType{Keystone}
60 |
61 | // DBSyncPropagation keeps track of the DBSync Service Propagation Type
62 | var DBSyncPropagation = []storage.PropagationType{storage.DBSync}
63 |
64 | // BootstrapPropagation keeps track of the KeystoneBootstrap Propagation Type
65 | var BootstrapPropagation = []storage.PropagationType{KeystoneBootstrap}
66 |
67 | // KeystoneCronJobPropagation keeps track of the KeystoneBootstrap Propagation Type
68 | var KeystoneCronJobPropagation = []storage.PropagationType{KeystoneCronJob}
69 |
--------------------------------------------------------------------------------
/internal/keystone/dbsync.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package keystone
17 |
18 | import (
19 | keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
20 |
21 | "github.com/openstack-k8s-operators/lib-common/modules/common/env"
22 |
23 | batchv1 "k8s.io/api/batch/v1"
24 | corev1 "k8s.io/api/core/v1"
25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 | )
27 |
28 | // DbSyncJob func
29 | func DbSyncJob(
30 | instance *keystonev1.KeystoneAPI,
31 | labels map[string]string,
32 | annotations map[string]string,
33 | ) *batchv1.Job {
34 |
35 | args := []string{"-c", DBSyncCommand}
36 |
37 | envVars := map[string]env.Setter{}
38 | envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS")
39 | envVars["KOLLA_BOOTSTRAP"] = env.SetValue("true")
40 |
41 | // create Volume and VolumeMounts
42 | dbSyncExtraMounts := []keystonev1.KeystoneExtraMounts{}
43 | volumes := getVolumes(instance, dbSyncExtraMounts, DBSyncPropagation)
44 | volumeMounts := getDBSyncVolumeMounts()
45 |
46 | // add CA cert if defined
47 | if instance.Spec.TLS.CaBundleSecretName != "" {
48 | volumes = append(volumes, instance.Spec.TLS.CreateVolume())
49 | volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...)
50 | }
51 |
52 | job := &batchv1.Job{
53 | ObjectMeta: metav1.ObjectMeta{
54 | Name: ServiceName + "-db-sync",
55 | Namespace: instance.Namespace,
56 | Labels: labels,
57 | },
58 | Spec: batchv1.JobSpec{
59 | Template: corev1.PodTemplateSpec{
60 | ObjectMeta: metav1.ObjectMeta{
61 | Annotations: annotations,
62 | },
63 | Spec: corev1.PodSpec{
64 | RestartPolicy: corev1.RestartPolicyOnFailure,
65 | ServiceAccountName: instance.RbacResourceName(),
66 | Containers: []corev1.Container{
67 | {
68 | Name: ServiceName + "-db-sync",
69 | Command: []string{
70 | "/bin/bash",
71 | },
72 | Args: args,
73 | Image: instance.Spec.ContainerImage,
74 | SecurityContext: dbSyncSecurityContext(),
75 | Env: env.MergeEnvs([]corev1.EnvVar{}, envVars),
76 | VolumeMounts: volumeMounts,
77 | },
78 | },
79 | Volumes: volumes,
80 | },
81 | },
82 | },
83 | }
84 |
85 | if instance.Spec.NodeSelector != nil {
86 | job.Spec.Template.Spec.NodeSelector = *instance.Spec.NodeSelector
87 | }
88 |
89 | return job
90 | }
91 |
--------------------------------------------------------------------------------
/test/kuttl/common/tls_ca_bundle.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Internal CA created with:
3 | #
4 | # apiVersion: cert-manager.io/v1
5 | # kind: Issuer
6 | # metadata:
7 | # name: rootca-kuttl-internal
8 | # namespace: openstack
9 | # spec:
10 | # ca:
11 | # secretName: rootca-kuttl-internal
12 | #
13 | # apiVersion: cert-manager.io/v1
14 | # kind: Certificate
15 | # metadata:
16 | # name: rootca-kuttl-internal
17 | # namespace: openstack
18 | # spec:
19 | # commonName: rootca-kuttl-internal
20 | # duration: 87600h0m0s
21 | # isCA: true
22 | # issuerRef:
23 | # name: selfsigned-issuer
24 | # privateKey:
25 | # algorithm: ECDSA
26 | # size: 256
27 | # secretName: rootca-kuttl-internal
28 | #
29 | # External CA created with:
30 | #
31 | # apiVersion: cert-manager.io/v1
32 | # kind: Issuer
33 | # metadata:
34 | # name: rootca-kuttl-public
35 | # namespace: openstack
36 | # spec:
37 | # ca:
38 | # secretName: rootca-kuttl-public
39 | #
40 | # apiVersion: cert-manager.io/v1
41 | # kind: Certificate
42 | # metadata:
43 | # name: rootca-kuttl-public
44 | # namespace: openstack
45 | # spec:
46 | # commonName: rootca-kuttl-public
47 | # duration: 87600h0m0s
48 | # isCA: true
49 | # issuerRef:
50 | # name: selfsigned-issuer
51 | # privateKey:
52 | # algorithm: ECDSA
53 | # size: 256
54 | # secretName: rootca-kuttl-public
55 | #
56 | # Then extracted both CAs and created added them as the bundle:
57 | apiVersion: v1
58 | data:
59 | tls-ca-bundle.pem: IyByb290Y2EtaW50ZXJuYWwKLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmekNDQVNhZ0F3SUJBZ0lRUWxlcTNZcDBtU2kwVDNiTm03Q29UVEFLQmdncWhrak9QUVFEQWpBZ01SNHcKSEFZRFZRUURFeFZ5YjI5MFkyRXRhM1YwZEd3dGFXNTBaWEp1WVd3d0hoY05NalF3TVRFMU1URTBOelUwV2hjTgpNelF3TVRFeU1URTBOelUwV2pBZ01SNHdIQVlEVlFRREV4VnliMjkwWTJFdGEzVjBkR3d0YVc1MFpYSnVZV3d3CldUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTRk9rNHJPUldVUGhoTjUrK09EN1I2MW5Gb1lBY0QKenpvUS91SW93NktjeGhwRWNQTDFxb3ZZUGxUYUJabEh3c2FpNE50VHA4aDA1RHVRSGZKOE9JNXFvMEl3UURBTwpCZ05WSFE4QkFmOEVCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXE3TGtFSk1TCm1MOVpKWjBSOUluKzZkclhycEl3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVlN1K00ydnZ3QlF3eTJHMVlhdkkKQld2RGtSNlRla0I5U0VqdzJIblRSMWtDSUZSNFNkWGFPQkFGWjVHa2RLWCtSY2IzaDFIZm52eFJEVW96bTl2agphenp3Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KIyByb290Y2EtcHVibGljCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlCZXpDQ0FTS2dBd0lCQWdJUU5IREdZc0JzNzk4aWJERDdxL28ybGpBS0JnZ3Foa2pPUFFRREFqQWVNUnd3CkdnWURWUVFERXhOeWIyOTBZMkV0YTNWMGRHd3RjSFZpYkdsak1CNFhEVEkwTURFeE5URXdNVFV6TmxvWERUTTAKTURFeE1qRXdNVFV6Tmxvd0hqRWNNQm9HQTFVRUF4TVRjbTl2ZEdOaExXdDFkSFJzTFhCMVlteHBZekJaTUJNRwpCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkQ3OGF2WHFocmhDNXc4czlXa2Q0SXBiZUV1MDNDUitYWFVkCmtEek9SeXhhOXdjY0lkRGl2YkdKakpGWlRUY1ZtYmpxMUJNWXNqcjEyVUlFNUVUM1ZscWpRakJBTUE0R0ExVWQKRHdFQi93UUVBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJUS0ppeldVSjllVUtpMQpkczBscjZjNnNEN0VCREFLQmdncWhrak9QUVFEQWdOSEFEQkVBaUJJWndZcTYxQnFNSmFCNlVjRm9Sc3hlY3dICjV6L3pNT2RyT3llMG1OaThKZ0lnUUxCNHdES3JwZjl0WDJsb00rMHVUb3BBRFNZSW5yY2ZWdTRGQnVZVTNJZz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
60 | kind: Secret
61 | metadata:
62 | labels:
63 | combined-ca-bundle: ""
64 | name: combined-ca-bundle
65 | type: Opaque
66 |
--------------------------------------------------------------------------------
/api/v1beta1/keystoneservice_types.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1beta1
18 |
19 | import (
20 | "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22 | )
23 |
24 | // KeystoneServiceSpec defines the desired state of KeystoneService
25 | type KeystoneServiceSpec struct {
26 | // +kubebuilder:validation:Required
27 | // ServiceType - Type is the type of the service.
28 | ServiceType string `json:"serviceType"`
29 | // +kubebuilder:validation:Required
30 | // ServiceName - Name of the service.
31 | ServiceName string `json:"serviceName"`
32 | // +kubebuilder:validation:Optional
33 | // ServiceDescription - Description for the service.
34 | ServiceDescription string `json:"serviceDescription,omitempty"`
35 | // +kubebuilder:validation:Required
36 | // Enabled - whether or not the service is enabled.
37 | Enabled bool `json:"enabled"`
38 | // +kubebuilder:validation:Required
39 | // ServiceUser - optional username used for this service
40 | ServiceUser string `json:"serviceUser"`
41 | // +kubebuilder:validation:Required
42 | // Secret containing OpenStack password information for the ServiceUser
43 | Secret string `json:"secret"`
44 | // +kubebuilder:validation:Required
45 | // PasswordSelector - Selector to get the ServiceUser password from the Secret, e.g. PlacementPassword
46 | PasswordSelector string `json:"passwordSelector"`
47 | }
48 |
49 | // KeystoneServiceStatus defines the observed state of KeystoneService
50 | type KeystoneServiceStatus struct {
51 | ServiceID string `json:"serviceID,omitempty"`
52 | // Conditions
53 | Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"`
54 | }
55 |
56 | //+kubebuilder:object:root=true
57 | //+kubebuilder:subresource:status
58 | //+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[0].status",description="Status"
59 | //+kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[0].message",description="Message"
60 |
61 | // KeystoneService is the Schema for the keystoneservices API
62 | type KeystoneService struct {
63 | metav1.TypeMeta `json:",inline"`
64 | metav1.ObjectMeta `json:"metadata,omitempty"`
65 |
66 | Spec KeystoneServiceSpec `json:"spec,omitempty"`
67 | Status KeystoneServiceStatus `json:"status,omitempty"`
68 | }
69 |
70 | //+kubebuilder:object:root=true
71 |
72 | // KeystoneServiceList contains a list of KeystoneService
73 | type KeystoneServiceList struct {
74 | metav1.TypeMeta `json:",inline"`
75 | metav1.ListMeta `json:"metadata,omitempty"`
76 | Items []KeystoneService `json:"items"`
77 | }
78 |
79 | func init() {
80 | SchemeBuilder.Register(&KeystoneService{}, &KeystoneServiceList{})
81 | }
82 |
83 | // IsReady - returns true if KeystoneService is reconciled successfully
84 | func (instance KeystoneService) IsReady() bool {
85 | return instance.Status.Conditions.IsTrue(condition.ReadyCondition)
86 | }
87 |
--------------------------------------------------------------------------------
/templates/keystoneapi/config/keystone-api-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "command": "/usr/sbin/httpd",
3 | "config_files": [
4 | {
5 | "source": "/var/lib/config-data/default/keystone.conf",
6 | "dest": "/etc/keystone/keystone.conf",
7 | "owner": "keystone",
8 | "perm": "0600"
9 | },
10 | {
11 | "source": "/var/lib/config-data/default/custom.conf",
12 | "dest": "/etc/keystone/keystone.conf.d/custom.conf",
13 | "owner": "keystone",
14 | "perm": "0600"
15 | },
16 | {
17 | "source": "/var/lib/config-data/default/httpd.conf",
18 | "dest": "/etc/httpd/conf/httpd.conf",
19 | "owner": "keystone:apache",
20 | "perm": "0644"
21 | },
22 | {
23 | "source": "/var/lib/config-data/default/ssl.conf",
24 | "dest": "/etc/httpd/conf.d/ssl.conf",
25 | "owner": "keystone:apache",
26 | "perm": "0644"
27 | },
28 | {
29 | "source": "/var/lib/config-data/tls/certs/*",
30 | "dest": "/etc/pki/tls/certs/",
31 | "owner": "keystone:apache",
32 | "perm": "0640",
33 | "optional": true,
34 | "merge": true
35 | },
36 | {
37 | "source": "/var/lib/config-data/tls/private/*",
38 | "dest": "/etc/pki/tls/private/",
39 | "owner": "keystone:apache",
40 | "perm": "0600",
41 | "optional": true,
42 | "merge": true
43 | },
44 | {
45 | "source": "/var/lib/config-data/mtls/certs/*",
46 | "dest": "/etc/pki/tls/certs/",
47 | "owner": "root:keystone",
48 | "perm": "0640",
49 | "optional": true,
50 | "merge": true
51 | },
52 | {
53 | "source": "/var/lib/config-data/mtls/private/*",
54 | "dest": "/etc/pki/tls/private/",
55 | "owner": "root:keystone",
56 | "perm": "0640",
57 | "optional": true,
58 | "merge": true
59 | },
60 | {
61 | "source": "/var/lib/config-data/default/my.cnf",
62 | "dest": "/etc/my.cnf",
63 | "owner": "keystone",
64 | "perm": "0644"
65 | },
66 | {
67 | "source": "/var/lib/config-data/default/httpd_custom_*",
68 | "dest": "/etc/httpd/conf/",
69 | "owner": "keystone:apache",
70 | "perm": "0444",
71 | "optional": true
72 | },
73 | {
74 | "source": "/var/lib/config-data/default/multirealm-federation/*",
75 | "dest": "/var/lib/httpd/metadata/",
76 | "owner": "keystone:apache",
77 | "perm": "0640",
78 | "optional": true,
79 | "merge": true
80 | }
81 |
82 | ],
83 | "permissions": [
84 | {
85 | "path": "/etc/httpd",
86 | "owner": "keystone:apache",
87 | "recurse": true
88 | },
89 | {
90 | "path": "/var/log/keystone",
91 | "owner": "keystone:apache",
92 | "recurse": true
93 | },
94 | {
95 | "path": "/var/lib/httpd/metadata",
96 | "owner": "keystone:apache",
97 | "perm": "0775",
98 | "recurse": true
99 | }
100 | ]
101 | }
102 |
--------------------------------------------------------------------------------
/api/v1beta1/keystoneendpoint_types.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1beta1
18 |
19 | import (
20 | "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22 | )
23 |
24 | // KeystoneEndpointSpec defines the desired state of KeystoneEndpoint
25 | type KeystoneEndpointSpec struct {
26 | // +kubebuilder:validation:Required
27 | // ServiceName - Name of the service to create the endpoint for
28 | ServiceName string `json:"serviceName"`
29 | // +kubebuilder:validation:Required
30 | // Endpoints - map with service api endpoint URLs with the endpoint type as index
31 | Endpoints map[string]string `json:"endpoints"`
32 | }
33 |
34 | // KeystoneEndpointStatus defines the observed state of KeystoneEndpoint
35 | type KeystoneEndpointStatus struct {
36 | EndpointIDs map[string]string `json:"endpointIDs,omitempty"`
37 | ServiceID string `json:"serviceID,omitempty"`
38 | // Conditions
39 | Conditions condition.Conditions `json:"conditions,omitempty" optional:"true"`
40 |
41 | // Endpoints - current status of latest configured endpoints for the service
42 | Endpoints []Endpoint `json:"endpoints,omitempty"`
43 |
44 | //ObservedGeneration - the most recent generation observed for this service. If the observed generation is less than the spec generation, then the controller has not processed the latest changes.
45 | ObservedGeneration int64 `json:"observedGeneration,omitempty"`
46 | }
47 |
48 | // Endpoint -
49 | type Endpoint struct {
50 | // Interface - public, internal, admin
51 | Interface string `json:"interface"`
52 | // URL - endpoint url
53 | URL string `json:"url"`
54 | // ID - endpoint id
55 | ID string `json:"id"`
56 | }
57 |
58 | //+kubebuilder:object:root=true
59 | //+kubebuilder:subresource:status
60 | //+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[0].status",description="Status"
61 | //+kubebuilder:printcolumn:name="Message",type="string",JSONPath=".status.conditions[0].message",description="Message"
62 |
63 | // KeystoneEndpoint is the Schema for the keystoneendpoints API
64 | type KeystoneEndpoint struct {
65 | metav1.TypeMeta `json:",inline"`
66 | metav1.ObjectMeta `json:"metadata,omitempty"`
67 |
68 | Spec KeystoneEndpointSpec `json:"spec,omitempty"`
69 | Status KeystoneEndpointStatus `json:"status,omitempty"`
70 | }
71 |
72 | //+kubebuilder:object:root=true
73 |
74 | // KeystoneEndpointList contains a list of KeystoneEndpoint
75 | type KeystoneEndpointList struct {
76 | metav1.TypeMeta `json:",inline"`
77 | metav1.ListMeta `json:"metadata,omitempty"`
78 | Items []KeystoneEndpoint `json:"items"`
79 | }
80 |
81 | func init() {
82 | SchemeBuilder.Register(&KeystoneEndpoint{}, &KeystoneEndpointList{})
83 | }
84 |
85 | // IsReady - returns true if KeystoneEndpoint is reconciled successfully
86 | func (instance KeystoneEndpoint) IsReady() bool {
87 | return instance.Status.Conditions.IsTrue(condition.ReadyCondition)
88 | }
89 |
--------------------------------------------------------------------------------
/config/rbac/role.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: manager-role
6 | rules:
7 | - apiGroups:
8 | - ""
9 | resources:
10 | - configmaps
11 | - pods
12 | - secrets
13 | - services
14 | verbs:
15 | - create
16 | - delete
17 | - get
18 | - list
19 | - patch
20 | - update
21 | - watch
22 | - apiGroups:
23 | - ""
24 | resources:
25 | - serviceaccounts
26 | verbs:
27 | - create
28 | - get
29 | - list
30 | - patch
31 | - update
32 | - watch
33 | - apiGroups:
34 | - apps
35 | resources:
36 | - deployments
37 | verbs:
38 | - create
39 | - delete
40 | - get
41 | - list
42 | - patch
43 | - update
44 | - watch
45 | - apiGroups:
46 | - batch
47 | resources:
48 | - cronjobs
49 | - jobs
50 | verbs:
51 | - create
52 | - delete
53 | - get
54 | - list
55 | - patch
56 | - update
57 | - watch
58 | - apiGroups:
59 | - k8s.cni.cncf.io
60 | resources:
61 | - network-attachment-definitions
62 | verbs:
63 | - get
64 | - list
65 | - watch
66 | - apiGroups:
67 | - keystone.openstack.org
68 | resources:
69 | - keystoneapis
70 | - keystoneendpoints
71 | - keystoneservices
72 | verbs:
73 | - create
74 | - delete
75 | - get
76 | - list
77 | - patch
78 | - update
79 | - watch
80 | - apiGroups:
81 | - keystone.openstack.org
82 | resources:
83 | - keystoneapis/finalizers
84 | - keystoneendpoints/finalizers
85 | - keystoneservices/finalizers
86 | verbs:
87 | - patch
88 | - update
89 | - apiGroups:
90 | - keystone.openstack.org
91 | resources:
92 | - keystoneapis/status
93 | - keystoneendpoints/status
94 | - keystoneservices/status
95 | verbs:
96 | - get
97 | - patch
98 | - update
99 | - apiGroups:
100 | - mariadb.openstack.org
101 | resources:
102 | - mariadbaccounts
103 | - mariadbdatabases
104 | verbs:
105 | - create
106 | - delete
107 | - get
108 | - list
109 | - patch
110 | - update
111 | - watch
112 | - apiGroups:
113 | - mariadb.openstack.org
114 | resources:
115 | - mariadbaccounts/finalizers
116 | verbs:
117 | - patch
118 | - update
119 | - apiGroups:
120 | - memcached.openstack.org
121 | resources:
122 | - memcacheds
123 | verbs:
124 | - get
125 | - list
126 | - patch
127 | - update
128 | - watch
129 | - apiGroups:
130 | - memcached.openstack.org
131 | resources:
132 | - memcacheds/finalizers
133 | verbs:
134 | - patch
135 | - update
136 | - apiGroups:
137 | - rabbitmq.openstack.org
138 | resources:
139 | - transporturls
140 | verbs:
141 | - create
142 | - delete
143 | - get
144 | - list
145 | - patch
146 | - update
147 | - watch
148 | - apiGroups:
149 | - rbac.authorization.k8s.io
150 | resources:
151 | - rolebindings
152 | - roles
153 | verbs:
154 | - create
155 | - get
156 | - list
157 | - patch
158 | - update
159 | - watch
160 | - apiGroups:
161 | - route.openshift.io
162 | resources:
163 | - routes
164 | verbs:
165 | - create
166 | - delete
167 | - get
168 | - list
169 | - patch
170 | - update
171 | - watch
172 | - apiGroups:
173 | - security.openshift.io
174 | resourceNames:
175 | - anyuid
176 | resources:
177 | - securitycontextconstraints
178 | verbs:
179 | - use
180 | - apiGroups:
181 | - topology.openstack.org
182 | resources:
183 | - topologies
184 | verbs:
185 | - get
186 | - list
187 | - update
188 | - watch
189 |
--------------------------------------------------------------------------------
/config/manager/manager.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | labels:
5 | control-plane: controller-manager
6 | app.kubernetes.io/name: keystone-operator
7 | app.kubernetes.io/managed-by: kustomize
8 | name: system
9 | ---
10 | apiVersion: apps/v1
11 | kind: Deployment
12 | metadata:
13 | name: controller-manager
14 | namespace: system
15 | labels:
16 | control-plane: controller-manager
17 | app.kubernetes.io/name: keystone-operator
18 | app.kubernetes.io/managed-by: kustomize
19 | spec:
20 | selector:
21 | matchLabels:
22 | control-plane: controller-manager
23 | app.kubernetes.io/name: keystone-operator
24 | replicas: 1
25 | template:
26 | metadata:
27 | annotations:
28 | kubectl.kubernetes.io/default-container: manager
29 | labels:
30 | control-plane: controller-manager
31 | app.kubernetes.io/name: keystone-operator
32 | spec:
33 | # TODO(user): Uncomment the following code to configure the nodeAffinity expression
34 | # according to the platforms which are supported by your solution.
35 | # It is considered best practice to support multiple architectures. You can
36 | # build your manager image using the makefile target docker-buildx.
37 | # affinity:
38 | # nodeAffinity:
39 | # requiredDuringSchedulingIgnoredDuringExecution:
40 | # nodeSelectorTerms:
41 | # - matchExpressions:
42 | # - key: kubernetes.io/arch
43 | # operator: In
44 | # values:
45 | # - amd64
46 | # - arm64
47 | # - ppc64le
48 | # - s390x
49 | # - key: kubernetes.io/os
50 | # operator: In
51 | # values:
52 | # - linux
53 | securityContext:
54 | # Projects are configured by default to adhere to the "restricted" Pod Security Standards.
55 | # This ensures that deployments meet the highest security requirements for Kubernetes.
56 | # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted
57 | runAsNonRoot: true
58 | seccompProfile:
59 | type: RuntimeDefault
60 | containers:
61 | - command:
62 | - /manager
63 | args:
64 | - --leader-elect
65 | - --health-probe-bind-address=:8081
66 | image: controller:latest
67 | name: manager
68 | ports: []
69 | securityContext:
70 | allowPrivilegeEscalation: false
71 | capabilities:
72 | drop:
73 | - "ALL"
74 | livenessProbe:
75 | httpGet:
76 | path: /healthz
77 | port: 8081
78 | initialDelaySeconds: 15
79 | periodSeconds: 20
80 | readinessProbe:
81 | httpGet:
82 | path: /readyz
83 | port: 8081
84 | initialDelaySeconds: 5
85 | periodSeconds: 10
86 | # TODO(user): Configure the resources accordingly based on the project requirements.
87 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
88 | resources:
89 | limits:
90 | cpu: 500m
91 | memory: 128Mi
92 | requests:
93 | cpu: 10m
94 | memory: 64Mi
95 | volumeMounts: []
96 | volumes: []
97 | serviceAccountName: controller-manager
98 | terminationGracePeriodSeconds: 10
99 |
--------------------------------------------------------------------------------
/api/v1beta1/conditions.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package v1beta1
17 |
18 | import (
19 | condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
20 | )
21 |
22 | // Keystone Condition Types used by API objects.
23 | const (
24 | // KeystoneAPIReadyCondition Status=True condition which indicates if the KeystoneAPI is configured and operational
25 | KeystoneAPIReadyCondition condition.Type = "KeystoneAPIReady"
26 |
27 | // AdminServiceClientReadyCondition Status=True condition which indicates if the admin client is ready and can be used
28 | AdminServiceClientReadyCondition condition.Type = "AdminServiceClientReady"
29 |
30 | // KeystoneServiceOSServiceReadyCondition Status=True condition which indicates if the service created in the keystone instance is ready/was successful
31 | KeystoneServiceOSServiceReadyCondition condition.Type = "KeystoneServiceOSServiceReady"
32 |
33 | // KeystoneServiceOSEndpointsReadyCondition Status=True condition which indicates if the endpoints got created in the keystone instance is ready/was successful
34 | KeystoneServiceOSEndpointsReadyCondition condition.Type = "KeystoneServiceOSEndpointsReady"
35 |
36 | // KeystoneServiceOSUserReadyCondition Status=True condition which indicates if the service user got created in the keystone instance is ready/was successful
37 | KeystoneServiceOSUserReadyCondition condition.Type = "KeystoneServiceOSUserReady"
38 | )
39 |
40 | // Common Messages used by API objects.
41 | const (
42 |
43 | //
44 | // KeystoneAPIReady condition messages
45 | //
46 | // KeystoneAPIReadyInitMessage
47 | KeystoneAPIReadyInitMessage = "KeystoneAPI not started"
48 |
49 | // KeystoneAPIReadyMessage
50 | KeystoneAPIReadyMessage = "KeystoneAPI ready"
51 |
52 | // KeystoneAPIReadyNotFoundMessage
53 | KeystoneAPIReadyNotFoundMessage = "KeystoneAPI not found"
54 |
55 | // KeystoneAPIReadyWaitingMessage
56 | KeystoneAPIReadyWaitingMessage = "KeystoneAPI not yet ready"
57 |
58 | // KeystoneAPIReadyErrorMessage
59 | KeystoneAPIReadyErrorMessage = "KeystoneAPI error occured %s"
60 |
61 | //
62 | // AdminServiceClientReady condition messages
63 | //
64 | // AdminServiceClientReadyInitMessage
65 | AdminServiceClientReadyInitMessage = "Admin client not started"
66 |
67 | // AdminServiceClientReadyMessage
68 | AdminServiceClientReadyMessage = "Admin client ready"
69 |
70 | // AdminServiceClientReadyWaitingMessage
71 | AdminServiceClientReadyWaitingMessage = "Admin client not yet ready"
72 |
73 | // AdminServiceClientReadyErrorMessage
74 | AdminServiceClientReadyErrorMessage = "Admin client error occured %s"
75 |
76 | //
77 | // KeystoneServiceOSServiceReady condition messages
78 | //
79 | // KeystoneServiceOSServiceReadyInitMessage
80 | KeystoneServiceOSServiceReadyInitMessage = "Keystone Service registration not started"
81 |
82 | // KeystoneServiceOSServiceReadyMessage
83 | KeystoneServiceOSServiceReadyMessage = "Keystone Service %s - %s ready"
84 |
85 | // AdminServiceClientReadyErrorMessage
86 | KeystoneServiceOSServiceReadyErrorMessage = "Keystone Service error occured %s"
87 |
88 | //
89 | // KeystoneServiceOSEndpointsReady condition messages
90 | //
91 | // KeystoneServiceOSEndpointsReadyInitMessage
92 | KeystoneServiceOSEndpointsReadyInitMessage = "Keystone Endpoints registration not started"
93 |
94 | // KeystoneServiceOSEndpointsReadyMessage
95 | KeystoneServiceOSEndpointsReadyMessage = "Keystone Endpoints ready: %+v"
96 |
97 | // KeystoneServiceOSEndpointsReadyErrorMessage
98 | KeystoneServiceOSEndpointsReadyErrorMessage = "Keystone Endpoints error occured %s"
99 |
100 | //
101 | // KeystoneServiceOSUserReady condition messages
102 | //
103 | // KeystoneServiceOSUserReadyInitMessage
104 | KeystoneServiceOSUserReadyInitMessage = "Keystone Service user registration not started"
105 |
106 | // KeystoneServiceOSUserReadyMessage
107 | KeystoneServiceOSUserReadyMessage = "Keystone Service user %s ready"
108 |
109 | // KeystoneServiceOSUserReadyWaitingMessage
110 | KeystoneServiceOSUserReadyWaitingMessage = "Keystone Service user not yet ready"
111 |
112 | // KeystoneServiceOSUserReadyErrorMessage
113 | KeystoneServiceOSUserReadyErrorMessage = "Keystone Service user error occured %s"
114 | )
115 |
--------------------------------------------------------------------------------
/internal/keystone/cronjob.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package keystone
17 |
18 | import (
19 | memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1"
20 | keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
21 | "github.com/openstack-k8s-operators/lib-common/modules/common/env"
22 | "github.com/openstack-k8s-operators/lib-common/modules/common/tls"
23 |
24 | batchv1 "k8s.io/api/batch/v1"
25 | corev1 "k8s.io/api/core/v1"
26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 | )
28 |
29 | const (
30 | // TrustFlushCommand -
31 | TrustFlushCommand = "keystone-manage trust_flush"
32 | )
33 |
34 | // CronJob func
35 | func CronJob(
36 | instance *keystonev1.KeystoneAPI,
37 | labels map[string]string,
38 | annotations map[string]string,
39 | memcached *memcachedv1.Memcached,
40 | ) *batchv1.CronJob {
41 |
42 | args := []string{"-c", TrustFlushCommand + instance.Spec.TrustFlushArgs}
43 |
44 | envVars := map[string]env.Setter{}
45 | envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS")
46 |
47 | parallelism := int32(1)
48 | completions := int32(1)
49 |
50 | // create Volume and VolumeMounts
51 | keystoneCronJobExtraMounts := []keystonev1.KeystoneExtraMounts{}
52 | volumes := getVolumes(instance, keystoneCronJobExtraMounts, KeystoneCronJobPropagation)
53 | volumeMounts := getCronJobVolumeMounts()
54 |
55 | // add CA cert if defined
56 | if instance.Spec.TLS.CaBundleSecretName != "" {
57 | volumes = append(volumes, instance.Spec.TLS.CreateVolume())
58 | volumeMounts = append(getCronJobVolumeMounts(), instance.Spec.TLS.CreateVolumeMounts(nil)...)
59 | }
60 |
61 | // add MTLS cert if defined
62 | if memcached.GetMemcachedMTLSSecret() != "" {
63 | mtlsVolume := memcached.CreateMTLSVolume()
64 | // Set file permissions to 0440
65 | mtlsVolume.Secret.DefaultMode = func() *int32 { mode := int32(0440); return &mode }()
66 | volumes = append(volumes, mtlsVolume)
67 | volumeMounts = append(volumeMounts, corev1.VolumeMount{
68 | Name: *memcached.Spec.TLS.MTLS.AuthCertSecret.SecretName,
69 | MountPath: "/etc/pki/tls/certs/mtls.crt",
70 | SubPath: tls.CertKey,
71 | ReadOnly: true,
72 | }, corev1.VolumeMount{
73 | Name: *memcached.Spec.TLS.MTLS.AuthCertSecret.SecretName,
74 | MountPath: "/etc/pki/tls/private/mtls.key",
75 | SubPath: tls.PrivateKey,
76 | ReadOnly: true,
77 | }, corev1.VolumeMount{
78 | Name: *memcached.Spec.TLS.MTLS.AuthCertSecret.SecretName,
79 | MountPath: "/etc/pki/tls/certs/mtls-ca.crt",
80 | SubPath: tls.CAKey,
81 | ReadOnly: true,
82 | })
83 | }
84 |
85 | cronjob := &batchv1.CronJob{
86 | ObjectMeta: metav1.ObjectMeta{
87 | Name: ServiceName + "-cron",
88 | Namespace: instance.Namespace,
89 | },
90 | Spec: batchv1.CronJobSpec{
91 | Schedule: instance.Spec.TrustFlushSchedule,
92 | Suspend: &instance.Spec.TrustFlushSuspend,
93 | ConcurrencyPolicy: batchv1.ForbidConcurrent,
94 | JobTemplate: batchv1.JobTemplateSpec{
95 | ObjectMeta: metav1.ObjectMeta{
96 | Annotations: annotations,
97 | Labels: labels,
98 | },
99 | Spec: batchv1.JobSpec{
100 | Parallelism: ¶llelism,
101 | Completions: &completions,
102 | Template: corev1.PodTemplateSpec{
103 | Spec: corev1.PodSpec{
104 | Containers: []corev1.Container{
105 | {
106 | Name: ServiceName + "-cron",
107 | Image: instance.Spec.ContainerImage,
108 | Command: []string{
109 | "/bin/bash",
110 | },
111 | Args: args,
112 | Env: env.MergeEnvs([]corev1.EnvVar{}, envVars),
113 | VolumeMounts: volumeMounts,
114 | SecurityContext: baseSecurityContext(),
115 | },
116 | },
117 | Volumes: volumes,
118 | RestartPolicy: corev1.RestartPolicyNever,
119 | ServiceAccountName: instance.RbacResourceName(),
120 | SecurityContext: &corev1.PodSecurityContext{
121 | FSGroup: func() *int64 { gid := int64(42425); return &gid }(), // keystone group
122 | },
123 | },
124 | },
125 | },
126 | },
127 | },
128 | }
129 | if instance.Spec.NodeSelector != nil {
130 | cronjob.Spec.JobTemplate.Spec.Template.Spec.NodeSelector = *instance.Spec.NodeSelector
131 | }
132 | return cronjob
133 | }
134 |
--------------------------------------------------------------------------------
/test/kuttl/tests/keystone_tls/01-assert.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 1 KeystoneAPI CR
5 | # - Deployment with tls volumes
6 |
7 | apiVersion: keystone.openstack.org/v1beta1
8 | kind: KeystoneAPI
9 | metadata:
10 | name: keystone
11 | spec:
12 | tls:
13 | api:
14 | internal:
15 | secretName: cert-keystone-internal-svc
16 | public:
17 | secretName: cert-keystone-public-svc
18 | caBundleSecretName: combined-ca-bundle
19 | status:
20 | databaseHostname: openstack.keystone-kuttl-tests.svc
21 | readyCount: 1
22 | ---
23 | apiVersion: apps/v1
24 | kind: Deployment
25 | metadata:
26 | name: keystone
27 | spec:
28 | replicas: 1
29 | template:
30 | metadata:
31 | labels:
32 | service: keystone
33 | spec:
34 | containers:
35 | - args:
36 | - -c
37 | - /usr/local/bin/kolla_start
38 | volumeMounts:
39 | - mountPath: /usr/local/bin/container-scripts
40 | name: scripts
41 | readOnly: true
42 | - mountPath: /var/lib/config-data/default
43 | name: config-data
44 | - mountPath: /var/lib/kolla/config_files/config.json
45 | name: config-data
46 | readOnly: true
47 | subPath: keystone-api-config.json
48 | - mountPath: /etc/keystone/fernet-keys
49 | name: fernet-keys
50 | readOnly: true
51 | - mountPath: /etc/keystone/credential-keys
52 | name: credential-keys
53 | readOnly: true
54 | - mountPath: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
55 | name: combined-ca-bundle
56 | readOnly: true
57 | subPath: tls-ca-bundle.pem
58 | - mountPath: /var/lib/config-data/tls/certs/internal.crt
59 | name: internal-tls-certs
60 | readOnly: true
61 | subPath: tls.crt
62 | - mountPath: /var/lib/config-data/tls/private/internal.key
63 | name: internal-tls-certs
64 | readOnly: true
65 | subPath: tls.key
66 | - mountPath: /var/lib/config-data/tls/certs/public.crt
67 | name: public-tls-certs
68 | readOnly: true
69 | subPath: tls.crt
70 | - mountPath: /var/lib/config-data/tls/private/public.key
71 | name: public-tls-certs
72 | readOnly: true
73 | subPath: tls.key
74 | volumes:
75 | - name: scripts
76 | secret:
77 | defaultMode: 493
78 | secretName: keystone-scripts
79 | - name: config-data
80 | secret:
81 | defaultMode: 420
82 | secretName: keystone-config-data
83 | - name: fernet-keys
84 | secret:
85 | defaultMode: 420
86 | items:
87 | - key: FernetKeys0
88 | path: "0"
89 | - key: FernetKeys1
90 | path: "1"
91 | - key: FernetKeys2
92 | path: "2"
93 | - key: FernetKeys3
94 | path: "3"
95 | - key: FernetKeys4
96 | path: "4"
97 | secretName: keystone
98 | - name: credential-keys
99 | secret:
100 | defaultMode: 420
101 | items:
102 | - key: CredentialKeys0
103 | path: "0"
104 | - key: CredentialKeys1
105 | path: "1"
106 | secretName: keystone
107 | - name: combined-ca-bundle
108 | secret:
109 | defaultMode: 292
110 | secretName: combined-ca-bundle
111 | - name: internal-tls-certs
112 | secret:
113 | defaultMode: 256
114 | secretName: cert-keystone-internal-svc
115 | - name: public-tls-certs
116 | secret:
117 | defaultMode: 256
118 | secretName: cert-keystone-public-svc
119 | ---
120 | # the actual addresses of the apiEndpoints are platform specific, so we can't rely on
121 | # kuttl asserts to check them. This short script gathers the addresses and checks that
122 | # the three endpoints are defined and their addresses follow the default pattern
123 | apiVersion: kuttl.dev/v1beta1
124 | kind: TestAssert
125 | commands:
126 | - script: |
127 | # the actual addresses of the apiEndpoints are platform specific, so we can't rely on
128 | # kuttl asserts to check them. This short script gathers the addresses and checks that
129 | # the three endpoints are defined and their addresses follow the default pattern
130 | template='{{.status.apiEndpoints.internal}}{{":"}}{{.status.apiEndpoints.public}}{{"\n"}}'
131 | regex="https:\/\/keystone-internal.$NAMESPACE.*:https:\/\/keystone-public.$NAMESPACE.*"
132 | apiEndpoints=$(oc get -n $NAMESPACE KeystoneAPI keystone -o go-template="$template")
133 | matches=$(echo "$apiEndpoints" | sed -e "s?$regex??")
134 | if [ -z "$matches" ]; then
135 | exit 0
136 | else
137 | echo "Endpoints URLS: $apiEndpoints do not match regex"
138 | exit 1
139 | fi
140 |
--------------------------------------------------------------------------------
/api/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/openstack-k8s-operators/keystone-operator/api
2 |
3 | go 1.24.4
4 |
5 | require (
6 | github.com/go-logr/logr v1.4.3
7 | github.com/google/uuid v1.6.0
8 | github.com/gophercloud/gophercloud/v2 v2.8.0
9 | github.com/onsi/gomega v1.38.2
10 | github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20251205192058-5cfbada0ab96
11 | github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20251122131503-b76943960b6c
12 | github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.1-0.20251122131503-b76943960b6c
13 | github.com/openstack-k8s-operators/lib-common/modules/storage v0.6.1-0.20251122131503-b76943960b6c
14 | github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20251122131503-b76943960b6c
15 | golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
16 | k8s.io/api v0.31.14
17 | k8s.io/apimachinery v0.31.14
18 | k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d
19 | sigs.k8s.io/controller-runtime v0.19.7
20 | )
21 |
22 | require (
23 | github.com/beorn7/perks v1.0.1 // indirect
24 | github.com/cespare/xxhash/v2 v2.3.0 // indirect
25 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
26 | github.com/emicklei/go-restful/v3 v3.12.2 // indirect
27 | github.com/evanphx/json-patch v5.9.11+incompatible // indirect
28 | github.com/evanphx/json-patch/v5 v5.9.11 // indirect
29 | github.com/fsnotify/fsnotify v1.9.0 // indirect
30 | github.com/fxamacker/cbor/v2 v2.9.0 // indirect
31 | github.com/go-openapi/jsonpointer v0.21.1 // indirect
32 | github.com/go-openapi/jsonreference v0.21.0 // indirect
33 | github.com/go-openapi/swag v0.23.1 // indirect
34 | github.com/gogo/protobuf v1.3.2 // indirect
35 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
36 | github.com/golang/protobuf v1.5.4 // indirect
37 | github.com/google/gnostic-models v0.7.0 // indirect
38 | github.com/google/go-cmp v0.7.0 // indirect
39 | github.com/google/gofuzz v1.2.0 // indirect
40 | github.com/imdario/mergo v0.3.16 // indirect
41 | github.com/josharian/intern v1.0.0 // indirect
42 | github.com/json-iterator/go v1.1.12 // indirect
43 | github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.7 // indirect
44 | github.com/mailru/easyjson v0.9.0 // indirect
45 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
46 | github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
47 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
48 | github.com/openshift/api v3.9.0+incompatible // indirect
49 | github.com/pkg/errors v0.9.1 // indirect
50 | github.com/prometheus/client_golang v1.22.0 // indirect
51 | github.com/prometheus/client_model v0.6.2 // indirect
52 | github.com/prometheus/common v0.65.0 // indirect
53 | github.com/prometheus/procfs v0.16.1 // indirect
54 | github.com/spf13/pflag v1.0.7 // indirect
55 | github.com/x448/float16 v0.8.4 // indirect
56 | go.yaml.in/yaml/v2 v2.4.2 // indirect
57 | go.yaml.in/yaml/v3 v3.0.4 // indirect
58 | golang.org/x/net v0.43.0 // indirect
59 | golang.org/x/oauth2 v0.30.0 // indirect
60 | golang.org/x/sys v0.36.0 // indirect
61 | golang.org/x/term v0.35.0 // indirect
62 | golang.org/x/text v0.29.0 // indirect
63 | golang.org/x/time v0.12.0 // indirect
64 | gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
65 | google.golang.org/protobuf v1.36.7 // indirect
66 | gopkg.in/inf.v0 v0.9.1 // indirect
67 | gopkg.in/yaml.v3 v3.0.1 // indirect
68 | k8s.io/apiextensions-apiserver v0.33.2 // indirect
69 | k8s.io/client-go v0.31.14 // indirect
70 | k8s.io/klog/v2 v2.130.1 // indirect
71 | k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f // indirect
72 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
73 | sigs.k8s.io/randfill v1.0.0 // indirect
74 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
75 | sigs.k8s.io/yaml v1.6.0 // indirect
76 | )
77 |
78 | // mschuppert: map to latest commit from release-4.18 tag
79 | // must consistent within modules and service operators
80 | replace github.com/openshift/api => github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e //allow-merging
81 |
82 | // pin these to avoid later versions pulled by rabbitmq
83 | replace k8s.io/apimachinery => k8s.io/apimachinery v0.31.14 //allow-merging
84 |
85 | replace k8s.io/api => k8s.io/api v0.31.14 //allow-merging
86 |
87 | replace k8s.io/apiserver => k8s.io/apiserver v0.31.14 //allow-merging
88 |
89 | replace k8s.io/client-go => k8s.io/client-go v0.31.14 //allow-merging
90 |
91 | replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.14 //allow-merging
92 |
93 | replace k8s.io/cli-runtime => k8s.io/cli-runtime v0.31.14 //allow-merging
94 |
95 | replace k8s.io/code-generator => k8s.io/code-generator v0.31.14 //allow-merging
96 |
97 | replace k8s.io/component-base => k8s.io/component-base v0.31.14 //allow-merging
98 |
99 | replace k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20250627150254-e9823e99808e //allow-merging
100 |
--------------------------------------------------------------------------------
/internal/keystone/volumes.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package keystone
17 |
18 | import (
19 | "fmt"
20 | keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
21 | "github.com/openstack-k8s-operators/lib-common/modules/storage"
22 | corev1 "k8s.io/api/core/v1"
23 | )
24 |
25 | // getVolumes - service volumes
26 | func getVolumes(
27 | instance *keystonev1.KeystoneAPI,
28 | extraVol []keystonev1.KeystoneExtraMounts,
29 | svc []storage.PropagationType,
30 | ) []corev1.Volume {
31 | name := instance.Name
32 | var scriptsVolumeDefaultMode int32 = 0755
33 | var config0640AccessMode int32 = 0644
34 |
35 | fernetKeys := []corev1.KeyToPath{}
36 | numberKeys := int(*instance.Spec.FernetMaxActiveKeys)
37 |
38 | for i := range numberKeys {
39 | fernetKeys = append(
40 | fernetKeys,
41 | corev1.KeyToPath{
42 | Key: fmt.Sprintf("FernetKeys%d", i),
43 | Path: fmt.Sprintf("%d", i),
44 | },
45 | )
46 | }
47 |
48 | res := []corev1.Volume{
49 | {
50 | Name: "scripts",
51 | VolumeSource: corev1.VolumeSource{
52 | Secret: &corev1.SecretVolumeSource{
53 | DefaultMode: &scriptsVolumeDefaultMode,
54 | SecretName: name + "-scripts",
55 | },
56 | },
57 | },
58 | {
59 | Name: "config-data",
60 | VolumeSource: corev1.VolumeSource{
61 | Secret: &corev1.SecretVolumeSource{
62 | DefaultMode: &config0640AccessMode,
63 | SecretName: name + "-config-data",
64 | },
65 | },
66 | },
67 | {
68 | Name: "fernet-keys",
69 | VolumeSource: corev1.VolumeSource{
70 | Secret: &corev1.SecretVolumeSource{
71 | SecretName: ServiceName,
72 | Items: fernetKeys,
73 | },
74 | },
75 | },
76 | {
77 | Name: "credential-keys",
78 | VolumeSource: corev1.VolumeSource{
79 | Secret: &corev1.SecretVolumeSource{
80 | SecretName: ServiceName,
81 | Items: []corev1.KeyToPath{
82 | {
83 | Key: "CredentialKeys0",
84 | Path: "0",
85 | },
86 | {
87 | Key: "CredentialKeys1",
88 | Path: "1",
89 | },
90 | },
91 | },
92 | },
93 | },
94 | }
95 | for _, exv := range extraVol {
96 | for _, vol := range exv.Propagate(svc) {
97 | for _, v := range vol.Volumes {
98 | volumeSource, _ := v.ToCoreVolumeSource()
99 | convertedVolume := corev1.Volume{
100 | Name: v.Name,
101 | VolumeSource: *volumeSource,
102 | }
103 | res = append(res, convertedVolume)
104 | }
105 | }
106 | }
107 | return res
108 | }
109 |
110 | // getVolumeMounts - general VolumeMounts
111 | func getVolumeMounts(
112 | extraVol []keystonev1.KeystoneExtraMounts,
113 | svc []storage.PropagationType,
114 | ) []corev1.VolumeMount {
115 | vm := []corev1.VolumeMount{
116 | {
117 | Name: "scripts",
118 | MountPath: "/usr/local/bin/container-scripts",
119 | ReadOnly: true,
120 | },
121 | {
122 | Name: "config-data",
123 | MountPath: "/var/lib/config-data/default",
124 | ReadOnly: false,
125 | },
126 | {
127 | Name: "config-data",
128 | MountPath: "/var/lib/kolla/config_files/config.json",
129 | SubPath: "keystone-api-config.json",
130 | ReadOnly: true,
131 | },
132 | {
133 | MountPath: "/etc/keystone/fernet-keys",
134 | ReadOnly: true,
135 | Name: "fernet-keys",
136 | },
137 | {
138 | MountPath: "/etc/keystone/credential-keys",
139 | ReadOnly: true,
140 | Name: "credential-keys",
141 | },
142 | }
143 | for _, exv := range extraVol {
144 | for _, vol := range exv.Propagate(svc) {
145 | vm = append(vm, vol.Mounts...)
146 | }
147 | }
148 | return vm
149 | }
150 |
151 | // getCronJobVolumeMounts - cronjob volumeMounts
152 | func getCronJobVolumeMounts() []corev1.VolumeMount {
153 | return []corev1.VolumeMount{
154 | {
155 | Name: "config-data",
156 | MountPath: "/etc/keystone/keystone.conf",
157 | SubPath: "keystone.conf",
158 | ReadOnly: true,
159 | },
160 | {
161 | Name: "config-data",
162 | MountPath: "/etc/my.cnf",
163 | SubPath: "my.cnf",
164 | ReadOnly: true,
165 | },
166 | {
167 | Name: "fernet-keys",
168 | MountPath: "/etc/keystone/fernet-keys",
169 | ReadOnly: true,
170 | },
171 | }
172 | }
173 |
174 | // getDBSyncVolumeMounts - cronjob volumeMounts
175 | func getDBSyncVolumeMounts() []corev1.VolumeMount {
176 | return []corev1.VolumeMount{
177 | {
178 | Name: "config-data",
179 | MountPath: "/etc/keystone/keystone.conf",
180 | SubPath: "keystone.conf",
181 | ReadOnly: true,
182 | },
183 | {
184 | Name: "config-data",
185 | MountPath: "/etc/my.cnf",
186 | SubPath: "my.cnf",
187 | ReadOnly: true,
188 | },
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/test/kuttl/common/tls_cert_keystone-public-svc.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Cert created with:
3 | #
4 | # apiVersion: cert-manager.io/v1
5 | # kind: Certificate
6 | # metadata:
7 | # name: kuttl-keystone-public-svc
8 | # namespace: openstack
9 | # spec:
10 | # dnsNames:
11 | # - keystone-public.openstack.svc
12 | # duration: 87600h0m0s
13 | # issuerRef:
14 | # group: cert-manager.io
15 | # kind: Issuer
16 | # name: rootca-kuttl-public
17 | # secretName: cert-keystone-public-svc
18 | # secretTemplate: {}
19 | # usages:
20 | # - key encipherment
21 | # - digital signature
22 | # - server auth
23 |
24 | apiVersion: v1
25 | data:
26 | ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlekNDQVNLZ0F3SUJBZ0lRTkhER1lzQnM3OThpYkREN3EvbzJsakFLQmdncWhrak9QUVFEQWpBZU1Sd3cKR2dZRFZRUURFeE55YjI5MFkyRXRhM1YwZEd3dGNIVmliR2xqTUI0WERUSTBNREV4TlRFd01UVXpObG9YRFRNMApNREV4TWpFd01UVXpObG93SGpFY01Cb0dBMVVFQXhNVGNtOXZkR05oTFd0MWRIUnNMWEIxWW14cFl6QlpNQk1HCkJ5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRDc4YXZYcWhyaEM1dzhzOVdrZDRJcGJlRXUwM0NSK1hYVWQKa0R6T1J5eGE5d2NjSWREaXZiR0pqSkZaVFRjVm1ianExQk1Zc2pyMTJVSUU1RVQzVmxxalFqQkFNQTRHQTFVZApEd0VCL3dRRUF3SUNwREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlRLSml6V1VKOWVVS2kxCmRzMGxyNmM2c0Q3RUJEQUtCZ2dxaGtqT1BRUURBZ05IQURCRUFpQklad1lxNjFCcU1KYUI2VWNGb1JzeGVjd0gKNXovek1PZHJPeWUwbU5pOEpnSWdRTEI0d0RLcnBmOXRYMmxvTSswdVRvcEFEU1lJbnJjZlZ1NEZCdVlVM0lnPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
27 | tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNiVENDQWhPZ0F3SUJBZ0lSQUtacXlMbUhLNC9VRTZmMi9LNWxiQnN3Q2dZSUtvWkl6ajBFQXdJd0hqRWMKTUJvR0ExVUVBeE1UY205dmRHTmhMV3QxZEhSc0xYQjFZbXhwWXpBZUZ3MHlOREF4TVRVeE1ESXdOVFJhRncwegpOREF4TVRJeE1ESXdOVFJhTUFBd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUJBUUMxCjhDcFJRVG1abHNzSUlmZ2hIK2ltUUtFMFdZVlJOeS8vMVM0aDVtV2tBcUZiVkhoUmptbFJ2cCtQUWpKOU16TDUKMXpXdmYxandEQ2pzYUxvL2FwSW9OSXJIcjN4TTRoYWl0emU0RjFwZzNoL3MvblExNWN5Q2U5dHdHR0RuWEllMwo2djBuNE9LNnAwSWJjcVk2Q1RBMTBwcGJZa3V6bzdVRkx6ZWxsc1ZhRlhzZ21JWDg4bTRXNmNBTi84cjJPWUI3Ck9HM0ZNOXAxSUFxT0hyT21EelFlTldqOUVjQy9TSCs5MGg4c1FyY1pvMWtWa1g1b2tpSUhDZjRlc2o3Q08rTGgKR3lsTmZyRzl6QTlPM0c3QVNDWVdPVWwyZTBhNHhZbE9QMmI4ejFEV3NIMTBVYXVsZHlRQXNtbkhtaW1VNzBmKwpEazZkQ1hXVHN4cGZ2cXphOVR4YkFnTUJBQUdqZ1lRd2dZRXdEZ1lEVlIwUEFRSC9CQVFEQWdXZ01CTUdBMVVkCkpRUU1NQW9HQ0NzR0FRVUZCd01CTUF3R0ExVWRFd0VCL3dRQ01BQXdId1lEVlIwakJCZ3dGb0FVeWlZczFsQ2YKWGxDb3RYYk5KYStuT3JBK3hBUXdLd1lEVlIwUkFRSC9CQ0V3SDRJZGEyVjVjM1J2Ym1VdGNIVmliR2xqTG05dwpaVzV6ZEdGamF5NXpkbU13Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnTzAzT2JmNm9uV2RiZG4xa282OVpuTFhMCmtQSHFYU3VRNlcxTDFvY3NDR3NDSVFEakEyVm9pWVdYN0hzSjVGNkZYV3FsZnl0RmduVVgvTmhvT1lIVnB2TWQKSGc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
28 | tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdGZBcVVVRTVtWmJMQ0NINElSL29wa0NoTkZtRlVUY3YvOVV1SWVabHBBS2hXMVI0ClVZNXBVYjZmajBJeWZUTXkrZGMxcjM5WThBd283R2k2UDJxU0tEU0t4Njk4VE9JV29yYzN1QmRhWU40ZjdQNTAKTmVYTWdudmJjQmhnNTF5SHQrcjlKK0RpdXFkQ0czS21PZ2t3TmRLYVcySkxzNk8xQlM4M3BaYkZXaFY3SUppRgovUEp1RnVuQURmL0s5am1BZXpodHhUUGFkU0FLamg2enBnODBIalZvL1JIQXYwaC92ZElmTEVLM0dhTlpGWkYrCmFKSWlCd24rSHJJK3dqdmk0UnNwVFg2eHZjd1BUdHh1d0VnbUZqbEpkbnRHdU1XSlRqOW0vTTlRMXJCOWRGR3IKcFhja0FMSnB4NW9wbE85SC9nNU9uUWwxazdNYVg3NnMydlU4V3dJREFRQUJBb0lCQUd0eVdvdUNLYkk3Qzh6Ugp3dWhOSCtpUFlxUzMrYlB0RTd2UytsdXE1WHZtMGNST0xvQjd5bGNzYks3K09UTVhlWk56TlpGZmMvYlFONXJtCmZwZlZLRnYySzcraU01WjBMMG9KU2k2K0cvSDVQSUdLQkxlUDd5ZGdYa2ZsSGRXRkgrSE9OWlBIakI4UGlFc04KZW4zcnp6ejZFNDdFamxDWTdkOFI4NXNuWDRYREN2bG1CQnhvcnpqVERuK1dTWWpKS09SSk5zY3oxQXFYR1VjVwpQaHRNYkwybC8zN2hPbTA4SjRRWXowTWduOWE5VUFXLzFNS2lXbHVpc1NHNG9YaFNPS1hkdk1IS3VxS09sUDJzCk9xWjBlR3JBNmpKdWlmZVY2Q2NIU2p3VUgwdHpiMmdZQVM2cm5RQlREbFkxR1I4Skx0YWhWREtqdUwyV0hjclkKbHhCOGZBRUNnWUVBMWpxc01weFo5cG9LNkpmRDZzVTU0ZUU1UWlNYlB0MERRZjlYU1J6NW5zQXlraHdKWEZDVwpKWTNiU3BhcGREeEgzakpDQ0VzN3NSWUhUeDRzNVJQSldtWC9oZTFwVEs4TDVlaFV2TmVudG5nVTB3aE4rZVEzCjl4Sk1VbHVYdGkvU0FpNi9jQk5HY1ZjQjFGRStmVzY0VDhqYVVQakRrL0Z2dFRXOXkzZnNVZnNDZ1lFQTJXbXMKYStuZ2RaS24rVTlCMlFCTXB0K0RLL0txNVF6RW1qZDVMSjJMb3FLUjhGbjlpVVZoUVljUEpobDJVV3VjTTl0RQp0QUlYdEY0anVUejlqUUNMMGQ5a09DeCswdTBKUFZJejdlVmFFVGs1enF0azRsTnhVUkJhQ3pCUEJkdjZJd3BDCkR4UXJWRXBXYlMra1JvYTNKSElOdHdjUWt2Nk50N1JIajd1WEVTRUNnWUJsREozbTdZc2Q0QUZmUHg4Qm9YQXgKRkt5T2ZzSytQei9uSkl0R2lHMVNMWFJ0S041ZGRnR3N5eUh5SitqY1ZBYk9UMFNJWnZ4TUJva0NEOGk3Y1Q3Ygo3aHErVUlNSDBkVzU1NEg0NVh4TmZJek9FaSs5dktHTllFc3gyZFJROG5PTDVnTVUyWEt6eVllcVgzd3JiRXR5CkR0cXpzUE9IMkMySiswU0FNaHY5ZXdLQmdRQ2szeWs5TUwvaUNWUk9rTmNybTdtRk5xeS9rQ2dleU43eTRDeUoKTS9RbllrZHYwSjZmRWJrZU96QzJ3TXBrRmtuL1hVR3RqSVN6YUV5STlnS0ZnaXVGL1hWL3orWmhTQllncFl6eAoxR0xIK3ZDbWxIMU4wTjkzRFFKcng3ZTFoc3NhOVhXQS85ZVg5VU96UzFTMWt3V2hvc2haeXdhN29rU1FVaXVPCmlVQ1hZUUtCZ1FEUVZUVHc3WUY3QzNTVmg5OWRObUdTaHV2LzZ2aTJmNDlOMklGMURNQ1haaEpoOUVZck9TV2kKY05oakxGRFhmdzVlZlFURWU3Ykx5bTJGVDd0YnZFSm5USHFyakVuUDRUWExqZnczL3RiQ3RxWVNZRlRqdThFUApadHVwd21ZWjhFVU1pSnVHS2l2SExmSjk2dy8xR21BOHVCZUVtV05YRW9FUU1ySmxuM3g5d3c9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
29 | kind: Secret
30 | metadata:
31 | annotations:
32 | cert-manager.io/alt-names: keystone-public.openstack.svc
33 | cert-manager.io/certificate-name: keystone-public-svc
34 | cert-manager.io/common-name: ""
35 | cert-manager.io/ip-sans: ""
36 | cert-manager.io/issuer-group: cert-manager.io
37 | cert-manager.io/issuer-kind: Issuer
38 | cert-manager.io/issuer-name: rootca-public
39 | cert-manager.io/uri-sans: ""
40 | labels:
41 | controller.cert-manager.io/fao: "true"
42 | name: cert-keystone-public-svc
43 | type: kubernetes.io/tls
44 |
--------------------------------------------------------------------------------
/test/kuttl/common/tls_cert_keystone-internal-svc.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Cert created with:
3 | #
4 | # apiVersion: cert-manager.io/v1
5 | # kind: Certificate
6 | # metadata:
7 | # name: keystone-kuttl-internal-svc
8 | # namespace: openstack
9 | # spec:
10 | # dnsNames:
11 | # - keystone-internal.openstack.svc
12 | # duration: 87600h0m0s
13 | # issuerRef:
14 | # group: cert-manager.io
15 | # kind: Issuer
16 | # name: rootca-kuttl-internal
17 | # secretName: cert-keystone-internal-svc
18 | # secretTemplate: {}
19 | # usages:
20 | # - key encipherment
21 | # - digital signature
22 | # - server auth
23 | #
24 |
25 | apiVersion: v1
26 | data:
27 | ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmekNDQVNhZ0F3SUJBZ0lRUWxlcTNZcDBtU2kwVDNiTm03Q29UVEFLQmdncWhrak9QUVFEQWpBZ01SNHcKSEFZRFZRUURFeFZ5YjI5MFkyRXRhM1YwZEd3dGFXNTBaWEp1WVd3d0hoY05NalF3TVRFMU1URTBOelUwV2hjTgpNelF3TVRFeU1URTBOelUwV2pBZ01SNHdIQVlEVlFRREV4VnliMjkwWTJFdGEzVjBkR3d0YVc1MFpYSnVZV3d3CldUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTRk9rNHJPUldVUGhoTjUrK09EN1I2MW5Gb1lBY0QKenpvUS91SW93NktjeGhwRWNQTDFxb3ZZUGxUYUJabEh3c2FpNE50VHA4aDA1RHVRSGZKOE9JNXFvMEl3UURBTwpCZ05WSFE4QkFmOEVCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXE3TGtFSk1TCm1MOVpKWjBSOUluKzZkclhycEl3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnVlN1K00ydnZ3QlF3eTJHMVlhdkkKQld2RGtSNlRla0I5U0VqdzJIblRSMWtDSUZSNFNkWGFPQkFGWjVHa2RLWCtSY2IzaDFIZm52eFJEVW96bTl2agphenp3Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
28 | tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNjRENDQWhhZ0F3SUJBZ0lRZG5oL3ZsTVFBZ1lEd3BpYkRDbXVhekFLQmdncWhrak9QUVFEQWpBZ01SNHcKSEFZRFZRUURFeFZ5YjI5MFkyRXRhM1YwZEd3dGFXNTBaWEp1WVd3d0hoY05NalF3TVRFMk1UQTFNekUxV2hjTgpNelF3TVRFek1UQTFNekUxV2pBQU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBCndidzhpWGZEd1RCYlRCTStlTVFPSktCSzVIeGV0bVB2NG5yWkVFc3V2QjRzbTZDNW1ZdUlmRGM2Y2xaV1FjUDEKbG1EMWNkNlYvTS9ydG4vNXFremZiSlJ0SlFiNFgwQzdJWFMxTDZJN3pWMDlORnhpZnZrRDR0Vy9MbEVTTHh2OAp0amZuYzZlbzhlZjZMYkxjby84Wjhkd245NVZSYmhGMWFYWXVyYlBpWVBoUDZTa29Ta20wVWpZK1NLUWhJMVoxCkMrUVBLWlZEQ1EvQkl3UmIrdUVZWCtUdWdmOVNTbkQrbEpLNFlMcVJpSW5LbjNVWTRzSFpTR3lmR0RBck5mSlQKOHUxcVI3TXAwWWRUL2tJb3pnbXp6aC9Jd3ZRRG16V05sZ0M4TmtRcmdmOXBrR2lyZFlLRUpQRmlFZjFTZXp3YwpMOG1Xc0hRYVA2bE95ZUVicW9nUUtRSURBUUFCbzRHR01JR0RNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WCkhTVUVEREFLQmdnckJnRUZCUWNEQVRBTUJnTlZIUk1CQWY4RUFqQUFNQjhHQTFVZEl3UVlNQmFBRkt1eTVCQ1QKRXBpL1dTV2RFZlNKL3VuYTE2NlNNQzBHQTFVZEVRRUIvd1FqTUNHQ0gydGxlWE4wYjI1bExXbHVkR1Z5Ym1GcwpMbTl3Wlc1emRHRmpheTV6ZG1Nd0NnWUlLb1pJemowRUF3SURTQUF3UlFJZ01Wa0w0dk8vbnJORXl2Z2pYdVFPCmxncU5iRVdQOEw4R3RYbFlPaWcxT2hvQ0lRRGUxTzgvaHdxYlpySWFzSkZzZzlMM2FzcysrWVc5bXdBUzVmWncKOStqRVZ3PT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
29 | tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBd2J3OGlYZkR3VEJiVEJNK2VNUU9KS0JLNUh4ZXRtUHY0bnJaRUVzdXZCNHNtNkM1Cm1ZdUlmRGM2Y2xaV1FjUDFsbUQxY2Q2Vi9NL3J0bi81cWt6ZmJKUnRKUWI0WDBDN0lYUzFMNkk3elYwOU5GeGkKZnZrRDR0Vy9MbEVTTHh2OHRqZm5jNmVvOGVmNkxiTGNvLzhaOGR3bjk1VlJiaEYxYVhZdXJiUGlZUGhQNlNrbwpTa20wVWpZK1NLUWhJMVoxQytRUEtaVkRDUS9CSXdSYit1RVlYK1R1Z2Y5U1NuRCtsSks0WUxxUmlJbktuM1VZCjRzSFpTR3lmR0RBck5mSlQ4dTFxUjdNcDBZZFQva0lvemdtenpoL0l3dlFEbXpXTmxnQzhOa1FyZ2Y5cGtHaXIKZFlLRUpQRmlFZjFTZXp3Y0w4bVdzSFFhUDZsT3llRWJxb2dRS1FJREFRQUJBb0lCQVFDQ0hweUdNK05OY040UQo1V2Z6RXJMeEZKdlloRlBVcXFDbWU1NG9uR1ppUU4zekZPc3pYbzBuNkt3ZnVTOHI4cUtUQXNJM1hhbGRhSVRIClNZTDFSN1pVSmdoOGN3Y0VhdVNFbnU5R2MrODRpbVFlTStLUHAwNWQzdlFOOXJPQTRvcEVGSjRtaHJnbzZZYVYKaE9rK1dJc2piNXVFWlV5UTRiYjdRejRzdW9IVVlDYXFkVGlqU1lYQzNOd092YUlwa3pTNEo0cU5CUlhyYnNWSwowaGt4ZFNIY1hKNEREN0hybktpcEsxT2xUbUVObVZYbmlaNnRPcWc2eUNFeXFteWN0UnlUVTZRRzVPbVM2clJVCm82Z25EclA1TlgwRUhuakY3b1lka0JVbGJxWk96UHVGbG5CdUVKOFpTUEtOZHA5ejhuS3lqbGJiL0YxWGRDdEkKZERhVUhmREZBb0dCQVBFZkZZbDhPb2VhU21oSElKcGpxd3RCanYrRjFuOXNJbHNuZWNyQ1JmN243RW53d1hXaQpReStXQ3l6aDJGRVVad1dod2RQeXFJT3NVaG1vaXBIQmN3NlVUaW9xalM4SlpvSDlURFBQUEd5OXIwMHZwRkNuCnFkdjNXMkhWVytRckMrWk1nc2ZKdUlTTnFtbFdFeHpCNFBJQWRHQTdKVzFMY0ZCcG1Zd25DdXZyQW9HQkFNMncKbS94cVRhMmgySjFnNUI0elE5UnBhM280SEoyL2pTaHEzNW9heVNGNWJDYWtnWGRxek0yU0FwQ0x4dzlvY3doRAp3WWRaMWliaHl6b1dDQVZZZ0RlaXViUi96ZTN3Nzk4NktScUNmNnptMk5HOEoxODVDZDdKSjBiaTZBTTgvalpTCnFqWkJIK0FqanF2aFFJM0FMMEdzNlFvc2Z3L3hOL2k1cG00UWM5TTdBb0dBZjZCbFpQVmxnWnN3WVV1c3ZTdWUKUUlIOTc5Qm12ZUY5dWVRR09rVmtpVTAzSzlnTWZuaFp1WmxnNXV2UDlQS29xVGw2Zi9aRUxoWUxDdHZFSk94UgpPMWxTbWswVmw5MFE3aU1scjVLMHVCWWE4TzhUdVVGVnprRjZsQ2s3ejJUZGtwUFM4VzhiaE1YN2VtLytBODIzCmhFQ3JXTGhWMGlrSkZQY2dPQ2YrUnVzQ2dZQTYvcld1cnhxNmUxb3l3WENNVE8zZWhhSUMrd2NTSTdlcjZRTmIKSXVXZlNVRkEwQndtRVNiT3ExczY5Q3hTK2dWTVVJcTRkSWJjdmhSWkE2cW5SZHY0bVI2a2E2ZTM0RXdjZllUKwppb0Z1S1FQMUcvODY2NVF1SndteDVqRGZoT1h3MU1MbkxzU2l0L0FhMGs5K21LbTFMNC9qa0NHZGcvVW16TEMwCmp0bDVzd0tCZ0VPTVI3ODVLT2hyNXFoWmE2b0MvU25JeEptS1FxTWdXU0NGV1pGMDZrVlRnSmthb1hwUEl0bUIKOUZGbE1nTTJSeC91S2V3YTNDSTdQK240ek1uYSswTmhDL0RwNkMxVFVsVWlrcnJYQ3I5a1NPR2dXaEFISDljTwozRENvdkhOcE1PaG51dnhoMlpDeTdYbjFJeGgxWXdlYnVobFZzeTFvR0tDQ0lJb00rOVg1Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
30 | kind: Secret
31 | metadata:
32 | annotations:
33 | cert-manager.io/alt-names: keystone-internal.openstack.svc
34 | cert-manager.io/certificate-name: keystone-internal-svc
35 | cert-manager.io/common-name: ""
36 | cert-manager.io/ip-sans: ""
37 | cert-manager.io/issuer-group: cert-manager.io
38 | cert-manager.io/issuer-kind: Issuer
39 | cert-manager.io/issuer-name: rootca-internal
40 | cert-manager.io/uri-sans: ""
41 | labels:
42 | controller.cert-manager.io/fao: "true"
43 | name: cert-keystone-internal-svc
44 | type: kubernetes.io/tls
45 |
--------------------------------------------------------------------------------
/hack/run_with_local_webhook.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -ex
3 |
4 | # Define a cleanup function
5 | cleanup() {
6 | echo "Caught signal, cleaning up local webhooks..."
7 | ./hack/clean_local_webhook.sh
8 | exit 0
9 | }
10 |
11 | # Set trap to catch SIGINT and SIGTERM
12 | trap cleanup SIGINT SIGTERM
13 |
14 | TMPDIR=${TMPDIR:-"/tmp/k8s-webhook-server/serving-certs"}
15 | SKIP_CERT=${SKIP_CERT:-false}
16 | CRC_IP=${CRC_IP:-$(/sbin/ip -o -4 addr list crc | awk '{print $4}' | cut -d/ -f1)}
17 | FIREWALL_ZONE=${FIREWALL_ZONE:-"libvirt"}
18 | WEBHOOK_PORT=${WEBHOOK_PORT:-${WEBHOOK_PORT}}
19 |
20 | #Open ${WEBHOOK_PORT}
21 | sudo firewall-cmd --zone=${FIREWALL_ZONE} --add-port=${WEBHOOK_PORT}/tcp
22 | sudo firewall-cmd --runtime-to-permanent
23 |
24 | # Generate the certs and the ca bundle
25 | if [ "$SKIP_CERT" = false ] ; then
26 | mkdir -p ${TMPDIR}
27 | rm -rf ${TMPDIR}/* || true
28 |
29 | openssl req -newkey rsa:2048 -days 3650 -nodes -x509 \
30 | -subj "/CN=${HOSTNAME}" \
31 | -addext "subjectAltName = IP:${CRC_IP}" \
32 | -keyout ${TMPDIR}/tls.key \
33 | -out ${TMPDIR}/tls.crt
34 |
35 | cat ${TMPDIR}/tls.crt ${TMPDIR}/tls.key | base64 -w 0 > ${TMPDIR}/bundle.pem
36 |
37 | fi
38 |
39 | CA_BUNDLE=`cat ${TMPDIR}/bundle.pem`
40 |
41 | # Patch the webhook(s)
42 | cat >> ${TMPDIR}/patch_webhook_configurations.yaml < "${CSV_FILE}"
114 |
115 | printf \
116 | "\n\tNow patching operator CSV to remove its OLM deployment and associated webhooks.
117 | The original OLM version of the operator's CSV has been copied to %s. To restore it, use:
118 | oc patch -n openstack-operators %s --type=merge --patch-file=%s\n\n" "${CSV_FILE}" "${CSV_NAME}" "${CSV_FILE}"
119 | fi
120 |
121 | oc patch "${CSV_NAME}" -n openstack-operators --type=json -p="[{'op': 'replace', 'path': '/spec/install/spec/deployments/0/spec/replicas', 'value': 0}]"
122 | oc patch "${CSV_NAME}" -n openstack-operators --type=json -p="[{'op': 'replace', 'path': '/spec/webhookdefinitions', 'value': []}]"
123 | else
124 | # Handle operator deployed by Openstack Initialization resource
125 | CSV_NAME="$(oc get csv -n openstack-operators -l operators.coreos.com/openstack-operator.openstack-operators -o name)"
126 |
127 | printf \
128 | "\n\tNow patching openstack operator CSV to scale down deployment resource.
129 | To restore it, use:
130 | oc patch "${CSV_NAME}" -n openstack-operators --type=json -p=\"[{'op': 'replace', 'path': '/spec/install/spec/deployments/0/spec/replicas', 'value': 1}]\""
131 |
132 | oc patch "${CSV_NAME}" -n openstack-operators --type=json -p="[{'op': 'replace', 'path': '/spec/install/spec/deployments/0/spec/replicas', 'value': 0}]"
133 | oc scale --replicas=0 -n openstack-operators deploy/keystone-operator-controller-manager
134 | fi
135 |
136 | go run ./cmd/main.go -metrics-bind-address ":${METRICS_PORT}" -health-probe-bind-address ":${HEALTH_PORT}" -pprof-bind-address ":${PPROF_PORT}" -webhook-bind-address "${WEBHOOK_PORT}"
137 |
--------------------------------------------------------------------------------
/internal/keystone/bootstrap.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | // Package keystone contains keystone service bootstrap functionality.
17 | package keystone
18 |
19 | import (
20 | memcachedv1 "github.com/openstack-k8s-operators/infra-operator/apis/memcached/v1beta1"
21 | keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
22 |
23 | "github.com/openstack-k8s-operators/lib-common/modules/common/env"
24 | "github.com/openstack-k8s-operators/lib-common/modules/common/tls"
25 |
26 | batchv1 "k8s.io/api/batch/v1"
27 | corev1 "k8s.io/api/core/v1"
28 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 | )
30 |
31 | const (
32 | // BootstrapCommand -
33 | BootstrapCommand = "/usr/local/bin/kolla_set_configs && keystone-manage bootstrap"
34 | )
35 |
36 | // BootstrapJob func
37 | func BootstrapJob(
38 | instance *keystonev1.KeystoneAPI,
39 | labels map[string]string,
40 | annotations map[string]string,
41 | endpoints map[string]string,
42 | memcached *memcachedv1.Memcached,
43 | ) *batchv1.Job {
44 | runAsUser := int64(0)
45 |
46 | args := []string{"-c", BootstrapCommand}
47 |
48 | envVars := map[string]env.Setter{}
49 | envVars["KOLLA_CONFIG_STRATEGY"] = env.SetValue("COPY_ALWAYS")
50 | envVars["KOLLA_BOOTSTRAP"] = env.SetValue("true")
51 | envVars["OS_BOOTSTRAP_USERNAME"] = env.SetValue(instance.Spec.AdminUser)
52 | envVars["OS_BOOTSTRAP_PROJECT_NAME"] = env.SetValue(instance.Spec.AdminProject)
53 | envVars["OS_BOOTSTRAP_SERVICE_NAME"] = env.SetValue(ServiceName)
54 | envVars["OS_BOOTSTRAP_REGION_ID"] = env.SetValue(instance.Spec.Region)
55 |
56 | if _, ok := endpoints["admin"]; ok {
57 | envVars["OS_BOOTSTRAP_ADMIN_URL"] = env.SetValue(endpoints["admin"])
58 | }
59 | if _, ok := endpoints["internal"]; ok {
60 | envVars["OS_BOOTSTRAP_INTERNAL_URL"] = env.SetValue(endpoints["internal"])
61 | }
62 | if _, ok := endpoints["public"]; ok {
63 | envVars["OS_BOOTSTRAP_PUBLIC_URL"] = env.SetValue(endpoints["public"])
64 | }
65 |
66 | // create Volume and VolumeMounts
67 | bootstrapExtraMounts := []keystonev1.KeystoneExtraMounts{}
68 | volumes := getVolumes(instance, bootstrapExtraMounts, BootstrapPropagation)
69 | volumeMounts := getVolumeMounts(bootstrapExtraMounts, BootstrapPropagation)
70 |
71 | // add CA cert if defined
72 | if instance.Spec.TLS.CaBundleSecretName != "" {
73 | volumes = append(volumes, instance.Spec.TLS.CreateVolume())
74 | volumeMounts = append(volumeMounts, instance.Spec.TLS.CreateVolumeMounts(nil)...)
75 | }
76 |
77 | // add MTLS cert if defined
78 | if memcached.GetMemcachedMTLSSecret() != "" {
79 | volumes = append(volumes, memcached.CreateMTLSVolume())
80 | volumeMounts = append(volumeMounts, corev1.VolumeMount{
81 | Name: *memcached.Spec.TLS.MTLS.AuthCertSecret.SecretName,
82 | MountPath: "/etc/pki/tls/certs/mtls.crt",
83 | SubPath: tls.CertKey,
84 | ReadOnly: true,
85 | }, corev1.VolumeMount{
86 | Name: *memcached.Spec.TLS.MTLS.AuthCertSecret.SecretName,
87 | MountPath: "/etc/pki/tls/private/mtls.key",
88 | SubPath: tls.PrivateKey,
89 | ReadOnly: true,
90 | }, corev1.VolumeMount{
91 | Name: *memcached.Spec.TLS.MTLS.AuthCertSecret.SecretName,
92 | MountPath: "/etc/pki/tls/certs/mtls-ca.crt",
93 | SubPath: tls.CAKey,
94 | ReadOnly: true,
95 | })
96 | }
97 |
98 | job := &batchv1.Job{
99 | ObjectMeta: metav1.ObjectMeta{
100 | Name: ServiceName + "-bootstrap",
101 | Namespace: instance.Namespace,
102 | Labels: labels,
103 | },
104 | Spec: batchv1.JobSpec{
105 | Template: corev1.PodTemplateSpec{
106 | ObjectMeta: metav1.ObjectMeta{
107 | Annotations: annotations,
108 | },
109 | Spec: corev1.PodSpec{
110 | RestartPolicy: corev1.RestartPolicyOnFailure,
111 | ServiceAccountName: instance.RbacResourceName(),
112 | Containers: []corev1.Container{
113 | {
114 | Name: ServiceName + "-bootstrap",
115 | Image: instance.Spec.ContainerImage,
116 | Command: []string{
117 | "/bin/bash",
118 | },
119 | Args: args,
120 | SecurityContext: &corev1.SecurityContext{
121 | RunAsUser: &runAsUser,
122 | },
123 | Env: []corev1.EnvVar{
124 | {
125 | Name: "OS_BOOTSTRAP_PASSWORD",
126 | ValueFrom: &corev1.EnvVarSource{
127 | SecretKeyRef: &corev1.SecretKeySelector{
128 | LocalObjectReference: corev1.LocalObjectReference{
129 | Name: instance.Spec.Secret,
130 | },
131 | Key: "AdminPassword",
132 | },
133 | },
134 | },
135 | },
136 | VolumeMounts: volumeMounts,
137 | },
138 | },
139 | Volumes: volumes,
140 | },
141 | },
142 | },
143 | }
144 | job.Spec.Template.Spec.Containers[0].Env = env.MergeEnvs(job.Spec.Template.Spec.Containers[0].Env, envVars)
145 |
146 | if instance.Spec.NodeSelector != nil {
147 | job.Spec.Template.Spec.NodeSelector = *instance.Spec.NodeSelector
148 | }
149 |
150 | return job
151 | }
152 |
--------------------------------------------------------------------------------
/api/v1beta1/keystoneservice.go:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | */
15 |
16 | package v1beta1
17 |
18 | import (
19 | "context"
20 | "fmt"
21 | "time"
22 |
23 | "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
24 | "github.com/openstack-k8s-operators/lib-common/modules/common/helper"
25 | "github.com/openstack-k8s-operators/lib-common/modules/common/util"
26 | "sigs.k8s.io/controller-runtime/pkg/client"
27 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
28 |
29 | k8s_errors "k8s.io/apimachinery/pkg/api/errors"
30 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 | "k8s.io/apimachinery/pkg/types"
32 | ctrl "sigs.k8s.io/controller-runtime"
33 | )
34 |
35 | // KeystoneServiceHelper -
36 | type KeystoneServiceHelper struct {
37 | service *KeystoneService
38 | timeout time.Duration
39 | labels map[string]string
40 | id string
41 | }
42 |
43 | // NewKeystoneService returns an initialized KeystoneService.
44 | func NewKeystoneService(
45 | spec KeystoneServiceSpec,
46 | namespace string,
47 | labels map[string]string,
48 | timeout time.Duration,
49 | ) *KeystoneServiceHelper {
50 | service := &KeystoneService{
51 | ObjectMeta: metav1.ObjectMeta{
52 | Name: spec.ServiceName,
53 | Namespace: namespace,
54 | },
55 | Spec: spec,
56 | }
57 |
58 | return &KeystoneServiceHelper{
59 | service: service,
60 | timeout: timeout,
61 | labels: labels,
62 | }
63 | }
64 |
65 | // CreateOrPatch - creates or patches a KeystoneService, reconciles after Xs if object won't exist.
66 | func (ks *KeystoneServiceHelper) CreateOrPatch(
67 | ctx context.Context,
68 | h *helper.Helper,
69 | ) (ctrl.Result, error) {
70 | service := &KeystoneService{
71 | ObjectMeta: metav1.ObjectMeta{
72 | Name: ks.service.Name,
73 | Namespace: ks.service.Namespace,
74 | },
75 | }
76 |
77 | // add finalizer
78 | controllerutil.AddFinalizer(service, h.GetFinalizer())
79 |
80 | op, err := controllerutil.CreateOrPatch(ctx, h.GetClient(), service, func() error {
81 | service.Spec = ks.service.Spec
82 | service.Labels = util.MergeStringMaps(service.Labels, ks.labels)
83 |
84 | return controllerutil.SetControllerReference(h.GetBeforeObject(), service, h.GetScheme())
85 | })
86 | if err != nil {
87 | if k8s_errors.IsNotFound(err) {
88 | h.GetLogger().Info(fmt.Sprintf("KeystoneService %s not found, reconcile in %s", service.Name, ks.timeout))
89 | return ctrl.Result{RequeueAfter: ks.timeout}, nil
90 | }
91 | return ctrl.Result{}, err
92 | }
93 | if op != controllerutil.OperationResultNone {
94 | h.GetLogger().Info(fmt.Sprintf("KeystoneService %s - %s", service.Name, op))
95 | }
96 |
97 | // update the service object of the KeystoneService type
98 | ks.service, err = GetKeystoneServiceWithName(ctx, h, service.GetName(), service.GetNamespace())
99 | if err != nil {
100 | return ctrl.Result{}, err
101 | }
102 |
103 | ks.id = ks.service.Status.ServiceID
104 |
105 | return ctrl.Result{}, nil
106 | }
107 |
108 | // GetServiceID - returns the openstack service ID
109 | func (ks *KeystoneServiceHelper) GetServiceID() string {
110 | return ks.id
111 | }
112 |
113 | // GetConditions - returns the conditions of the keystone service object
114 | func (ks *KeystoneServiceHelper) GetConditions() *condition.Conditions {
115 | return &ks.service.Status.Conditions
116 | }
117 |
118 | // Delete - deletes a KeystoneService if it exists.
119 | func (ks *KeystoneServiceHelper) Delete(
120 | ctx context.Context,
121 | h *helper.Helper,
122 | ) error {
123 |
124 | service, err := GetKeystoneServiceWithName(ctx, h, ks.service.Name, ks.service.Namespace)
125 | if err != nil {
126 | if k8s_errors.IsNotFound(err) {
127 | return nil
128 | }
129 | return err
130 | }
131 |
132 | err = h.GetClient().Delete(ctx, service, &client.DeleteOptions{})
133 | if err != nil && !k8s_errors.IsNotFound(err) {
134 | return err
135 | }
136 |
137 | // Service is deleted so remove the finalizer.
138 | if controllerutil.RemoveFinalizer(service, h.GetFinalizer()) {
139 | err := h.GetClient().Update(ctx, service)
140 | if err != nil && !k8s_errors.IsNotFound(err) {
141 | return err
142 | }
143 | }
144 |
145 | h.GetLogger().Info(fmt.Sprintf("KeystoneService %s in namespace %s deleted", service.Name, service.Namespace))
146 |
147 | return nil
148 | }
149 |
150 | // GetKeystoneServiceWithName func
151 | func GetKeystoneServiceWithName(
152 | ctx context.Context,
153 | h *helper.Helper,
154 | name string,
155 | namespace string,
156 | ) (*KeystoneService, error) {
157 |
158 | ks := &KeystoneService{}
159 | err := h.GetClient().Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, ks)
160 | if err != nil {
161 | return nil, err
162 | }
163 |
164 | return ks, nil
165 | }
166 |
167 | // DeleteKeystoneServiceWithName func
168 | func DeleteKeystoneServiceWithName(
169 | ctx context.Context,
170 | h *helper.Helper,
171 | name string,
172 | namespace string,
173 | ) error {
174 |
175 | ks, err := GetKeystoneServiceWithName(ctx, h, name, namespace)
176 | if err != nil && !k8s_errors.IsNotFound(err) {
177 | return err
178 | }
179 |
180 | if ks != nil {
181 | ksSvcObj := NewKeystoneService(ks.Spec, namespace, map[string]string{}, 10)
182 |
183 | err = ksSvcObj.Delete(ctx, h)
184 | if err != nil {
185 | return err
186 | }
187 | }
188 |
189 | return nil
190 | }
191 |
--------------------------------------------------------------------------------
/test/kuttl/common/assert_sample_deployment.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # Check for:
3 | #
4 | # - 1 KeystoneAPI CR
5 | # - Deployment with 1 Pod for KeystoneAPI CR
6 | # - Keystone-internal Service
7 | # - Keystone-public Service
8 |
9 | apiVersion: keystone.openstack.org/v1beta1
10 | kind: KeystoneAPI
11 | metadata:
12 | finalizers:
13 | - openstack.org/keystoneapi
14 | name: keystone
15 | spec:
16 | adminProject: admin
17 | adminUser: admin
18 | customServiceConfig: |
19 | [DEFAULT]
20 | debug = true
21 | databaseInstance: openstack
22 | databaseAccount: keystone
23 | memcachedInstance: memcached
24 | passwordSelectors:
25 | admin: AdminPassword
26 | preserveJobs: false
27 | region: regionOne
28 | replicas: 1
29 | resources:
30 | requests:
31 | cpu: "1"
32 | memory: 500Mi
33 | secret: osp-secret
34 | status:
35 | databaseHostname: openstack.keystone-kuttl-tests.svc
36 | readyCount: 1
37 | ---
38 | apiVersion: apps/v1
39 | kind: Deployment
40 | metadata:
41 | name: keystone
42 | spec:
43 | replicas: 1
44 | template:
45 | metadata:
46 | labels:
47 | service: keystone
48 | spec:
49 | affinity:
50 | podAntiAffinity:
51 | preferredDuringSchedulingIgnoredDuringExecution:
52 | - podAffinityTerm:
53 | labelSelector:
54 | matchExpressions:
55 | - key: service
56 | operator: In
57 | values:
58 | - keystone
59 | topologyKey: kubernetes.io/hostname
60 | weight: 100
61 | containers:
62 | - args:
63 | - -c
64 | - /usr/local/bin/kolla_start
65 | command:
66 | - /bin/bash
67 | imagePullPolicy: IfNotPresent
68 | livenessProbe:
69 | failureThreshold: 3
70 | httpGet:
71 | path: /v3
72 | port: 5000
73 | scheme: HTTP
74 | initialDelaySeconds: 5
75 | periodSeconds: 30
76 | successThreshold: 1
77 | timeoutSeconds: 30
78 | name: keystone-api
79 | readinessProbe:
80 | failureThreshold: 3
81 | httpGet:
82 | path: /v3
83 | port: 5000
84 | scheme: HTTP
85 | initialDelaySeconds: 5
86 | periodSeconds: 30
87 | successThreshold: 1
88 | timeoutSeconds: 30
89 | resources:
90 | requests:
91 | cpu: "1"
92 | memory: 500Mi
93 | restartPolicy: Always
94 | serviceAccount: keystone-keystone
95 | serviceAccountName: keystone-keystone
96 | status:
97 | availableReplicas: 1
98 | replicas: 1
99 | ---
100 | # the openshift annotations can't be checked through the deployment above
101 | apiVersion: v1
102 | kind: Pod
103 | metadata:
104 | annotations:
105 | openshift.io/scc: anyuid
106 | labels:
107 | service: keystone
108 | ---
109 | apiVersion: v1
110 | kind: Service
111 | metadata:
112 | labels:
113 | endpoint: internal
114 | service: keystone
115 | name: keystone-internal
116 | spec:
117 | ports:
118 | - name: keystone-internal
119 | selector:
120 | service: keystone
121 | type: ClusterIP
122 | ---
123 | apiVersion: v1
124 | kind: Service
125 | metadata:
126 | labels:
127 | endpoint: public
128 | service: keystone
129 | name: keystone-public
130 | spec:
131 | ports:
132 | - name: keystone-public
133 | selector:
134 | service: keystone
135 | type: ClusterIP
136 | ---
137 | # the actual addresses of the apiEndpoints are platform specific, so we can't rely on
138 | # kuttl asserts to check them. This short script gathers the addresses and checks that
139 | # the three endpoints are defined and their addresses follow the default pattern
140 | apiVersion: kuttl.dev/v1beta1
141 | kind: TestAssert
142 | commands:
143 | - script: |
144 | # the actual addresses of the apiEndpoints are platform specific, so we can't rely on
145 | # kuttl asserts to check them. This short script gathers the addresses and checks that
146 | # the three endpoints are defined and their addresses follow the default pattern
147 | template='{{.status.apiEndpoints.internal}}{{":"}}{{.status.apiEndpoints.public}}{{"\n"}}'
148 | regex="http:\/\/keystone-internal.$NAMESPACE.*:http:\/\/keystone-public.$NAMESPACE.*"
149 | apiEndpoints=$(oc get -n $NAMESPACE KeystoneAPI keystone -o go-template="$template")
150 | matches=$(echo "$apiEndpoints" | sed -e "s?$regex??")
151 | if [ -z "$matches" ]; then
152 | exit 0
153 | else
154 | echo "Endpoints URLS: $apiEndpoints do not match regex"
155 | exit 1
156 | fi
157 |
158 | # when using image digests the containerImage URLs are SHA's so we verify them with a script
159 | tupleTemplate='{{ range (index .spec.template.spec.containers 0).env }}{{ .name }}{{ "#" }}{{ .value}}{{"\n"}}{{ end }}'
160 | imageTuples=$(oc get -n openstack-operators deployment keystone-operator-controller-manager -o go-template="$tupleTemplate")
161 | # format of imageTuple is: RELATED_IMAGE_KEYSTONE_# separated by newlines
162 | for ITEM in $(echo $imageTuples); do
163 | # it is an image
164 | if echo $ITEM | grep 'RELATED_IMAGE' &> /dev/null; then
165 | NAME=$(echo $ITEM | sed -e 's|^RELATED_IMAGE_KEYSTONE_\([^_]*\)_.*|\1|')
166 | IMG_FROM_ENV=$(echo $ITEM | sed -e 's|^.*#\(.*\)|\1|')
167 | template='{{.spec.containerImage}}'
168 | case $NAME in
169 | API)
170 | SERVICE_IMAGE=$(oc get -n $NAMESPACE keystoneapi keystone -o go-template="$template")
171 | ;;
172 | esac
173 | if [ "$SERVICE_IMAGE" != "$IMG_FROM_ENV" ]; then
174 | echo "$NAME image does not equal $VALUE"
175 | exit 1
176 | fi
177 | fi
178 | done
179 |
--------------------------------------------------------------------------------
/api/bases/keystone.openstack.org_keystoneservices.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | controller-gen.kubebuilder.io/version: v0.18.0
7 | name: keystoneservices.keystone.openstack.org
8 | spec:
9 | group: keystone.openstack.org
10 | names:
11 | kind: KeystoneService
12 | listKind: KeystoneServiceList
13 | plural: keystoneservices
14 | singular: keystoneservice
15 | scope: Namespaced
16 | versions:
17 | - additionalPrinterColumns:
18 | - description: Status
19 | jsonPath: .status.conditions[0].status
20 | name: Status
21 | type: string
22 | - description: Message
23 | jsonPath: .status.conditions[0].message
24 | name: Message
25 | type: string
26 | name: v1beta1
27 | schema:
28 | openAPIV3Schema:
29 | description: KeystoneService is the Schema for the keystoneservices API
30 | properties:
31 | apiVersion:
32 | description: |-
33 | APIVersion defines the versioned schema of this representation of an object.
34 | Servers should convert recognized schemas to the latest internal value, and
35 | may reject unrecognized values.
36 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
37 | type: string
38 | kind:
39 | description: |-
40 | Kind is a string value representing the REST resource this object represents.
41 | Servers may infer this from the endpoint the client submits requests to.
42 | Cannot be updated.
43 | In CamelCase.
44 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
45 | type: string
46 | metadata:
47 | type: object
48 | spec:
49 | description: KeystoneServiceSpec defines the desired state of KeystoneService
50 | properties:
51 | enabled:
52 | description: Enabled - whether or not the service is enabled.
53 | type: boolean
54 | passwordSelector:
55 | description: PasswordSelector - Selector to get the ServiceUser password
56 | from the Secret, e.g. PlacementPassword
57 | type: string
58 | secret:
59 | description: Secret containing OpenStack password information for
60 | the ServiceUser
61 | type: string
62 | serviceDescription:
63 | description: ServiceDescription - Description for the service.
64 | type: string
65 | serviceName:
66 | description: ServiceName - Name of the service.
67 | type: string
68 | serviceType:
69 | description: ServiceType - Type is the type of the service.
70 | type: string
71 | serviceUser:
72 | description: ServiceUser - optional username used for this service
73 | type: string
74 | required:
75 | - enabled
76 | - passwordSelector
77 | - secret
78 | - serviceName
79 | - serviceType
80 | - serviceUser
81 | type: object
82 | status:
83 | description: KeystoneServiceStatus defines the observed state of KeystoneService
84 | properties:
85 | conditions:
86 | description: Conditions
87 | items:
88 | description: Condition defines an observation of a API resource
89 | operational state.
90 | properties:
91 | lastTransitionTime:
92 | description: |-
93 | Last time the condition transitioned from one status to another.
94 | This should be when the underlying condition changed. If that is not known, then using the time when
95 | the API field changed is acceptable.
96 | format: date-time
97 | type: string
98 | message:
99 | description: A human readable message indicating details about
100 | the transition.
101 | type: string
102 | reason:
103 | description: The reason for the condition's last transition
104 | in CamelCase.
105 | type: string
106 | severity:
107 | description: |-
108 | Severity provides a classification of Reason code, so the current situation is immediately
109 | understandable and could act accordingly.
110 | It is meant for situations where Status=False and it should be indicated if it is just
111 | informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue
112 | and no actions to automatically resolve the issue can/should be done).
113 | For conditions where Status=Unknown or Status=True the Severity should be SeverityNone.
114 | type: string
115 | status:
116 | description: Status of the condition, one of True, False, Unknown.
117 | type: string
118 | type:
119 | description: Type of condition in CamelCase.
120 | type: string
121 | required:
122 | - lastTransitionTime
123 | - status
124 | - type
125 | type: object
126 | type: array
127 | serviceID:
128 | type: string
129 | type: object
130 | type: object
131 | served: true
132 | storage: true
133 | subresources:
134 | status: {}
135 |
--------------------------------------------------------------------------------
/config/crd/bases/keystone.openstack.org_keystoneservices.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | controller-gen.kubebuilder.io/version: v0.18.0
7 | name: keystoneservices.keystone.openstack.org
8 | spec:
9 | group: keystone.openstack.org
10 | names:
11 | kind: KeystoneService
12 | listKind: KeystoneServiceList
13 | plural: keystoneservices
14 | singular: keystoneservice
15 | scope: Namespaced
16 | versions:
17 | - additionalPrinterColumns:
18 | - description: Status
19 | jsonPath: .status.conditions[0].status
20 | name: Status
21 | type: string
22 | - description: Message
23 | jsonPath: .status.conditions[0].message
24 | name: Message
25 | type: string
26 | name: v1beta1
27 | schema:
28 | openAPIV3Schema:
29 | description: KeystoneService is the Schema for the keystoneservices API
30 | properties:
31 | apiVersion:
32 | description: |-
33 | APIVersion defines the versioned schema of this representation of an object.
34 | Servers should convert recognized schemas to the latest internal value, and
35 | may reject unrecognized values.
36 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
37 | type: string
38 | kind:
39 | description: |-
40 | Kind is a string value representing the REST resource this object represents.
41 | Servers may infer this from the endpoint the client submits requests to.
42 | Cannot be updated.
43 | In CamelCase.
44 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
45 | type: string
46 | metadata:
47 | type: object
48 | spec:
49 | description: KeystoneServiceSpec defines the desired state of KeystoneService
50 | properties:
51 | enabled:
52 | description: Enabled - whether or not the service is enabled.
53 | type: boolean
54 | passwordSelector:
55 | description: PasswordSelector - Selector to get the ServiceUser password
56 | from the Secret, e.g. PlacementPassword
57 | type: string
58 | secret:
59 | description: Secret containing OpenStack password information for
60 | the ServiceUser
61 | type: string
62 | serviceDescription:
63 | description: ServiceDescription - Description for the service.
64 | type: string
65 | serviceName:
66 | description: ServiceName - Name of the service.
67 | type: string
68 | serviceType:
69 | description: ServiceType - Type is the type of the service.
70 | type: string
71 | serviceUser:
72 | description: ServiceUser - optional username used for this service
73 | type: string
74 | required:
75 | - enabled
76 | - passwordSelector
77 | - secret
78 | - serviceName
79 | - serviceType
80 | - serviceUser
81 | type: object
82 | status:
83 | description: KeystoneServiceStatus defines the observed state of KeystoneService
84 | properties:
85 | conditions:
86 | description: Conditions
87 | items:
88 | description: Condition defines an observation of a API resource
89 | operational state.
90 | properties:
91 | lastTransitionTime:
92 | description: |-
93 | Last time the condition transitioned from one status to another.
94 | This should be when the underlying condition changed. If that is not known, then using the time when
95 | the API field changed is acceptable.
96 | format: date-time
97 | type: string
98 | message:
99 | description: A human readable message indicating details about
100 | the transition.
101 | type: string
102 | reason:
103 | description: The reason for the condition's last transition
104 | in CamelCase.
105 | type: string
106 | severity:
107 | description: |-
108 | Severity provides a classification of Reason code, so the current situation is immediately
109 | understandable and could act accordingly.
110 | It is meant for situations where Status=False and it should be indicated if it is just
111 | informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue
112 | and no actions to automatically resolve the issue can/should be done).
113 | For conditions where Status=Unknown or Status=True the Severity should be SeverityNone.
114 | type: string
115 | status:
116 | description: Status of the condition, one of True, False, Unknown.
117 | type: string
118 | type:
119 | description: Type of condition in CamelCase.
120 | type: string
121 | required:
122 | - lastTransitionTime
123 | - status
124 | - type
125 | type: object
126 | type: array
127 | serviceID:
128 | type: string
129 | type: object
130 | type: object
131 | served: true
132 | storage: true
133 | subresources:
134 | status: {}
135 |
--------------------------------------------------------------------------------
/test/functional/base_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package functional_test
18 |
19 | import (
20 | "fmt"
21 |
22 | . "github.com/onsi/gomega" //revive:disable:dot-imports
23 |
24 | batchv1 "k8s.io/api/batch/v1"
25 | corev1 "k8s.io/api/core/v1"
26 | "k8s.io/apimachinery/pkg/types"
27 | "sigs.k8s.io/controller-runtime/pkg/client"
28 |
29 | keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
30 | keystone_base "github.com/openstack-k8s-operators/keystone-operator/internal/keystone"
31 | condition "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
32 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33 | )
34 |
35 | func GetKeystoneAPISpec(fernetMaxKeys int32) map[string]any {
36 | return map[string]any{
37 | "databaseInstance": "openstack",
38 | "replicas": 1,
39 | "secret": SecretName,
40 | "databaseAccount": AccountName,
41 | "fernetMaxActiveKeys": fernetMaxKeys,
42 | }
43 | }
44 |
45 | func GetDefaultKeystoneAPISpec() map[string]any {
46 | return GetKeystoneAPISpec(5)
47 | }
48 |
49 | func GetTLSKeystoneAPISpec() map[string]any {
50 | return map[string]any{
51 | "databaseInstance": "openstack",
52 | "replicas": 1,
53 | "secret": SecretName,
54 | "databaseAccount": AccountName,
55 | "tls": map[string]any{
56 | "api": map[string]any{
57 | "internal": map[string]any{
58 | "secretName": InternalCertSecretName,
59 | },
60 | "public": map[string]any{
61 | "secretName": PublicCertSecretName,
62 | },
63 | },
64 | "caBundleSecretName": CABundleSecretName,
65 | },
66 | }
67 | }
68 |
69 | func CreateKeystoneAPI(name types.NamespacedName, spec map[string]any) client.Object {
70 |
71 | raw := map[string]any{
72 | "apiVersion": "keystone.openstack.org/v1beta1",
73 | "kind": "KeystoneAPI",
74 | "metadata": map[string]any{
75 | "name": name.Name,
76 | "namespace": name.Namespace,
77 | },
78 | "spec": spec,
79 | }
80 | return th.CreateUnstructured(raw)
81 | }
82 |
83 | func GetKeystoneAPI(name types.NamespacedName) *keystonev1.KeystoneAPI {
84 | instance := &keystonev1.KeystoneAPI{}
85 | Eventually(func(g Gomega) {
86 | g.Expect(k8sClient.Get(ctx, name, instance)).Should(Succeed())
87 | }, timeout, interval).Should(Succeed())
88 | return instance
89 | }
90 |
91 | func CreateKeystoneAPISecret(namespace string, name string) *corev1.Secret {
92 | return th.CreateSecret(
93 | types.NamespacedName{Namespace: namespace, Name: name},
94 | map[string][]byte{
95 | "AdminPassword": []byte("12345678"),
96 | },
97 | )
98 | }
99 |
100 | func KeystoneConditionGetter(name types.NamespacedName) condition.Conditions {
101 | instance := GetKeystoneAPI(name)
102 | return instance.Status.Conditions
103 | }
104 |
105 | func GetCronJob(name types.NamespacedName) *batchv1.CronJob {
106 | instance := &batchv1.CronJob{}
107 | Eventually(func(g Gomega) {
108 | g.Expect(k8sClient.Get(ctx, name, instance)).Should(Succeed())
109 | }, timeout, interval).Should(Succeed())
110 | return instance
111 | }
112 |
113 | func CreateKeystoneMessageBusSecret(namespace string, name string) *corev1.Secret {
114 | s := th.CreateSecret(
115 | types.NamespacedName{Namespace: namespace, Name: name},
116 | map[string][]byte{
117 | "transport_url": fmt.Appendf(nil, "rabbit://%s/fake", name),
118 | },
119 | )
120 | logger.Info("Secret created", "name", name)
121 | return s
122 | }
123 |
124 | // GetSampleTopologySpec - A sample (and opinionated) Topology Spec used to
125 | // test KeystoneAPI
126 | // Note this is just an example that should not be used in production for
127 | // multiple reasons:
128 | // 1. It uses ScheduleAnyway as strategy, which is something we might
129 | // want to avoid by default
130 | // 2. Usually a topologySpreadConstraints is used to take care about
131 | // multi AZ, which is not applicable in this context
132 | func GetSampleTopologySpec(label string) (map[string]any, []corev1.TopologySpreadConstraint) {
133 | // Build the topology Spec
134 | topologySpec := map[string]any{
135 | "topologySpreadConstraints": []map[string]any{
136 | {
137 | "maxSkew": 1,
138 | "topologyKey": corev1.LabelHostname,
139 | "whenUnsatisfiable": "ScheduleAnyway",
140 | "labelSelector": map[string]any{
141 | "matchLabels": map[string]any{
142 | "service": keystone_base.ServiceName,
143 | "component": label,
144 | },
145 | },
146 | },
147 | },
148 | }
149 | // Build the topologyObj representation
150 | topologySpecObj := []corev1.TopologySpreadConstraint{
151 | {
152 | MaxSkew: 1,
153 | TopologyKey: corev1.LabelHostname,
154 | WhenUnsatisfiable: corev1.ScheduleAnyway,
155 | LabelSelector: &metav1.LabelSelector{
156 | MatchLabels: map[string]string{
157 | "service": keystone_base.ServiceName,
158 | "component": label,
159 | },
160 | },
161 | },
162 | }
163 | return topologySpec, topologySpecObj
164 | }
165 |
166 | // GetExtraMounts - Utility function that simulates extraMounts pointing
167 | // to a secret
168 | func GetExtraMounts(kemName string, kemPath string) []map[string]any {
169 | return []map[string]any{
170 | {
171 | "name": kemName,
172 | "region": "az0",
173 | "extraVol": []map[string]any{
174 | {
175 | "extraVolType": kemName,
176 | "propagation": []string{
177 | "Keystone",
178 | },
179 | "volumes": []map[string]any{
180 | {
181 | "name": kemName,
182 | "secret": map[string]any{
183 | "secretName": kemName,
184 | },
185 | },
186 | },
187 | "mounts": []map[string]any{
188 | {
189 | "name": kemName,
190 | "mountPath": kemPath,
191 | "readOnly": true,
192 | },
193 | },
194 | },
195 | },
196 | },
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/internal/webhook/v1beta1/keystoneapi_webhook.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package v1beta1 contains webhook implementations for KeystoneAPI v1beta1 resources.
18 | package v1beta1
19 |
20 | import (
21 | "context"
22 | "errors"
23 | "fmt"
24 |
25 | "k8s.io/apimachinery/pkg/runtime"
26 | ctrl "sigs.k8s.io/controller-runtime"
27 | logf "sigs.k8s.io/controller-runtime/pkg/log"
28 | "sigs.k8s.io/controller-runtime/pkg/webhook"
29 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
30 |
31 | keystonev1beta1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
32 | )
33 |
34 | // Static errors for type assertions
35 | var (
36 | errUnexpectedObjectType = errors.New("unexpected object type")
37 | )
38 |
39 | // nolint:unused
40 | // log is for logging in this package.
41 | var keystoneapilog = logf.Log.WithName("keystoneapi-resource")
42 |
43 | // SetupKeystoneAPIWebhookWithManager registers the webhook for KeystoneAPI in the manager.
44 | func SetupKeystoneAPIWebhookWithManager(mgr ctrl.Manager) error {
45 | return ctrl.NewWebhookManagedBy(mgr).For(&keystonev1beta1.KeystoneAPI{}).
46 | WithValidator(&KeystoneAPICustomValidator{}).
47 | WithDefaulter(&KeystoneAPICustomDefaulter{}).
48 | Complete()
49 | }
50 |
51 | // TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
52 |
53 | // +kubebuilder:webhook:path=/mutate-keystone-openstack-org-v1beta1-keystoneapi,mutating=true,failurePolicy=fail,sideEffects=None,groups=keystone.openstack.org,resources=keystoneapis,verbs=create;update,versions=v1beta1,name=mkeystoneapi-v1beta1.kb.io,admissionReviewVersions=v1
54 |
55 | // KeystoneAPICustomDefaulter struct is responsible for setting default values on the custom resource of the
56 | // Kind KeystoneAPI when those are created or updated.
57 | //
58 | // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
59 | // as it is used only for temporary operations and does not need to be deeply copied.
60 | type KeystoneAPICustomDefaulter struct {
61 | // TODO(user): Add more fields as needed for defaulting
62 | }
63 |
64 | var _ webhook.CustomDefaulter = &KeystoneAPICustomDefaulter{}
65 |
66 | // Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind KeystoneAPI.
67 | func (d *KeystoneAPICustomDefaulter) Default(_ context.Context, obj runtime.Object) error {
68 | keystoneapi, ok := obj.(*keystonev1beta1.KeystoneAPI)
69 |
70 | if !ok {
71 | return fmt.Errorf("%w: expected an KeystoneAPI object but got %T", errUnexpectedObjectType, obj)
72 | }
73 | keystoneapilog.Info("Defaulting for KeystoneAPI", "name", keystoneapi.GetName())
74 |
75 | // Call the defaulting logic from api/v1beta1
76 | keystoneapi.Default()
77 |
78 | return nil
79 | }
80 |
81 | // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
82 | // NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
83 | // Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
84 | // +kubebuilder:webhook:path=/validate-keystone-openstack-org-v1beta1-keystoneapi,mutating=false,failurePolicy=fail,sideEffects=None,groups=keystone.openstack.org,resources=keystoneapis,verbs=create;update,versions=v1beta1,name=vkeystoneapi-v1beta1.kb.io,admissionReviewVersions=v1
85 |
86 | // KeystoneAPICustomValidator struct is responsible for validating the KeystoneAPI resource
87 | // when it is created, updated, or deleted.
88 | //
89 | // NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
90 | // as this struct is used only for temporary operations and does not need to be deeply copied.
91 | type KeystoneAPICustomValidator struct {
92 | // TODO(user): Add more fields as needed for validation
93 | }
94 |
95 | var _ webhook.CustomValidator = &KeystoneAPICustomValidator{}
96 |
97 | // ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type KeystoneAPI.
98 | func (v *KeystoneAPICustomValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
99 | keystoneapi, ok := obj.(*keystonev1beta1.KeystoneAPI)
100 | if !ok {
101 | return nil, fmt.Errorf("%w: expected a KeystoneAPI object but got %T", errUnexpectedObjectType, obj)
102 | }
103 | keystoneapilog.Info("Validation for KeystoneAPI upon creation", "name", keystoneapi.GetName())
104 |
105 | // Call the validation logic from api/v1beta1
106 | return keystoneapi.ValidateCreate()
107 | }
108 |
109 | // ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type KeystoneAPI.
110 | func (v *KeystoneAPICustomValidator) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
111 | keystoneapi, ok := newObj.(*keystonev1beta1.KeystoneAPI)
112 | if !ok {
113 | return nil, fmt.Errorf("%w: expected a KeystoneAPI object for the newObj but got %T", errUnexpectedObjectType, newObj)
114 | }
115 | keystoneapilog.Info("Validation for KeystoneAPI upon update", "name", keystoneapi.GetName())
116 |
117 | // Call the validation logic from api/v1beta1
118 | return keystoneapi.ValidateUpdate(oldObj)
119 | }
120 |
121 | // ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type KeystoneAPI.
122 | func (v *KeystoneAPICustomValidator) ValidateDelete(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
123 | keystoneapi, ok := obj.(*keystonev1beta1.KeystoneAPI)
124 | if !ok {
125 | return nil, fmt.Errorf("%w: expected a KeystoneAPI object but got %T", errUnexpectedObjectType, obj)
126 | }
127 | keystoneapilog.Info("Validation for KeystoneAPI upon deletion", "name", keystoneapi.GetName())
128 |
129 | // Call the validation logic from api/v1beta1
130 | return keystoneapi.ValidateDelete()
131 | }
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # keystone-operator
2 |
3 | A Kubernetes Operator built using the [Operator Framework](https://github.com/operator-framework) for Go. The Operator provides a way to easily install and manage an OpenStack Keystone installation
4 | on Kubernetes. This Operator was developed using [TCIB](https://github.com/openstack-k8s-operators/tcib/blob/main/container-images/containers.yaml) containers for OpenStack.
5 |
6 | # Deployment
7 |
8 | The operator is intended to be deployed via OLM [Operator Lifecycle Manager](https://github.com/operator-framework/operator-lifecycle-manager)
9 |
10 | # API Example
11 |
12 | The Operator creates a custom KeystoneAPI resource that can be used to create Keystone API
13 | instances within the cluster. Example CR to create an Keystone API in your cluster:
14 |
15 | ```yaml
16 | apiVersion: keystone.openstack.org/v1beta1
17 | kind: KeystoneAPI
18 | metadata:
19 | name: keystone
20 | spec:
21 | containerImage: quay.io/podified-antelope-centos9/openstack-keystone:current-podified
22 | replicas: 1
23 | secret: osp-secret
24 | ```
25 |
26 | ## Example: configure Keystone with additional networks
27 |
28 | The Keystone spec can be used to configure Keystone to have the pods
29 | being attached to additional networks.
30 |
31 | Create a network-attachement-definition which then can be referenced
32 | from the Keystone API CR.
33 |
34 | ```
35 | ---
36 | apiVersion: k8s.cni.cncf.io/v1
37 | kind: NetworkAttachmentDefinition
38 | metadata:
39 | name: storage
40 | namespace: openstack
41 | spec:
42 | config: |
43 | {
44 | "cniVersion": "0.3.1",
45 | "name": "storage",
46 | "type": "macvlan",
47 | "master": "enp7s0.21",
48 | "ipam": {
49 | "type": "whereabouts",
50 | "range": "172.18.0.0/24",
51 | "range_start": "172.18.0.50",
52 | "range_end": "172.18.0.100"
53 | }
54 | }
55 | ```
56 |
57 | The following represents an example of Keystone resource that can be used
58 | to trigger the service deployment, and have the service pods attached to
59 | the storage network using the above NetworkAttachmentDefinition.
60 |
61 | ```
62 | apiVersion: keystone.openstack.org/v1beta1
63 | kind: KeystoneAPI
64 | metadata:
65 | name: keystone
66 | spec:
67 | ...
68 | networkAttachents:
69 | - storage
70 | ...
71 | ```
72 |
73 | When the service is up and running, it will now have an additional nic
74 | configured for the storage network:
75 |
76 | ```
77 | # oc rsh keystone-75f5cd6595-kpfr2
78 | sh-5.1# ip a
79 | 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
80 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
81 | inet 127.0.0.1/8 scope host lo
82 | valid_lft forever preferred_lft forever
83 | inet6 ::1/128 scope host
84 | valid_lft forever preferred_lft forever
85 | 3: eth0@if298: mtu 1450 qdisc noqueue state UP group default
86 | link/ether 0a:58:0a:82:01:18 brd ff:ff:ff:ff:ff:ff link-netnsid 0
87 | inet 10.130.1.24/23 brd 10.130.1.255 scope global eth0
88 | valid_lft forever preferred_lft forever
89 | inet6 fe80::4cf2:a3ff:feb0:932/64 scope link
90 | valid_lft forever preferred_lft forever
91 | 4: net1@if26: mtu 1500 qdisc noqueue state UP group default
92 | link/ether a2:f1:3b:12:fd:be brd ff:ff:ff:ff:ff:ff link-netnsid 0
93 | inet 172.18.0.52/24 brd 172.18.0.255 scope global net1
94 | valid_lft forever preferred_lft forever
95 | inet6 fe80::a0f1:3bff:fe12:fdbe/64 scope link
96 | valid_lft forever preferred_lft forever
97 | ```
98 |
99 | ## Example: expose Keystone to an isolated network
100 |
101 | The Keystone spec can be used to configure Keystone to register e.g.
102 | the internal endpoint to an isolated network. MetalLB is used for this
103 | scenario.
104 |
105 | As a pre requisite, MetalLB needs to be installed and worker nodes
106 | prepared to work as MetalLB nodes to serve the LoadBalancer service.
107 |
108 | In this example the following MetalLB IPAddressPool is used:
109 |
110 | ```
111 | ---
112 | apiVersion: metallb.io/v1beta1
113 | kind: IPAddressPool
114 | metadata:
115 | name: osp-internalapi
116 | namespace: metallb-system
117 | spec:
118 | addresses:
119 | - 172.17.0.200-172.17.0.210
120 | autoAssign: false
121 | ```
122 |
123 | The following represents an example of Keystone resource that can be used
124 | to trigger the service deployment, and have the internal keystoneAPI endpoint
125 | registerd as a MetalLB service using the IPAddressPool `osp-internal`,
126 | request to use the IP `172.17.0.202` as the VIP and the IP is shared with
127 | other services.
128 |
129 | ```
130 | apiVersion: keystone.openstack.org/v1beta1
131 | kind: KeystoneAPI
132 | metadata:
133 | name: keystone
134 | spec:
135 | ...
136 | externalEndpoints:
137 | - endpoint: internal
138 | ipAddressPool: osp-internalapi
139 | loadBalancerIPs:
140 | - 172.17.0.202
141 | sharedIP: true
142 | sharedIPKey: ""
143 | ...
144 | ...
145 | ```
146 |
147 | The internal keystone endpoint gets registered with its service name. This
148 | service name needs to resolve to the `LoadBalancerIP` on the isolated network
149 | either by DNS or via /etc/hosts:
150 |
151 | ```
152 | # openstack endpoint list -c 'Service Name' -c Interface -c URL --service keystone
153 | +--------------+-----------+-----------------------------------------------------------------+
154 | | Service Name | Interface | URL |
155 | +--------------+-----------+-----------------------------------------------------------------+
156 | | keystone | public | http://keystone-public-openstack.apps.ostest.test.metalkube.org |
157 | | keystone | internal | http://keystone-internal.openstack.svc:5000 |
158 | +--------------+-----------+-----------------------------------------------------------------+
159 | ```
160 |
161 | # Design
162 | The current design takes care of the following:
163 |
164 | - Creates keystone config files via config maps
165 | - Creates a keystone deployment with the specified replicas
166 | - Creates a keystone service
167 | - Generates Fernet keys (TODO: rotate them, and bounce the APIs upon rotation)
168 | - Keystone bootstrap, and db sync are executed automatically on install and updates
169 | - ConfigMap is recreated on any changes KeystoneAPI object changes and the Deployment updated.
170 |
--------------------------------------------------------------------------------
/api/bases/keystone.openstack.org_keystoneendpoints.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | controller-gen.kubebuilder.io/version: v0.18.0
7 | name: keystoneendpoints.keystone.openstack.org
8 | spec:
9 | group: keystone.openstack.org
10 | names:
11 | kind: KeystoneEndpoint
12 | listKind: KeystoneEndpointList
13 | plural: keystoneendpoints
14 | singular: keystoneendpoint
15 | scope: Namespaced
16 | versions:
17 | - additionalPrinterColumns:
18 | - description: Status
19 | jsonPath: .status.conditions[0].status
20 | name: Status
21 | type: string
22 | - description: Message
23 | jsonPath: .status.conditions[0].message
24 | name: Message
25 | type: string
26 | name: v1beta1
27 | schema:
28 | openAPIV3Schema:
29 | description: KeystoneEndpoint is the Schema for the keystoneendpoints API
30 | properties:
31 | apiVersion:
32 | description: |-
33 | APIVersion defines the versioned schema of this representation of an object.
34 | Servers should convert recognized schemas to the latest internal value, and
35 | may reject unrecognized values.
36 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
37 | type: string
38 | kind:
39 | description: |-
40 | Kind is a string value representing the REST resource this object represents.
41 | Servers may infer this from the endpoint the client submits requests to.
42 | Cannot be updated.
43 | In CamelCase.
44 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
45 | type: string
46 | metadata:
47 | type: object
48 | spec:
49 | description: KeystoneEndpointSpec defines the desired state of KeystoneEndpoint
50 | properties:
51 | endpoints:
52 | additionalProperties:
53 | type: string
54 | description: Endpoints - map with service api endpoint URLs with the
55 | endpoint type as index
56 | type: object
57 | serviceName:
58 | description: ServiceName - Name of the service to create the endpoint
59 | for
60 | type: string
61 | required:
62 | - endpoints
63 | - serviceName
64 | type: object
65 | status:
66 | description: KeystoneEndpointStatus defines the observed state of KeystoneEndpoint
67 | properties:
68 | conditions:
69 | description: Conditions
70 | items:
71 | description: Condition defines an observation of a API resource
72 | operational state.
73 | properties:
74 | lastTransitionTime:
75 | description: |-
76 | Last time the condition transitioned from one status to another.
77 | This should be when the underlying condition changed. If that is not known, then using the time when
78 | the API field changed is acceptable.
79 | format: date-time
80 | type: string
81 | message:
82 | description: A human readable message indicating details about
83 | the transition.
84 | type: string
85 | reason:
86 | description: The reason for the condition's last transition
87 | in CamelCase.
88 | type: string
89 | severity:
90 | description: |-
91 | Severity provides a classification of Reason code, so the current situation is immediately
92 | understandable and could act accordingly.
93 | It is meant for situations where Status=False and it should be indicated if it is just
94 | informational, warning (next reconciliation might fix it) or an error (e.g. DB create issue
95 | and no actions to automatically resolve the issue can/should be done).
96 | For conditions where Status=Unknown or Status=True the Severity should be SeverityNone.
97 | type: string
98 | status:
99 | description: Status of the condition, one of True, False, Unknown.
100 | type: string
101 | type:
102 | description: Type of condition in CamelCase.
103 | type: string
104 | required:
105 | - lastTransitionTime
106 | - status
107 | - type
108 | type: object
109 | type: array
110 | endpointIDs:
111 | additionalProperties:
112 | type: string
113 | type: object
114 | endpoints:
115 | description: Endpoints - current status of latest configured endpoints
116 | for the service
117 | items:
118 | description: Endpoint -
119 | properties:
120 | id:
121 | description: ID - endpoint id
122 | type: string
123 | interface:
124 | description: Interface - public, internal, admin
125 | type: string
126 | url:
127 | description: URL - endpoint url
128 | type: string
129 | required:
130 | - id
131 | - interface
132 | - url
133 | type: object
134 | type: array
135 | observedGeneration:
136 | description: ObservedGeneration - the most recent generation observed
137 | for this service. If the observed generation is less than the spec
138 | generation, then the controller has not processed the latest changes.
139 | format: int64
140 | type: integer
141 | serviceID:
142 | type: string
143 | type: object
144 | type: object
145 | served: true
146 | storage: true
147 | subresources:
148 | status: {}
149 |
--------------------------------------------------------------------------------