# Required vars
# - openqa_email
##       string - Email address of admin user
# - openqa_nickname
##       string - Short name of admin user (shown in the web UI for e.g.)
# - openqa_fullname
##       string - Full name of admin user
# - openqa_key
# - openqa_secret
##       string - MUST be 16-character hexadecimals, and are secrets
# openqa_userid
##       string - User ID of admin user: for Fedora should be a Fedora openID URL,
##                http://fasname.id.fedoraproject.org

# Required vars with defaults
# - external_hostname
##       string - The public hostname for the server (will be used as ServerName)
##       default - ansible_nodename
# - openqa_repo
##      string - Repo to enable when updating openQA packages. Set to
##               'updates-testing' to use packages from updates-testing
##      default - 'updates', which is effectively a no-op

# Optional vars
# - openqa_static_uid
##       int - a static ID for the geekotest user and group if desired
##             this is useful for NFS mounting openQA data files
# - openqa_dbname
##       string - The name of the database to use
# - openqa_dbhost
##       string - The hostname of the database server
# - openqa_dbuser
##       string - The database username
# - openqa_dbpassword
##       string - The database password
# - openqa_assetsize
##       int - the asset size limit to set in GB (upstream default is 100GB)
##             higher is recommended for normal Fedora testing, 300GB is good
##             FIXME: this only works for pgsql ATM
# - openqa_assetsize_updates
##       int - the asset size limit to set for update job groups in GB
##             (upstream default is 100GB). Lower is recommended for normal
##             Fedora testing, 50GB is good
##             FIXME: this only works for pgsql ATM
# - openqa_amqp_publisher_prefix
##      string - openQA AMQP 'topic_prefix' config value. This prefix is
##               prepended to the topic when publishing messages with the
##               AMQP or FedoraMessaging plugins, with a . added (so don't
##               include the . in the value). If set to an empty string,
##               openQA from before August 2019 will use 'suse' as the
##               prefix; later openQA will omit the prefix entirely
##      default - '' (empty string)
# - openqa_amqp_publisher_url
##      string - AMQP broker URL for publishing messages with the AMQP or
##               FedoraMessaging plugins, e.g.
##               amqps://fedora:@rabbitmq.fedoraproject.org/%2Fpubsub
##      default - amqp://test:@localhost/%2Fpubsub
# - openqa_amqp_publisher_exchange
##      string - exchange to publish AMQP messages to with AMQP or
##               FedoraMessaging plugins, e.g. amq.topic. Note, this can
##               be (ab)used to set additional query parameters for the
##               publish request, by just appending them, e.g.
##               amq.topic&cacertfile=/path/to/ca_certificate_file
##      default - amq.topic
# - openqa_webapi_plugins
##       string - Space-separated list of openQA WebAPI plugins to enable
##                Note if you enable FedoraMessaging or AMQP plugins, you should
##                also set openqa_amqp_publisher_prefix, openqa_amqp_publisher_url
##                and openqa_amqp_publisher_exchange
# - deployment_type
##      string - Fedora Infrastructure thing; for this role, applies an
##               infra-specific tweak to httpd config. Don't set it outside
##               Fedora infra.
# - openqa_scratch
##      list -   A list of task IDs of scratch builds to install. If set, we
##               will download the scratch build(s) and set up a side repo
##               containing them. We do this because openQA staging is our
##               main platform for testing new openQA releases; we need to
##               be able to run scratch builds to see if a new release is
##               really bad before submitting it as an update, we don't want
##               to send new builds to updates-testing if they have problems
# - openqa_nfs_workers
##      list -   A list of hostnames of workers that will share the factory
##               directory via NFS. All these will be granted rw access to
##               the share (they need write access to be able to decompress
##               compressed disk images on the fly). If not defined, the
##               factory dir will not be shared via NFS at all.

# If openqa_dbhost is set, the other openqa_db* variables must be too,
# and the server will be configured to use a pgsql database accordingly.
# If openqa_dbhost is not set, the server will use a local SQLite database
# and the other openqa_db* values are ignored.

- name: Create geekotest group with static GID
  group: "name=geekotest gid={{ openqa_static_uid }} system=yes"
  when: "openqa_static_uid is defined"

- name: Create geekotest user with static UID
  user:
    name: geekotest
    comment: "openQA user"
    uid: "{{ openqa_static_uid }}"
    group: geekotest
    home: "/var/lib/openqa"
    createhome: no
    system: yes
    shell: /sbin/nologin
  when: "openqa_static_uid is defined"

- name: Remove scratch repo directory
  file: path=/var/tmp/scratchrepo state=absent

