diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e09ee26..cafc1eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,8 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Checkout code + uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v1 with: @@ -35,4 +36,18 @@ jobs: env: ANSIBLE_GALAXY_API_KEY: ${{ secrets.ANSIBLE_GALAXY_API_KEY }} run: | - ansible-galaxy collection publish *.tar.gz --api-key $ANSIBLE_GALAXY_API_KEY \ No newline at end of file + ansible-galaxy collection publish *.tar.gz --api-key $ANSIBLE_GALAXY_API_KEY + dispatch: + needs: release + strategy: + matrix: + repo: ['ansible-middleware/cross-dc-rhsso-demo', 'ansible-middleware/flange-demo'] + runs-on: ubuntu-latest + steps: + - name: Repository Dispatch + uses: peter-evans/repository-dispatch@v1 + with: + token: ${{ secrets.TRIGGERING_PAT }} + repository: ${{ matrix.repo }} + event-type: "Dependency released - Keycloak" + client-payload: '{ "github": ${{toJson(github)}} }' diff --git a/.gitignore b/.gitignore index c32b546..c50fe52 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -*.tar.gz \ No newline at end of file +*.tar.gz +*.zip diff --git a/README.md b/README.md index 93e41eb..caafef7 100644 --- a/README.md +++ b/README.md @@ -29,60 +29,87 @@ collections: - name: middleware_automation.keycloak ``` +The keycloak collection also depends on the following python packages to be present on the controller host: + +* netaddr + +A requirement file is provided to install: + + pip install -r requirements.txt + + +### Included roles + +* [`keycloak`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md): role for installing the service. +* [`keycloak_realm`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md): role for configuring a realm, user federation(s), clients and users, in an installed service. + + +## Usage + + ### Install Playbook -`playbooks/keycloak.yml` installs the upstream(Keycloak) based on the defined variables. -`playbooks/rhsso.yml` installs Red Hat Single Sign-On(RHSSO) based on defined variables. +* [`playbooks/keycloak.yml`](playbooks/keycloak.yml) installs the upstream(Keycloak) based on the defined variables. +* [`playbooks/rhsso.yml`](playbooks/rhsso.yml) installs Red Hat Single Sign-On(RHSSO) based on defined variables. -### Choosing between upstream(Keycloak) project and Red Hat Single Sign-On(RHSSO) +Both playbooks include the `keycloak` role, with different settings, as described in the following sections. -The roles supports installing upstream(Keycloak) or Red Hat Single Sign-On in the following ways +For service configuration details, refer to the [keycloak role README](roles/keycloak/README.md). -#### Install upstream(Keycloak) from remote source -This is default approach, there is one required variable +### Choosing between upstream project (Keycloak) and Red Hat Single Sign-On (RHSSO) -``` -keycloak_admin_password: "" -``` +The general flag `keycloak_rhsso_enable` controls what to install between upstream(Keycloak, when `False`) or Red Hat Single Sign-On (when `True`). +The default value for the flag if `True` when Red Hat Network credentials are defined, `False` otherwise. -#### Install upstream(Keycloak) from local source when the following variable is defined -``` -keycloak_admin_password: "" -zip_file_local_path: -``` +#### Install upstream (Keycloak) from keycloak releases -#### Install RHSSO from the Red Hat Customer Support Portal, when the following variables are defined +This is the default approach when RHN credentials are not defined. Keycloak is downloaded from keycloak builds (hosted on github.com) locally, and distributed to target nodes. -``` -keycloak_admin_password: "" + +#### Install RHSSO from the Red Hat Customer Support Portal + +Define the credentials as follows, and the default behaviour is to download a fresh archive of RHSSO on the controller node, then distribute to target nodes. + +```yaml rhn_username: '' rhn_password: '' -rhsso_rhn_id: '' +# (keycloak_rhsso_enable defaults to True) ``` -where `sso_product_id` is the ID for the specific Red Hat Single Sign-On version, ie. _101971_ will install version _7.5_) -#### Install RHSSO from remote sources like Nexus etc, when the following variables are defined +#### Install from controller node (local source) +Making the keycloak zip archive (or the RHSSO zip archive), available to the playbook repository root directory, and setting `keycloak_offline_install` to `True`, allows to skip +the download tasks. The local path for the archive matches the downloaded archive path, so it is also used as a cache when multiple hosts are provisioned in a cluster. + +```yaml +keycloak_offline_install: True ``` -keycloak_admin_password: "" + +And depending on `keycloak_rhsso_enable`: + +* `True`: install RHSSO using file rh-sso-x.y.z-server-dist.zip +* `False`: install keycloak using file keycloak-x.y.zip + + +#### Install from alternate sources (like corporate Nexus, artifactory, proxy, etc) + +For RHSSO: + +```yaml keycloak_rhsso_enable: True -rhsso_source_download_url: '' +keycloak_rhsso_download_url: "https://///rh-sso-x.y.z-server-dist.zip" ``` -#### Install RHSSO from local source when the following variable is defined +For keycloak: -``` -keycloak_admin_password: "" -keycloak_rhsso_enable: True -zip_file_local_path: +```yaml +keycloak_rhsso_enable: False +keycloak_download_url: "https://///keycloak-x.y.zip" ``` -### Install role - -* [`keycloak`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md): role for installing the service. _Requires: python3-netaddr_ ### Example installation command @@ -100,21 +127,20 @@ ansible-playbook -i -e @rhn-creds.yml playbooks/keycloak.yml -e localhost ansible_connection=local ``` + ## Configuration + ### Config Playbook -`playbooks/keycloak-realm.yml` creates provided realm, user federation(s), client(s), client role(s) and client user(s) if they don't exist. +[`playbooks/keycloak-realm.yml`](playbooks/keycloak-realm.yml) creates provided realm, user federation(s), client(s), client role(s) and client user(s) if they don't exist. -### Config role - -* [`keycloak_realm`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md): role for configuring a realm, user federation(s), clients and users, in an installed service. ### Example configuration command Execute the following command from the source root directory -``` +```bash ansible-playbook -i playbooks/keycloak-realm.yml -e keycloak_admin_password= -e keycloak_realm=test ``` @@ -127,9 +153,12 @@ ansible-playbook -i playbooks/keycloak-realm.yml -e keycloak_adm localhost ansible_connection=local ``` +For configuration details, refer to the [keycloak_realm role README](roles/keycloak_realm/README.md). + + ## License Apache License v2.0 or later -See [LICENCE](LICENSE) to view the full text. +See [LICENSE](LICENSE) to view the full text. diff --git a/galaxy.yml b/galaxy.yml index ecf8d7e..5266e28 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,6 +1,6 @@ namespace: middleware_automation name: keycloak -version: "0.1.9" +version: "0.2.0" readme: README.md authors: - Romain Pelisse diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b2366a5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +################################################# +# python dependencies required to be installed +# on the controller host with: +# pip install -r requirements.txt +# +netaddr \ No newline at end of file diff --git a/roles/keycloak/README.md b/roles/keycloak/README.md index 93a1a8f..3dffb47 100644 --- a/roles/keycloak/README.md +++ b/roles/keycloak/README.md @@ -11,6 +11,16 @@ This role requires the `python3-netaddr` library installed on the controller nod * to install via yum/dnf: `dnf install python3-netaddr` * or via pip: `pip install netaddr==0.8.0` +* or via the collection: `pip install -r requirements.txt` + + +Dependencies +------------ + +The roles depends on: + +* the `redhat_csp_download` role from [middleware_automation.redhat_csp_download](https://github.com/ansible-middleware/redhat-csp-download) collection if Red Hat Single Sign-on zip have to be downloaded from RHN. +* the `wildfly_driver` role from [middleware_automation.wildfly](https://github.com/ansible-middleware/wildfly) collection Versions @@ -24,9 +34,10 @@ Versions Role Defaults ------------- +* Service configuration + | Variable | Description | Default | |:---------|:------------|:---------| -|`keycloak_rhsso_enable`| Enable Red Hat Single Sign-on installation | `False` | |`keycloak_ha_enabled`| Enable auto configuration for database backend, clustering and remote caches on infinispan | `False` | |`keycloak_db_enabled`| Enable auto configuration for database backend | `True` if `keycloak_ha_enabled` is True, else `False` | |`keycloak_admin_user`| Administration console user account | `admin` | @@ -34,13 +45,32 @@ Role Defaults |`keycloak_host`| hostname | `localhost` | |`keycloak_http_port`| HTTP port | `8080` | |`keycloak_https_port`| TLS HTTP port | `8443` | +|`keycloak_ajp_port`| AJP port | `8009` | +|`keycloak_jgroups_port`| jgroups cluster tcp port | `7600` | |`keycloak_management_http_port`| Management port | `9990` | |`keycloak_management_https_port`| TLS management port | `9993` | |`keycloak_java_opts`| Additional JVM options | `-Xms1024m -Xmx2048m` | |`keycloak_prefer_ipv4`| Prefer IPv4 stack and addresses for port binding | `True` | +|`keycloak_config_standalone_xml`| filename for configuration | `keycloak.xml` | +|`keycloak_service_user`| posix account username | `keycloak` | +|`keycloak_service_group`| posix account group | `keycloak` | +|`keycloak_service_pidfile`| pid file path for service | `/run/keycloak.pid` | |`jvm_package`| RHEL java package runtime | `java-1.8.0-openjdk-devel` | +* Install options + +| Variable | Description | Default | +|:---------|:------------|:---------| +|`keycloak_rhsso_enable`| Enable Red Hat Single Sign-on installation | `False` | +|`keycloak_offline_install` | perform an offline install | `False`| +|`keycloak_download_url`| Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download//`| +|`keycloak_rhsso_download_url`| Download URL for RHSSO | `https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId=`| +|`keycloak_version`| keycloak.org package version | `15.0.2` | +|`keycloak_rhsso_version`| RHSSO version | `7.5.0` | +|`keycloak_dest`| Installation root path | `/opt/keycloak` | + + Role Variables -------------- @@ -76,29 +106,14 @@ The following variables are _required_ only when `keycloak_db_enabled` is True: |`keycloak_db_user` | username for connecting to postgres | `keycloak-user` | |`keycloak_db_pass` | password for connecting to postgres | `keycloak-pass` | -The following variable can be used to install Keycloak or Red Hat Single Sign-On from local path: -| Variable | Description | Example | -|:---------|:------------|:---------| -|`zip_file_local_path` | Full local path of upstream(Keycloak) or Red Hat Single Sign-On zip file on Ansible control plane | `tmp/rhsso/rh-sso-7.5-server-dist.zip` | -The following variable can be used to install Red Hat Single Sign-On from source via url, auth support is not added right now. -| Variable | Description | Example | -|:---------|:------------|:---------| -|`rhsso_source_download_url` | URL to download Red Hat Single Sign-On zip file from source | `http://localhost:8081/nexus/rhsso/rh-sso-7.5-server-dist.zip` | +Example Playbooks +----------------- -Dependencies ------------- - -The roles depends on: - -* the redhat_csp_download role from [middleware_automation.redhat_csp_download](https://github.com/ansible-middleware/redhat-csp-download) collection if Red Hat Single Sign-on zip have to be downloaded from RHN. -* the wildfly_driver role from [middleware_automation.wildfly](https://github.com/ansible-middleware/wildfly) collection +_NOTE_: use ansible vaults or other security systems for storing credentials. -Example Playbook ----------------- - -The following is an example playbook that makes use of the role to install keycloak from remote +* The following is an example playbook that makes use of the role to install keycloak from remote: ```yaml --- @@ -113,23 +128,7 @@ The following is an example playbook that makes use of the role to install keycl keycloak_admin_password: "changeme" ``` -The following is an example playbook that makes use of the role to install keycloak from local path on Ansible node - -```yaml ---- -- hosts: ... - collections: - - middleware_automation.keycloak - tasks: - - name: Include keycloak role - include_role: - name: keycloak - vars: - keycloak_admin_password: "changeme" - zip_file_local_path: "/tmp/keycloak/keycloak-16.1.0.zip" # This should be local path on Ansible node of upstream(keycloak) zip file -``` - -The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from RHN +* The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from RHN: ```yaml --- @@ -146,9 +145,30 @@ The following is an example playbook that makes use of the role to install Red H vars: keycloak_admin_password: "changeme" keycloak_rhsso_enable: True + rhn_username: '' + rhn_password: '' ``` -The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from source url + +* The following example playbook makes use of the role to install keycloak from the controller node: + +```yaml +--- +- hosts: ... + collections: + - middleware_automation.keycloak + tasks: + - name: Include keycloak role + include_role: + name: keycloak + vars: + keycloak_admin_password: "changeme" + keycloak_offline_install: True + # This should be the filename of keycloak archive on Ansible node: keycloak-16.1.0.zip +``` + + +* This playbook installs Red Hat Single Sign-On from an alternate url: ```yaml --- @@ -162,10 +182,12 @@ The following is an example playbook that makes use of the role to install Red H vars: keycloak_admin_password: "changeme" keycloak_rhsso_enable: True - rhsso_source_download_url: "" # This should be the full of remote source rhsso zip file + keycloak_rhsso_download_url: "" + # This should be the full of remote source rhsso zip file and can contain basic authentication credentials ``` -The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from local path on Ansible node + +* The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from the controller node: ```yaml --- @@ -179,7 +201,8 @@ The following is an example playbook that makes use of the role to install Red H vars: keycloak_admin_password: "changeme" keycloak_rhsso_enable: True - zip_file_local_path: "/tmp/rhsso/rh-sso-7.5-server-dist.zip" # This should be local path on Ansible node of rhsso zip file + keycloak_offline_install: True + # This should be the filename of rhsso zip file on Ansible node: rh-sso-7.5-server-dist.zip ``` License diff --git a/roles/keycloak/defaults/main.yml b/roles/keycloak/defaults/main.yml index de619ac..a4af3fe 100644 --- a/roles/keycloak/defaults/main.yml +++ b/roles/keycloak/defaults/main.yml @@ -7,31 +7,36 @@ keycloak_download_url_9x: "https://downloads.jboss.org/keycloak/{{ keycloak_vers keycloak_installdir: "{{ keycloak_dest }}/keycloak-{{ keycloak_version }}" ### Configuration specific to Red Hat Single Sing-On -keycloak_rhsso_enable: False -keycloak_rhsso_version: 7.5 +keycloak_rhsso_version: 7.5.0 +rhsso_rhn_id: "{{ rhsso_rhn_ids[keycloak_rhsso_version] }}" keycloak_rhsso_archive: "rh-sso-{{ keycloak_rhsso_version }}-server-dist.zip" -keycloak_rhsso_installdir: "{{ keycloak_dest }}/rh-sso-{{ keycloak_rhsso_version }}" -keycloak_rhsso_base_url: 'https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId=' +keycloak_rhsso_installdir: "{{ keycloak_dest }}/rh-sso-{{ keycloak_rhsso_version | regex_replace('^([0-9])\\.([0-9]*).*', '\\1.\\2') }}" +keycloak_rhn_url: 'https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId=' +keycloak_rhsso_download_url: "{{ keycloak_rhn_url }}{{ rhsso_rhn_id }}" + +### keycloak/rhsso choice: by default install rhsso if rhn credentials are defined +keycloak_rhsso_enable: "{{ True if rhsso_rhn_id is defined and rhn_username is defined and rhn_password is defined else False }}" +# whether to install from local archive; filename must be keycloak_archive or keycloak_rhsso_archive depending on keycloak_rhsso_enable +keycloak_offline_install: False ### Install location and service settings jvm_package: java-1.8.0-openjdk-devel keycloak_dest: /opt/keycloak keycloak_jboss_home: "{{ keycloak_rhsso_installdir if keycloak_rhsso_enable else keycloak_installdir }}" keycloak_config_dir: "{{ keycloak_jboss_home }}/standalone/configuration" - keycloak_config_standalone_xml: "keycloak.xml" keycloak_config_path_to_standalone_xml: "{{ keycloak_jboss_home }}/standalone/configuration/{{ keycloak_config_standalone_xml }}" - keycloak_service_user: keycloak keycloak_service_group: keycloak keycloak_service_pidfile: "/run/keycloak.pid" -keycloak_service_logfile: "{{ keycloak_dest }}/keycloak.log" -### Keycloak configuration settings +### Common configuration settings keycloak_bind_address: 0.0.0.0 keycloak_host: localhost keycloak_http_port: 8080 keycloak_https_port: 8443 +keycloak_ajp_port: 8009 +keycloak_jgroups_port: 7600 keycloak_management_http_port: 9990 keycloak_management_https_port: 9993 keycloak_java_opts: "-Xms1024m -Xmx2048m" diff --git a/roles/keycloak/tasks/firewalld.yml b/roles/keycloak/tasks/firewalld.yml index 8757678..e05c58f 100644 --- a/roles/keycloak/tasks/firewalld.yml +++ b/roles/keycloak/tasks/firewalld.yml @@ -24,5 +24,5 @@ - "{{ keycloak_https_port }}/tcp" - "{{ keycloak_management_http_port }}/tcp" - "{{ keycloak_management_https_port }}/tcp" - - "7600/tcp" - - "8009/tcp" + - "{{ keycloak_jgroups_port }}/tcp" + - "{{ keycloak_ajp_port }}/tcp" diff --git a/roles/keycloak/tasks/get_rhsso.yml b/roles/keycloak/tasks/get_rhsso.yml deleted file mode 100644 index fa3fc2b..0000000 --- a/roles/keycloak/tasks/get_rhsso.yml +++ /dev/null @@ -1,104 +0,0 @@ ---- -- assert: - that: - - zipfile_dest is defined - - keycloak_rhsso_enable - quiet: true - -- set_fact: - rhn_download_url: "{{ keycloak_rhsso_base_url }}{{ rhsso_rhn_id }}" - when: - - rhsso_rhn_id is defined - -- name: "Check zipfile dest directory {{ zipfile_dest }}" - stat: - path: "{{ zipfile_dest }}" - register: archive_path - -- name: "Download zipfile from RHN: {{ rhn_download_url }}" - redhat_csp_download: - url: "{{ rhn_download_url }}" - dest: "{{ zipfile_dest }}" - username: "{{ rhn_username }}" - password: "{{ rhn_password }}" - no_log: "{{ omit_rhn_output | default(true) }}" - when: - - archive_path is defined - - archive_path.stat is defined - - not archive_path.stat.exists - - rhn_username is defined - - rhn_password is defined - - rhsso_rhn_id is defined - -- name: "Copy zipfile from source like Nexus etc : {{ rhsso_source_download_url }}" - get_url: - url: "{{ rhsso_source_download_url }}" - dest: "{{ zipfile_dest }}" - owner: "{{ keycloak_service_user }}" - group: "{{ keycloak_service_group }}" - mode: 0750 - when: - - archive_path is defined - - archive_path.stat is defined - - not archive_path.stat.exists - - rhsso_source_download_url is defined - -- name: "Copy zipfile from local source: {{ zip_file_local_path }}" - ansible.builtin.copy: - src: "{{ zip_file_local_path }}" - dest: "{{ zipfile_dest }}" - owner: "{{ keycloak_service_user }}" - group: "{{ keycloak_service_group }}" - mode: 0750 - when: - - archive_path is defined - - archive_path.stat is defined - - not archive_path.stat.exists - - zip_file_local_path is defined - -- name: "Check zipfile dest directory {{ zipfile_dest }}" - stat: - path: "{{ zipfile_dest }}" - register: path_to_downloaded_artifact - -- block: - - file: - path: "{{ work_dir }}" - state: directory - owner: "{{ keycloak_service_user }}" - group: "{{ keycloak_service_group }}" - mode: 0750 - - - name: "Check directory {{ target_dir }}" - stat: - path: "{{ target_dir }}" - register: target_dir_state - - - assert: - that: - - target_dir_state is defined - - target_dir_state.stat is defined - fail_msg: "Directory layout for {{ target_dir }} is invalid." - quiet: true - - - name: "Decompress {{ zipfile_dest }} into {{ work_dir }} (results in {{ target_dir }}." - unarchive: - src: "{{ zipfile_dest }}" - dest: "{{ work_dir }}" - owner: "{{ keycloak_service_user }}" - group: "{{ keycloak_service_user }}" - remote_src: yes - creates: "{{ target_dir }}" - when: - - not target_dir_state.stat.exists - - - debug: - msg: "{{ target_dir }} already exists, skipping decompressing {{ zipfile_dest }}" - when: - - target_dir_state.stat.exists - when: - - path_to_downloaded_artifact is defined - - path_to_downloaded_artifact.stat is defined - - path_to_downloaded_artifact.stat.exists - - target_dir is defined - - work_dir is defined diff --git a/roles/keycloak/tasks/install.yml b/roles/keycloak/tasks/install.yml index a346da5..af7e022 100644 --- a/roles/keycloak/tasks/install.yml +++ b/roles/keycloak/tasks/install.yml @@ -1,5 +1,6 @@ --- -- assert: +- name: Validate parameters + assert: that: - keycloak_jboss_home is defined - keycloak_service_user is defined @@ -9,25 +10,20 @@ - keycloak_version is defined quiet: true -- set_fact: - keycloak_service_group: "{{ keycloak_service_user }}" - when: - - not keycloak_service_group is defined - -- name: check for an existing deployment +- name: Check for an existing deployment become: yes stat: path: "{{ keycloak_jboss_home }}" register: existing_deploy - block: - - name: stop the old keycloak service + - name: Stop the old keycloak service become: yes ignore_errors: yes systemd: name: keycloak state: stopped - - name: remove the old Keycloak deployment + - name: Remove the old Keycloak deployment become: yes file: path: "{{ keycloak_jboss_home }}" @@ -56,75 +52,111 @@ group: "{{ keycloak_service_group }}" mode: 0750 -- block: - - set_fact: - archive: "{{ keycloak_dest }}/{{ keycloak_archive }}" - - name: "Check archive directory {{ archive }}" - stat: - path: "{{ archive }}" - register: archive_path +## check remote archive +- name: Set download archive path + set_fact: + archive: "{{ keycloak_dest }}/{{ keycloak.bundle }}" - - name: download Keycloak archive to target - get_url: - url: "{{ keycloak_download_url }}" - dest: "{{ keycloak_dest }}" - owner: "{{ keycloak_service_user }}" - group: "{{ keycloak_service_group }}" - when: - - archive_path is defined - - archive_path.stat is defined - - not archive_path.stat.exists - - not keycloak_rhsso_enable and not zip_file_local_path is defined +- name: Check download archive path + stat: + path: "{{ archive }}" + register: archive_path - - name: "Copy zipfile from local source: {{ zip_file_local_path }}" - ansible.builtin.copy: - src: "{{ zip_file_local_path }}" - dest: "{{ keycloak_dest }}" - owner: "{{ keycloak_service_user }}" - group: "{{ keycloak_service_group }}" - mode: 0750 - when: - - archive_path is defined - - archive_path.stat is defined - - not archive_path.stat.exists - - not keycloak_rhsso_enable and zip_file_local_path is defined +## download to controller +- name: Check load download archive path + stat: + path: "{{ lookup('env', 'PWD') }}" + register: local_path + delegate_to: localhost - - name: extract Keycloak archive on target - unarchive: - remote_src: yes - src: "{{ archive }}" - dest: "{{ keycloak_dest }}" - creates: "{{ keycloak_jboss_home }}" - owner: "{{ keycloak_service_user }}" - group: "{{ keycloak_service_group }}" - notify: - - restart keycloak +- name: Download keycloak archive + get_url: + url: "{{ keycloak_download_url }}" + dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" + delegate_to: localhost + when: + - archive_path is defined + - archive_path.stat is defined + - not archive_path.stat.exists + - not keycloak_rhsso_enable + - not keycloak_offline_install + +- name: Performing download from RHN + redhat_csp_download: + url: "{{ keycloak_rhsso_download_url }}" + dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" + username: "{{ rhn_username }}" + password: "{{ rhn_password }}" + no_log: "{{ omit_rhn_output | default(true) }}" + delegate_to: localhost + when: + - archive_path is defined + - archive_path.stat is defined + - not archive_path.stat.exists + - keycloak_rhsso_enable + - not keycloak_offline_install + - keycloak_rhn_url in keycloak_rhsso_download_url + +- name: Download rhsso archive from alternate location + get_url: + url: "{{ keycloak_rhsso_download_url }}" + dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" + delegate_to: localhost + when: + - archive_path is defined + - archive_path.stat is defined + - not archive_path.stat.exists + - keycloak_rhsso_enable + - not keycloak_offline_install + - not keycloak_rhn_url in keycloak_rhsso_download_url + +## copy and unpack +- name: Copy archive to target nodes + copy: + src: "{{ local_path.stat.path }}/{{ keycloak.bundle }}" + dest: "{{ archive }}" + owner: "{{ keycloak_service_user }}" + group: "{{ keycloak_service_group }}" + mode: 0750 + register: new_version_downloaded become: yes - when: not keycloak_rhsso_enable -- block: - - assert: - that: - - rhsso_rhn_id is defined or zip_file_local_path is defined - quiet: true - fail_msg: "Can't install RHSSO without either RHN ID or RHSSO zip file located on Ansible node" - - - name: create download directory - file: - path: /opt/apps - state: directory - owner: "{{ keycloak_service_user }}" - group: "{{ keycloak_service_group }}" - mode: 0750 - - - include_tasks: get_rhsso.yml - vars: - zipfile_dest: "{{ keycloak_dest }}/{{ keycloak_rhsso_archive }}" - work_dir: "{{ keycloak_dest }}" - target_dir: "{{ keycloak_jboss_home }}" +- name: "Check target directory: {{ keycloak.home }}" + stat: + path: "{{ keycloak.home }}" + register: path_to_workdir become: yes - when: keycloak_rhsso_enable +- name: "Extract {{ 'Red Hat Single Sign-On' if keycloak_rhsso_enable else 'Keycloak' }} archive on target" + unarchive: + remote_src: yes + src: "{{ archive }}" + dest: "{{ keycloak_dest }}" + creates: "{{ keycloak.home }}" + owner: "{{ keycloak_service_user }}" + group: "{{ keycloak_service_group }}" + become: yes + when: + - new_version_downloaded.changed or not path_to_workdir.stat.exists + notify: + - restart keycloak + +- name: Inform decompression was not executed + debug: + msg: "{{ keycloak.home }} already exists and version unchanged, skipping decompression" + when: + - not new_version_downloaded.changed and path_to_workdir.stat.exists + +- name: "Reown installation directory to {{ keycloak_service_user }}" + file: + path: "{{ keycloak.home }}" + owner: "{{ keycloak_service_user }}" + group: "{{ keycloak_service_group }}" + recurse: true + become: yes + changed_when: false + +# driver and configuration - name: "Install {{ keycloak_jdbc_engine }} driver" include_role: name: wildfly_driver @@ -139,7 +171,7 @@ jdbc_driver_module_name: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}" when: keycloak_jdbc[keycloak_jdbc_engine].enabled -- name: "Deploy Keycloak's standalone.xml" +- name: "Deploy {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }}" become: yes template: src: templates/standalone.xml.j2 @@ -151,7 +183,7 @@ - restart keycloak when: not keycloak_remotecache.enabled -- name: "Deploy Keycloak's standalone.xml with remote cache store" +- name: "Deploy {{ keycloak.service_name }} config with remote cache store to {{ keycloak_config_path_to_standalone_xml }}" become: yes template: src: templates/standalone-infinispan.xml.j2 diff --git a/roles/keycloak/tasks/main.yml b/roles/keycloak/tasks/main.yml index bcf0c06..6ee9041 100644 --- a/roles/keycloak/tasks/main.yml +++ b/roles/keycloak/tasks/main.yml @@ -15,7 +15,7 @@ - name: Link default logs directory file: state: link - src: "{{keycloak_jboss_home}}/standalone/log" + src: "{{ keycloak_jboss_home }}/standalone/log" dest: /var/log/keycloak - block: @@ -30,7 +30,7 @@ retries: 2 delay: 2 rescue: - - name: create Keycloak admin user + - name: "Create {{ keycloak.service_name }} admin user" command: args: argv: @@ -39,11 +39,11 @@ - "-u{{ keycloak_admin_user }}" - "-p{{ keycloak_admin_password }}" become: yes - - name: restart keycloak + - name: "Restart {{ keycloak.service_name }}" include_tasks: tasks/restart_keycloak.yml - - name: "Wait until Keycloak becomes active {{ health_url }}" + - name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}" uri: - url: "{{ health_url }}" + url: "{{ keycloak.health_url }}" register: keycloak_status until: keycloak_status.status == 200 retries: 25 diff --git a/roles/keycloak/tasks/prereqs.yml b/roles/keycloak/tasks/prereqs.yml index 51c2ee8..bb1c44b 100644 --- a/roles/keycloak/tasks/prereqs.yml +++ b/roles/keycloak/tasks/prereqs.yml @@ -10,11 +10,11 @@ - name: Validate credentials assert: that: - - (rhn_username is defined and rhsso_rhn_id is defined) or rhsso_rhn_id is not defined - - (rhn_password is defined and rhsso_rhn_id is defined) or rhsso_rhn_id is not defined + - (rhn_username is defined and keycloak_rhsso_enable) or not keycloak_rhsso_enable or keycloak_offline_install + - (rhn_password is defined and keycloak_rhsso_enable) or not keycloak_rhsso_enable or keycloak_offline_install quiet: True fail_msg: "Cannot install Red Hat SSO without RHN credentials. Check rhn_username and rhn_password are defined" - success_msg: "{{ 'Installing Red Hat Single Sign-On' if rhsso_rhn_id is defined else 'Installing keycloak.org' }}" + success_msg: "{{ 'Installing Red Hat Single Sign-On' if keycloak_rhsso_enable else 'Installing keycloak.org' }}" - name: Set required packages facts set_fact: diff --git a/roles/keycloak/tasks/systemd.yml b/roles/keycloak/tasks/systemd.yml index 858f5d7..ca63491 100644 --- a/roles/keycloak/tasks/systemd.yml +++ b/roles/keycloak/tasks/systemd.yml @@ -38,9 +38,6 @@ daemon_reload: yes when: systemdunit.changed -- set_fact: - health_url: "{{ keycloak_management_url }}/health" - - name: start keycloak systemd: name: keycloak @@ -48,20 +45,22 @@ state: started become: yes -- command: "systemctl status keycloak" +- name: Check service status + command: "systemctl status keycloak" register: keycloak_service_status changed_when: False -- assert: +- name: Verify service status + assert: that: - keycloak_service_status is defined - keycloak_service_status.stdout is defined - meta: flush_handlers -- name: "Wait until Keycloak becomes active {{ health_url }}" +- name: "Wait until Keycloak becomes active {{ keycloak.health_url }}" uri: - url: "{{ health_url }}" + url: "{{ keycloak.health_url }}" register: keycloak_status until: keycloak_status.status == 200 retries: 25 diff --git a/roles/keycloak/templates/keycloak-service.sh.j2 b/roles/keycloak/templates/keycloak-service.sh.j2 index 82e3a21..2281b17 100755 --- a/roles/keycloak/templates/keycloak-service.sh.j2 +++ b/roles/keycloak/templates/keycloak-service.sh.j2 @@ -1,4 +1,5 @@ #!/bin/bash -eu +# {{ ansible_managed }} set +u -o pipefail @@ -22,7 +23,6 @@ readonly KEYCLOAK_HTTP_PORT=${KEYCLOAK_HTTP_PORT} readonly KEYCLOAK_HTTPS_PORT=${KEYCLOAK_HTTPS_PORT} readonly KEYCLOAK_MANAGEMENT_HTTP_PORT=${KEYCLOAK_MANAGEMENT_HTTP_PORT} readonly KEYCLOAK_MANAGEMENT_HTTPS_PORT=${KEYCLOAK_MANAGEMENT_HTTPS_PORT} -readonly KEYCLOAK_LOGFILE={{ keycloak_service_logfile }} readonly KEYCLOAK_PIDFILE={{ keycloak_service_pidfile }} set -u @@ -70,7 +70,6 @@ startKeycloak() { checkEnvVar "${KEYCLOAK_HTTPS_PORT}" 'KEYCLOAK_HTTPS_PORT not provided' 5 checkEnvVar "${KEYCLOAK_MANAGEMENT_HTTP_PORT}" 'KEYCLOAK_MANAGEMENT_HTTP_PORT not provided' 6 checkEnvVar "${KEYCLOAK_MANAGEMENT_HTTPS_PORT}" 'KEYCLOAK_MANAGEMENT_HTTPS_PORT not provided' 7 - checkEnvVar "${KEYCLOAK_LOGFILE}" 'KEYCLOAK_LOGFILE not provided' 8 if [ "$(isKeyCloakRunning)" -eq 1 ]; then statusKeycloak diff --git a/roles/keycloak/templates/keycloak-sysconfig.j2 b/roles/keycloak/templates/keycloak-sysconfig.j2 index f2eda03..15b777c 100644 --- a/roles/keycloak/templates/keycloak-sysconfig.j2 +++ b/roles/keycloak/templates/keycloak-sysconfig.j2 @@ -1,3 +1,4 @@ +# {{ ansible_managed }} JAVA_OPTS='{{ keycloak_java_opts }}' JBOSS_HOME={{ keycloak_jboss_home }} KEYCLOAK_BIND_ADDRESS={{ keycloak_bind_address }} diff --git a/roles/keycloak/templates/keycloak.service.j2 b/roles/keycloak/templates/keycloak.service.j2 index 5816af0..e7233f2 100644 --- a/roles/keycloak/templates/keycloak.service.j2 +++ b/roles/keycloak/templates/keycloak.service.j2 @@ -1,3 +1,4 @@ +# {{ ansible_managed }} [Unit] Description=Keycloak Server After=network.target diff --git a/roles/keycloak/templates/standalone-infinispan.xml.j2 b/roles/keycloak/templates/standalone-infinispan.xml.j2 index cceed6e..1097047 100644 --- a/roles/keycloak/templates/standalone-infinispan.xml.j2 +++ b/roles/keycloak/templates/standalone-infinispan.xml.j2 @@ -1,5 +1,5 @@ - + @@ -738,12 +738,12 @@ - - - - - - + + + + + + diff --git a/roles/keycloak/templates/standalone.xml.j2 b/roles/keycloak/templates/standalone.xml.j2 index 14fd3fb..5b57e09 100644 --- a/roles/keycloak/templates/standalone.xml.j2 +++ b/roles/keycloak/templates/standalone.xml.j2 @@ -1,5 +1,5 @@ - + @@ -139,14 +139,32 @@ +{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %} + {{ keycloak_jdbc[keycloak_jdbc_engine].connection_url }} + {{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }} + + 20 + + + {{ keycloak_jdbc[keycloak_jdbc_engine].db_user }} + {{ keycloak_jdbc[keycloak_jdbc_engine].db_password }} + +{% else %} jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE h2 sa sa +{% endif %} +{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %} + + {{ keycloak_jdbc[keycloak_jdbc_engine].driver_class }} + {{ keycloak_jdbc[keycloak_jdbc_engine].xa_datasource_class }} + +{% endif %} org.h2.jdbcx.JdbcDataSource @@ -621,11 +639,11 @@ - - - - - + + + + + diff --git a/roles/keycloak/vars/main.yml b/roles/keycloak/vars/main.yml index cf1d6cc..43ece24 100644 --- a/roles/keycloak/vars/main.yml +++ b/roles/keycloak/vars/main.yml @@ -4,11 +4,22 @@ keycloak_admin_password: # internal variables below +rhsso_rhn_ids: + '7.5.0': '101971' + '7.5.1': '103836' # locations keycloak_url: "http://{{ keycloak_host }}:{{ keycloak_http_port }}" keycloak_management_url: "http://{{ keycloak_host }}:{{ keycloak_management_http_port }}" + +keycloak: + home: "{{ keycloak_jboss_home }}" + config_dir: "{{ keycloak_config_dir }}" + bundle: "{{ keycloak_rhsso_archive if keycloak_rhsso_enable else keycloak_archive }}" + service_name: "{{ 'rhsso' if keycloak_rhsso_enable else 'keycloak' }}" + health_url: "{{ keycloak_management_url }}/health" + # database keycloak_jdbc: postgres: