main.yml 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. ---
  2. # Ensures there is an instance of RHBoK running in a configurable namespace.
  3. #
  4. # Configures it with a realm, and some users.
  5. #
  6. # Required variables (some are supplied by defaults/main.yml):
  7. #
  8. # rhbk:
  9. # namespace: namespace to deploy to (keycloak)
  10. # name: name of the instance (sso)
  11. # replicas: how many instances
  12. # fqdn: fqdn of the route (hostname), detected if omitted
  13. # admin: bootstrap admin credentials
  14. # username: username (rhbk)
  15. # password: password (secret)
  16. # db: database-specific settings
  17. # image: db server image
  18. # name: database name (rhbk)
  19. # username: database owner (rhbk)
  20. # password: db owner's password (secret)
  21. # claim_modes: volume claim template access modes, list (ReadWriteOnce)
  22. # storage_class: storage class name, no default (omitted)
  23. # size: pvc size (1Gi)
  24. # replicas: how many instances (TODO ignored for now)
  25. # realm: name of the realm (sample-realm)
  26. # users: users to create in realm, no default (meaning no users)
  27. # - username: required (as it is key)
  28. # password: optional, defaults to "secret"
  29. # fullname: optional, set to username if empty
  30. # email: optional, set to username@example.com if empty
  31. #
  32. # NOTE: Must have an operator deployed in that namespace prior (use deploy-operators role for that).
  33. #
  34. - name: Tech hack. Prevent anything from blowing up because rhbk is defined somewhere, but not its structured contents.
  35. ansible.builtin.set_fact:
  36. rhbk: "{{ rhbk | default({}) | combine({ 'db': {} }) }}"
  37. when:
  38. - rhbk.db is not defined
  39. - name: Ensure there is a secret containing DB credentials in the project.
  40. kubernetes.core.k8s:
  41. kubeconfig: tmp/kubeconfig-ocp4
  42. validate_certs: no
  43. api_version: v1
  44. kind: secret
  45. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  46. name: "{{ rhbk.name | default('sso') }}-db-auth"
  47. resource_definition:
  48. data:
  49. username: "{{ rhbk.db.username | default('rhbk') | b64encode }}"
  50. password: "{{ rhbk.db.password | default('secret') | b64encode }}"
  51. # TODO: ensure that there is no STS to begin with, or if there is, verify that
  52. # only the allowed fields would change, if anything. Otherwise:
  53. #
  54. # Forbidden: updates to statefulset spec for fields other than
  55. # 'replicas', 'ordinals', 'template', 'updateStrategy',
  56. # 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are
  57. # forbidden
  58. #
  59. - name: Ensure there is a database sts in the project
  60. kubernetes.core.k8s:
  61. kubeconfig: tmp/kubeconfig-ocp4
  62. validate_certs: no
  63. api_version: apps/v1
  64. kind: statefulset
  65. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  66. name: "{{ rhbk.name | default('sso') }}-db"
  67. resource_definition:
  68. spec:
  69. # TODO: implement rhbk.db.replicas at some point?
  70. replicas: 1
  71. serviceName: "{{ rhbk.name | default('sso') }}-db"
  72. selector:
  73. matchLabels:
  74. app: "{{ rhbk.name | default('sso') }}-db"
  75. template:
  76. metadata:
  77. labels:
  78. app: "{{ rhbk.name | default('sso') }}-db"
  79. spec:
  80. containers:
  81. - name: "{{ rhbk.name | default('sso') }}-db"
  82. image: "{{ rhbk.db.image | default('registry.redhat.io/rhel9/postgresql-15:latest') }}"
  83. volumeMounts:
  84. - mountPath: /var/lib/pgsql/data
  85. name: "{{ rhbk.name | default('sso') }}-db-data"
  86. env:
  87. - name: POSTGRESQL_USER
  88. valueFrom:
  89. secretKeyRef:
  90. name: "{{ rhbk.name | default('sso') }}-db-auth"
  91. key: username
  92. - name: POSTGRESQL_PASSWORD
  93. valueFrom:
  94. secretKeyRef:
  95. name: "{{ rhbk.name | default('sso') }}-db-auth"
  96. key: password
  97. - name: POSTGRESQL_DATABASE
  98. value: "{{ rhbk.db.name | default('rhbk') }}"
  99. volumeClaimTemplates:
  100. - metadata:
  101. name: "{{ rhbk.name | default('sso') }}-db-data"
  102. spec:
  103. accessModes: "{{ rhbk.db.claim_modes | default(['ReadWriteOnce']) }}"
  104. storageClassName: "{{ rhbk.db.storage_class | default(omit) }}"
  105. resources:
  106. requests:
  107. storage: "{{ rhbk.db.size | default('1Gi') }}"
  108. - name: Ensure there is a service for the database as well
  109. kubernetes.core.k8s:
  110. kubeconfig: tmp/kubeconfig-ocp4
  111. validate_certs: no
  112. api_version: v1
  113. kind: service
  114. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  115. name: "{{ rhbk.name | default('sso') }}-db"
  116. resource_definition:
  117. spec:
  118. selector:
  119. app: "{{ rhbk.name | default('sso') }}-db"
  120. type: LoadBalancer
  121. ports:
  122. - port: 5432
  123. targetPort: 5432
  124. protocol: TCP
  125. - name: Ensure there is a secret containing Keycloak bootstrap credentials
  126. kubernetes.core.k8s:
  127. kubeconfig: tmp/kubeconfig-ocp4
  128. validate_certs: no
  129. api_version: v1
  130. kind: secret
  131. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  132. name: "{{ rhbk.name | default('sso') }}-auth"
  133. resource_definition:
  134. data:
  135. username: "{{ rhbk.admin.username | default('rhbk') | b64encode }}"
  136. password: "{{ rhbk.admin.password | default('secret') | b64encode }}"
  137. - name: If there is no FQDN, check what the default domain is.
  138. kubernetes.core.k8s_info:
  139. kubeconfig: tmp/kubeconfig-ocp4
  140. validate_certs: no
  141. api_version: operator.openshift.io/v1
  142. kind: ingresscontroller
  143. namespace: openshift-ingress-operator
  144. name: default
  145. register: default_ingress
  146. when: rhbk.fqdn is not defined
  147. - name: Set a fact that reflects either the FQDN as set, or a composition of vars and default ingress info.
  148. ansible.builtin.set_fact:
  149. rhbk_fqdn: "{{ rhbk.fqdn | default((rhbk.name | default('sso')) + '-' + (rhbk.namespace | default('keycloak')) + '.' + default_ingress.resources[0].status.domain) }}"
  150. - name: Announce what hostname would be used.
  151. ansible.builtin.debug:
  152. msg: Using "https://{{ rhbk_fqdn }}" as the hostname.
  153. # TODO: remember if there were changes, and force delete any non-ready pod?
  154. - name: Lastly, make sure there is a Keycloak
  155. kubernetes.core.k8s:
  156. kubeconfig: tmp/kubeconfig-ocp4
  157. validate_certs: no
  158. api_version: k8s.keycloak.org/v2alpha1
  159. kind: keycloak
  160. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  161. name: "{{ rhbk.name | default('sso') }}"
  162. resource_definition:
  163. spec:
  164. instances: "{{ rhbk.replicas | default(1) }}"
  165. db:
  166. vendor: postgres
  167. host: "{{ rhbk.name | default('sso') }}-db"
  168. database: "{{ rhbk.db.name | default('rhbk') }}"
  169. usernameSecret:
  170. name: "{{ rhbk.name | default('sso') }}-db-auth"
  171. key: username
  172. passwordSecret:
  173. name: "{{ rhbk.name | default('sso') }}-db-auth"
  174. key: password
  175. hostname:
  176. hostname: "https://{{ rhbk_fqdn }}"
  177. strict: false
  178. backchannelDynamic: true
  179. http:
  180. httpEnabled: true
  181. httpPort: 8080
  182. httpsPort: 8443
  183. tlsSecret: "{{ rhbk.name | default('sso') }}-tls"
  184. bootstrapAdmin:
  185. user:
  186. secret: "{{ rhbk.name | default('sso') }}-auth"
  187. ingress:
  188. enabled: false
  189. - name: Wait for the service to show up.
  190. kubernetes.core.k8s_info:
  191. kubeconfig: tmp/kubeconfig-ocp4
  192. validate_certs: no
  193. api_version: v1
  194. kind: service
  195. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  196. name: "{{ rhbk.name | default('sso') }}-service"
  197. register: rhbk_svc
  198. until:
  199. - rhbk_svc.resources is defined
  200. - (rhbk_svc.resources | length) > 0
  201. retries: 24
  202. delay: 5
  203. - name: Ensure the service is correctly annotated.
  204. kubernetes.core.k8s:
  205. kubeconfig: tmp/kubeconfig-ocp4
  206. validate_certs: no
  207. api_version: v1
  208. kind: service
  209. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  210. name: "{{ rhbk.name | default('sso') }}-service"
  211. state: patched
  212. resource_definition:
  213. metadata:
  214. annotations:
  215. service.beta.openshift.io/serving-cert-secret-name: "{{ rhbk.name | default('sso') }}-tls"
  216. - name: Make sure there is a re-encrypt route.
  217. kubernetes.core.k8s:
  218. kubeconfig: tmp/kubeconfig-ocp4
  219. validate_certs: no
  220. api_version: route.openshift.io/v1
  221. kind: route
  222. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  223. name: "{{ rhbk.name | default('sso') }}"
  224. resource_definition:
  225. spec:
  226. to:
  227. kind: Service
  228. name: "{{ rhbk.name | default('sso') }}-service"
  229. port:
  230. targetPort: 8443
  231. tls:
  232. termination: reencrypt
  233. insecureEdgeTerminationPolicy: Redirect
  234. - name: Check whether there is already a realm import CR
  235. kubernetes.core.k8s_info:
  236. kubeconfig: tmp/kubeconfig-ocp4
  237. validate_certs: no
  238. api_version: k8s.keycloak.org/v2alpha1
  239. kind: keycloakrealmimport
  240. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  241. register: realm_imports
  242. - name: If there are no realm imports, create one.
  243. block:
  244. - name: Apply a template realm import.
  245. kubernetes.core.k8s:
  246. kubeconfig: tmp/kubeconfig-ocp4
  247. validate_certs: no
  248. api_version: k8s.keycloak.org/v2alpha1
  249. kind: keycloakrealmimport
  250. namespace: "{{ rhbk.namespace | default('keycloak') }}"
  251. name: "{{ rhbk.name | default('sso') }}-{{ rhbk.realm | default('sample-realm') }}-import"
  252. template: templates/realm-import-template.yaml.j2
  253. ...