- name: (Re-)create scratch repo directory
  file: path=/var/tmp/scratchrepo state=directory owner=root group=root
  when: "openqa_scratch is defined"

- name: Install Koji CLI client and createrepo
  package:
    name: ["koji", "createrepo"]
    state: present
  tags:
  - packages
  when: "openqa_scratch is defined"

- name: Download scratch builds
  command: "koji download-task --arch=noarch --arch={{ ansible_architecture }} {{ item }}"
  args:
    chdir: /var/tmp/scratchrepo
  loop: "{{ openqa_scratch|flatten(levels=1) }}"
  when: "openqa_scratch is defined"

- name: Write repodata
  command: "createrepo ."
  args:
    chdir: /var/tmp/scratchrepo
  when: "openqa_scratch is defined"

- name: Write scratch build repo config
  copy: src=scratchrepo.repo dest=/etc/yum.repos.d/scratchrepo.repo owner=root group=root mode=0644
  when: "openqa_scratch is defined"

- name: Delete scratch build repo config
  file: path=/etc/yum.repos.d/scratchrepo.conf state=absent
  when: "openqa_scratch is not defined"

# this is separate from the step below so we can use openqa_repo just
# for these packages
- name: Install openQA packages
  package:
    name:
      - openqa
      - openqa-httpd
      - openqa-plugin-fedora-messaging
    state: latest
    enablerepo: "{{ openqa_repo }}"
  tags:
  - packages

- name: Install various other required packages
  package:
    name:
      - python3-libselinux # for using seboolean module
      - git # for checking out tests/tools
      - jq # for checking if tests changed after template load
      - python3-libsemanage # for using seboolean module
      - perl(Class::DBI::Pg) # for using postgresql DB
      - genisoimage # for building cloud-init ISO
      - policycoreutils # for loading SELinux policy module
    state: present
  tags:
  - packages

- name: Install various other required packages
  package:
    name:
      - nfs-utils # for configuring/running NFS server
    state: present
  when: "openqa_nfs_workers is defined"
  tags:
  - packages

- name: Check test directory exists with correct ownership
  file: path=/var/lib/openqa/share/tests/fedora state=directory owner=geekotest group=geekotest recurse=yes

# we don't want to run the checkout if the tests are on a non-standard
# branch, as that usually means we're messing around on staging and
# don't want the checkout reset to HEAD.
- name: Check if tests are checked out and on a non-standard branch
  command: "git status" # noqa 303
  args:
    chdir: /var/lib/openqa/share/tests/fedora
  register: testsbranch
  failed_when: "1 != 1"
  changed_when: "1 != 1"
  check_mode: no

- name: Check out the tests
  git:
    repo: https://pagure.io/fedora-qa/os-autoinst-distri-fedora.git # noqa 401
    dest: /var/lib/openqa/share/tests/fedora
  register: gittests
  become: true
  become_user: geekotest
  when: >
    (testsbranch.stderr.find('ot a git repository') != -1) or
    (testsbranch.stdout.find('On branch main') != -1 and
    testsbranch.stdout.find('Changes not staged') == -1)

- name: Remove old openqa_fedora_tools checkout
  file: path=/root/openqa_fedora_tools state=absent

- name: Create asset directories
  file: path={{ item }} state=directory owner=geekotest group=geekotest mode=0775
  with_items:
  - /var/lib/openqa/share/factory/iso
  - /var/lib/openqa/share/factory/iso/fixed
  - /var/lib/openqa/share/factory/hdd
  - /var/lib/openqa/share/factory/hdd/fixed
  - /var/lib/openqa/share/factory/repo
  - /var/lib/openqa/share/factory/other

- name: Copy in meta-data for cloud-init ISO creation
  copy: src=meta-data dest=/var/tmp/meta-data owner=root group=root mode=0644

- name: Copy in user-data for cloud-init ISO creation
  copy: src=user-data dest=/var/tmp/user-data owner=root group=root mode=0644

- name: Create cloud-init ISO
  command: genisoimage -output cloudinit.iso -volid cidata -joliet -rock /var/tmp/user-data /var/tmp/meta-data
  args:
    chdir: /var/lib/openqa/share/factory/iso/fixed
    creates: /var/lib/openqa/share/factory/iso/fixed/cloudinit.iso

- name: Create exports file
  template: src=exports.j2 dest=/etc/exports.d/openqa.exports owner=root group=root mode=0644
  register: exportsfile
  when: openqa_nfs_workers is defined
  tags:
  - config

- name: Enable and start NFS server
  service: name=nfs-server enabled=yes state=started
  when: openqa_nfs_workers is defined

- name: Refresh exports
  command: exportfs -r
  when: exportsfile is changed

