瀏覽代碼

add initial rhbk deployment role and vars

Grega Bremec 1 月之前
父節點
當前提交
0d78520522

+ 11 - 0
playbooks/inventory.yml

@@ -14,6 +14,17 @@ all:
     ocp_maj: "4.16"
     ocp_z: "4.16.2"
 
+    rhbk:
+      namespace: keycloak
+      realm: ichp
+      users:
+        - username: johndoe
+          password: redhat
+          fullname: John Doe
+        - username: janedoe
+          password: redhat
+          fullname: Jane Doe
+
     # Operators that need to be (re)installed.
     #
     # The first item in the list is also used for checking whether catalog(s)

+ 10 - 0
playbooks/pre-flight.yml

@@ -58,4 +58,14 @@
       tags:
         - prep
         - setup
+    # Ensure a Keycloak is there.
+    - include_role:
+        name: deploy-rhbk
+        apply:
+          tags:
+            - prep
+            - sso
+      tags:
+        - prep
+        - sso
 ...

+ 15 - 0
playbooks/roles/deploy-rhbk/defaults/main.yml

@@ -0,0 +1,15 @@
+---
+# Defaults for the deploy-rhbk role.
+#rhbk:
+#  namespace: keycloak
+#  name: sso
+#  db:
+#    image: registry.redhat.io/rhel9/postgresql-15:latest
+#    name: rhbk
+#    username: rhbk
+#    password: secret
+#    claim_modes:
+#      - ReadWriteOnce
+#    size: 1Gi
+#  realm: sample-realm
+...

+ 245 - 0
playbooks/roles/deploy-rhbk/tasks/main.yml

@@ -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
+...