|
@@ -0,0 +1,245 @@
|
|
|
|
+---
|
|
|
|
+# Ensures there is an instance of RHBoK running in a configurable namespace.
|
|
|
|
+#
|
|
|
|
+# Configures it with a realm, and some users.
|
|
|
|
+#
|
|
|
|
+# Required variables (some are supplied by defaults/main.yml):
|
|
|
|
+#
|
|
|
|
+# rhbk:
|
|
|
|
+# namespace: namespace to deploy to (keycloak)
|
|
|
|
+# name: name of the instance (sso)
|
|
|
|
+# replicas: how many instances
|
|
|
|
+# fqdn: fqdn of the route (hostname), detected if omitted
|
|
|
|
+# admin: bootstrap admin credentials
|
|
|
|
+# username: username (rhbk)
|
|
|
|
+# password: password (secret)
|
|
|
|
+# db: database-specific settings
|
|
|
|
+# image: db server image
|
|
|
|
+# name: database name (rhbk)
|
|
|
|
+# username: database owner (rhbk)
|
|
|
|
+# password: db owner's password (secret)
|
|
|
|
+# claim_modes: volume claim template access modes, list (ReadWriteOnce)
|
|
|
|
+# storage_class: storage class name, no default (omitted)
|
|
|
|
+# size: pvc size (1Gi)
|
|
|
|
+# replicas: how many instances (TODO ignored for now)
|
|
|
|
+# realm: name of the realm (sample-realm)
|
|
|
|
+# users: users to create in realm, no default (meaning no users)
|
|
|
|
+# - username: required (as it is key)
|
|
|
|
+# password: optional, defaults to "secret"
|
|
|
|
+# fullname: optional, set to username if empty
|
|
|
|
+# email: optional, set to username@example.com if empty
|
|
|
|
+#
|
|
|
|
+# NOTE: Must have an operator deployed in that namespace prior (use deploy-operators role for that).
|
|
|
|
+#
|
|
|
|
+- name: Tech hack. Prevent anything from blowing up because rhbk is defined somewhere, but not its structured contents.
|
|
|
|
+ ansible.builtin.set_fact:
|
|
|
|
+ rhbk:
|
|
|
|
+ db: {}
|
|
|
|
+ when:
|
|
|
|
+ - rhbk.db is not defined
|
|
|
|
+
|
|
|
|
+- name: Ensure there is a secret containing DB credentials in the project.
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: v1
|
|
|
|
+ kind: secret
|
|
|
|
+ namespace: "{{ rhbk.namespace | default('keycloak') }}"
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-db-auth"
|
|
|
|
+ resource_definition:
|
|
|
|
+ data:
|
|
|
|
+ username: "{{ rhbk.db.username | default('rhbk') | b64encode }}"
|
|
|
|
+ password: "{{ rhbk.db.password | default('secret') | b64encode }}"
|
|
|
|
+
|
|
|
|
+# TODO: ensure that there is no STS to begin with, or if there is, verify that
|
|
|
|
+# only the allowed fields would change, if anything. Otherwise:
|
|
|
|
+#
|
|
|
|
+# Forbidden: updates to statefulset spec for fields other than
|
|
|
|
+# 'replicas', 'ordinals', 'template', 'updateStrategy',
|
|
|
|
+# 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are
|
|
|
|
+# forbidden
|
|
|
|
+#
|
|
|
|
+- name: Ensure there is a database sts in the project
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: apps/v1
|
|
|
|
+ kind: statefulset
|
|
|
|
+ namespace: "{{ rhbk.namespace | default('keycloak') }}"
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-db"
|
|
|
|
+ resource_definition:
|
|
|
|
+ spec:
|
|
|
|
+ # TODO: implement rhbk.db.replicas at some point?
|
|
|
|
+ replicas: 1
|
|
|
|
+ serviceName: "{{ rhbk.name | default('sso') }}-db"
|
|
|
|
+ selector:
|
|
|
|
+ matchLabels:
|
|
|
|
+ app: "{{ rhbk.name | default('sso') }}-db"
|
|
|
|
+ template:
|
|
|
|
+ metadata:
|
|
|
|
+ labels:
|
|
|
|
+ app: "{{ rhbk.name | default('sso') }}-db"
|
|
|
|
+ spec:
|
|
|
|
+ containers:
|
|
|
|
+ - name: "{{ rhbk.name | default('sso') }}-db"
|
|
|
|
+ image: "{{ rhbk.db.image | default('registry.redhat.io/rhel9/postgresql-15:latest') }}"
|
|
|
|
+ volumeMounts:
|
|
|
|
+ - mountPath: /var/lib/pgsql/data
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-db-data"
|
|
|
|
+ env:
|
|
|
|
+ - name: POSTGRESQL_USER
|
|
|
|
+ valueFrom:
|
|
|
|
+ secretKeyRef:
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-db-auth"
|
|
|
|
+ key: username
|
|
|
|
+ - name: POSTGRESQL_PASSWORD
|
|
|
|
+ valueFrom:
|
|
|
|
+ secretKeyRef:
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-db-auth"
|
|
|
|
+ key: password
|
|
|
|
+ - name: POSTGRESQL_DATABASE
|
|
|
|
+ value: "{{ rhbk.db.name | default('rhbk') }}"
|
|
|
|
+ volumeClaimTemplates:
|
|
|
|
+ - metadata:
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-db-data"
|
|
|
|
+ spec:
|
|
|
|
+ accessModes: "{{ rhbk.db.claim_modes | default(['ReadWriteOnce']) }}"
|
|
|
|
+ storageClassName: "{{ rhbk.db.storage_class | default(omit) }}"
|
|
|
|
+ resources:
|
|
|
|
+ requests:
|
|
|
|
+ storage: "{{ rhbk.db.size | default('1Gi') }}"
|
|
|
|
+
|
|
|
|
+- name: Ensure there is a service for the database as well
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: v1
|
|
|
|
+ kind: service
|
|
|
|
+ namespace: "{{ rhbk.namespace | default('keycloak') }}"
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-db"
|
|
|
|
+ resource_definition:
|
|
|
|
+ spec:
|
|
|
|
+ selector:
|
|
|
|
+ app: "{{ rhbk.name | default('sso') }}-db"
|
|
|
|
+ type: LoadBalancer
|
|
|
|
+ ports:
|
|
|
|
+ - port: 5432
|
|
|
|
+ targetPort: 5432
|
|
|
|
+ protocol: TCP
|
|
|
|
+
|
|
|
|
+- name: Ensure there is a secret containing Keycloak bootstrap credentials
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: v1
|
|
|
|
+ kind: secret
|
|
|
|
+ namespace: "{{ rhbk.namespace | default('keycloak') }}"
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-auth"
|
|
|
|
+ resource_definition:
|
|
|
|
+ data:
|
|
|
|
+ username: "{{ rhbk.admin.username | default('rhbk') | b64encode }}"
|
|
|
|
+ password: "{{ rhbk.admin.password | default('secret') | b64encode }}"
|
|
|
|
+
|
|
|
|
+- name: If there is no FQDN, check what the default domain is.
|
|
|
|
+ kubernetes.core.k8s_info:
|
|
|
|
+ kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: operator.openshift.io/v1
|
|
|
|
+ kind: ingresscontroller
|
|
|
|
+ namespace: openshift-ingress-operator
|
|
|
|
+ name: default
|
|
|
|
+ register: default_ingress
|
|
|
|
+ when: rhbk.fqdn is not defined
|
|
|
|
+
|
|
|
|
+- name: Set a fact that reflects either the FQDN as set, or a composition of vars and default ingress info.
|
|
|
|
+ ansible.builtin.set_fact:
|
|
|
|
+ rhbk_fqdn: "{{ rhbk.fqdn | default((rhbk.name | default('sso')) + '-' + (rhbk.namespace | default('keycloak')) + '.' + default_ingress.resources[0].status.domain) }}"
|
|
|
|
+
|
|
|
|
+- name: Announce what hostname would be used.
|
|
|
|
+ ansible.builtin.debug:
|
|
|
|
+ msg: Using "https://{{ rhbk_fqdn }}" as the hostname.
|
|
|
|
+
|
|
|
|
+# TODO: remember if there were changes, and force delete any non-ready pod?
|
|
|
|
+- name: Lastly, make sure there is a Keycloak
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: k8s.keycloak.org/v2alpha1
|
|
|
|
+ kind: keycloak
|
|
|
|
+ namespace: "{{ rhbk.namespace | default('keycloak') }}"
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}"
|
|
|
|
+ resource_definition:
|
|
|
|
+ spec:
|
|
|
|
+ instances: "{{ rhbk.replicas | default(1) }}"
|
|
|
|
+ db:
|
|
|
|
+ vendor: postgres
|
|
|
|
+ host: "{{ rhbk.name | default('sso') }}-db"
|
|
|
|
+ database: "{{ rhbk.db.name | default('rhbk') }}"
|
|
|
|
+ usernameSecret:
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-db-auth"
|
|
|
|
+ key: username
|
|
|
|
+ passwordSecret:
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-db-auth"
|
|
|
|
+ key: password
|
|
|
|
+ hostname:
|
|
|
|
+ hostname: "https://{{ rhbk_fqdn }}"
|
|
|
|
+ strict: false
|
|
|
|
+ backchannelDynamic: true
|
|
|
|
+ http:
|
|
|
|
+ httpEnabled: true
|
|
|
|
+ httpPort: 8080
|
|
|
|
+ httpsPort: 8443
|
|
|
|
+ tlsSecret: "{{ rhbk.name | default('sso') }}-tls"
|
|
|
|
+ bootstrapAdmin:
|
|
|
|
+ user:
|
|
|
|
+ secret: "{{ rhbk.name | default('sso') }}-auth"
|
|
|
|
+ ingress:
|
|
|
|
+ enabled: false
|
|
|
|
+
|
|
|
|
+- name: Wait for the service to show up.
|
|
|
|
+ kubernetes.core.k8s_info:
|
|
|
|
+ kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: v1
|
|
|
|
+ kind: service
|
|
|
|
+ namespace: "{{ rhbk.namespace | default('keycloak') }}"
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-service"
|
|
|
|
+ register: rhbk_svc
|
|
|
|
+ until:
|
|
|
|
+ - rhbk_svc.resources is defined
|
|
|
|
+ - (rhbk_svc.resources | length) > 0
|
|
|
|
+ retries: 24
|
|
|
|
+ delay: 5
|
|
|
|
+
|
|
|
|
+- name: Ensure the service is correctly annotated.
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: v1
|
|
|
|
+ kind: service
|
|
|
|
+ namespace: "{{ rhbk.namespace | default('keycloak') }}"
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-service"
|
|
|
|
+ state: patched
|
|
|
|
+ resource_definition:
|
|
|
|
+ metadata:
|
|
|
|
+ annotations:
|
|
|
|
+ service.beta.openshift.io/serving-cert-secret-name: "{{ rhbk.name | default('sso') }}-tls"
|
|
|
|
+
|
|
|
|
+- name: Make sure there is a re-encrypt route.
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: route.openshift.io/v1
|
|
|
|
+ kind: route
|
|
|
|
+ namespace: "{{ rhbk.namespace | default('keycloak') }}"
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}"
|
|
|
|
+ resource_definition:
|
|
|
|
+ spec:
|
|
|
|
+ to:
|
|
|
|
+ kind: Service
|
|
|
|
+ name: "{{ rhbk.name | default('sso') }}-service"
|
|
|
|
+ port:
|
|
|
|
+ targetPort: 8443
|
|
|
|
+ tls:
|
|
|
|
+ termination: reencrypt
|
|
|
|
+...
|