- name: Set up Apache config
  template: src=openqa.conf.httpd.j2 dest=/etc/httpd/conf.d/openqa.conf owner=root group=root mode=0644
  notify:
  - reload httpd
  tags:
  - config

- name: OpenQA config
  template: src=openqa.ini.j2 dest=/etc/openqa/openqa.ini owner=geekotest group=root mode=0644
  tags:
  - config

- name: Create database
  delegate_to: "{{ openqa_dbhost_delegate|default(openqa_dbhost) }}"
  become_user: postgres
  become: true
  postgresql_db: db={{ openqa_dbname }}
  when: "openqa_dbhost is defined"

- name: Ensure db user has access to database
  delegate_to: "{{ openqa_dbhost_delegate|default(openqa_dbhost) }}"
  become_user: postgres
  become: true
  postgresql_user: db={{ openqa_dbname }} user={{ openqa_dbuser }} password={{ openqa_dbpassword }} role_attr_flags=NOSUPERUSER
  when: "openqa_dbhost is defined"

- name: Database config
  template: src=database.ini.pgsql.j2 dest=/etc/openqa/database.ini owner=geekotest group=root mode=0640
  when: "openqa_dbhost is defined"
  tags:
  - config

- name: Initialize database
  shell: "/usr/share/openqa/script/initdb --user geekotest --init_database" # noqa 305
  register: initdb
  changed_when: "initdb.rc == 0"
  failed_when: "(initdb.rc > 0) and (initdb.stdout is not defined or initdb.stdout.find('already exists') == -1)"

- name: Enable and start services
  service: name={{ item }} enabled=yes state=started
  register: services
  with_items:
  - openqa-livehandler
  - openqa-scheduler
  - openqa-webui
  - openqa-websockets
  - openqa-gru

- name: Create somewhere to stick our custom SELinux module
  file:
    path: /usr/local/share/selinux
    state: directory
    mode: '0755'

- name: Copy over custom SELinux module allowing httpd to connect to openQA
  copy: src=httpd-openqa.pp dest=/usr/local/share/selinux/httpd-openqa.pp owner=root group=root mode=0644
  register: selinux_module

- name: Load our custom SELinux module
  command: semodule -i /usr/local/share/selinux/httpd-openqa.pp
  when: selinux_module is changed

# Unfortunately still need this until port 9528 is tagged:
# https://bugzilla.redhat.com/show_bug.cgi?id=1277312
- name: Set httpd_can_network_connect SELinux boolean
  seboolean: name=httpd_can_network_connect state=yes persistent=yes

- name: Allow Apache to read from NFS (as we store test data files there now)
  seboolean: name=httpd_use_nfs state=yes persistent=yes

# services is undefined in check mode
- name: Wait for openQA to be fully started
  pause: seconds=5
  when: "services is defined and services is changed"

# the 'dispatcher' role may require this to have a non-root group and
# sets it 0640, so we don't enforce ownership here and set mode to
# 0640 so we don't wind up ping-ponging it between server and
# dispatcher roles.
- name: openQA client config
  template: src=client.conf.j2 dest=/etc/openqa/client.conf mode=0640
  tags:
  - config

- name: Create admin user
  command: >
    /var/lib/openqa/script/create_admin --email {{ openqa_email }} --nickname {{ openqa_nickname }}
    --fullname '{{ openqa_fullname }}' --key {{ openqa_key }} --secret {{ openqa_secret }}
    {{ openqa_userid }}
  register: admin
  changed_when: "admin.rc == 0"
  failed_when: "(admin.rc > 0) and (admin.stderr is not defined or admin.stderr.find('already exists') == -1)"

- name: Check if we're on upstream template format or FIF
  stat:
    path: /var/lib/openqa/share/tests/fedora/templates.fif.json
  register: templatesfif

- name: Dump existing config for checking changes
  shell: "/usr/share/openqa/script/dump_templates --json > /tmp/tmpl-old.json || :"
  when: "(gittests is defined) and (gittests is changed)"
  changed_when: "1 != 1"

# Because of the boring details of how template loading works, getting
# a correct 'changed' for this step is too difficult. Instead we have
# the dump (above) and check (later) steps; when the templates actually
# changed, the *check* step will register as changed.
- name: Load main tests (upstream format)
  command: "/var/lib/openqa/share/tests/fedora/templates --clean"
  when: "(gittests is defined) and (gittests is changed) and (not templatesfif.stat.exists)"
  changed_when: "1 != 1"

- name: Load update tests (upstream format)
  command: "/var/lib/openqa/share/tests/fedora/templates-updates --update"
  when: "(gittests is defined) and (gittests is changed) and (not templatesfif.stat.exists)"
  changed_when: "1 != 1"

- name: Load all tests (FIF format)
  command: "/var/lib/openqa/share/tests/fedora/fifloader.py -l --clean templates.fif.json templates-updates.fif.json"
  args:
    chdir: /var/lib/openqa/share/tests/fedora
  when: "(gittests is defined) and (gittests is changed) and (templatesfif.stat.exists)"
  changed_when: "1 != 1"

- name: Check if the tests changed in previous step
  shell: "/usr/share/openqa/script/dump_templates --json > /tmp/tmpl-new.json && diff <(jq -S . /tmp/tmpl-old.json) <(jq -S . /tmp/tmpl-new.json)"
  when: "(gittests is defined) and (gittests is changed)"
  register: testsdiff
  changed_when: "testsdiff.rc > 0"
  failed_when: "1 != 1"

- name: Set 'fedora' asset size limit (if specified) (pgsql)
  delegate_to: "{{ openqa_dbhost_delegate|default(openqa_dbhost) }}"
  become_user: postgres
  become: true
  command: >
    psql -d {{ openqa_dbname }} -c "UPDATE job_groups SET size_limit_gb = {{ openqa_assetsize }}
    WHERE name = 'fedora' AND (size_limit_gb != {{ openqa_assetsize }} OR size_limit_gb IS NULL);"
  when: "openqa_dbhost is defined and openqa_assetsize is defined"
  register: pgsqlsize
  changed_when: "pgsqlsize.stdout.find('UPDATE 0') == -1"

- name: Set 'Fedora PowerPC' asset size limit (if specified) (pgsql)
  delegate_to: "{{ openqa_dbhost_delegate|default(openqa_dbhost) }}"
  become_user: postgres
  become: true
  command: >
    psql -d {{ openqa_dbname }} -c "UPDATE job_groups SET size_limit_gb = {{ openqa_assetsize_ppc }}
    WHERE name = 'Fedora PowerPC' AND (size_limit_gb != {{ openqa_assetsize_ppc }}
    OR size_limit_gb IS NULL);"
  when: "openqa_dbhost is defined and openqa_assetsize_ppc is defined"
  register: pgsqlsizeppc
  changed_when: "pgsqlsizeppc.stdout.find('UPDATE 0') == -1"

- name: Set 'Fedora AArch64' asset size limit (if specified) (pgsql)
  delegate_to: "{{ openqa_dbhost_delegate|default(openqa_dbhost) }}"
  become_user: postgres
  become: true
  command: >
    psql -d {{ openqa_dbname }} -c "UPDATE job_groups SET size_limit_gb = {{ openqa_assetsize_aarch64 }}
    WHERE name = 'Fedora AArch64' AND (size_limit_gb != {{ openqa_assetsize_aarch64 }}
    OR size_limit_gb IS NULL);"
  when: "openqa_dbhost is defined and openqa_assetsize_aarch64 is defined"
  register: pgsqlsizeaarch64
  changed_when: "pgsqlsizeaarch64.stdout.find('UPDATE 0') == -1"

- name: Set (x86_64) update job group asset size limit (if specified) (pgsql)
  delegate_to: "{{ openqa_dbhost_delegate|default(openqa_dbhost) }}"
  become_user: postgres
  become: true
  command: >
    psql -d {{ openqa_dbname }} -c "UPDATE job_groups SET size_limit_gb = {{ openqa_assetsize_updates }}
    WHERE name = 'Fedora Updates' AND (size_limit_gb != {{ openqa_assetsize_updates }}
    OR size_limit_gb IS NULL);"
  when: "openqa_dbhost is defined and openqa_assetsize_updates is defined"
  register: pgsqlupdatesize
  changed_when: "pgsqlupdatesize.stdout.find('UPDATE 0') == -1"

- name: Set ppc64le update job group asset size limit (if specified) (pgsql)
  delegate_to: "{{ openqa_dbhost_delegate|default(openqa_dbhost) }}"
  become_user: postgres
  become: true
  command: >
    psql -d {{ openqa_dbname }} -c "UPDATE job_groups SET size_limit_gb = {{ openqa_assetsize_updates_ppc }}
    WHERE name = 'Fedora PowerPC Updates' AND (size_limit_gb != {{ openqa_assetsize_updates_ppc }}
    OR size_limit_gb IS NULL);"
  when: "openqa_dbhost is defined and openqa_assetsize_updates_ppc is defined"
  register: pgsqlupdatesizeppc
  changed_when: "pgsqlupdatesizeppc.stdout.find('UPDATE 0') == -1"
