Playbook #7

/home/zuul/src/opendev.org/opendev/system-config/playbooks/service-gitea.yaml

Report Status CLI Date Duration Controller User Versions Hosts Plays Tasks Results Files Records
18 Jul 2025 20:49:11 +0000 00:02:01.40 bridge99.opendev.org root Ansible 2.15.13 ara 1.7.2 (client), 1.7.2 (server) Python 3.10.12 1 1 94 92 21 0

File: /home/zuul/src/opendev.org/opendev/system-config/playbooks/roles/gitea/tasks/main.yaml

- name: Ensure docker-compose directory exists
  file:
    state: directory
    path: /etc/gitea-docker
    mode: 0700
- name: Write docker-compose file
  template:
    src: docker-compose.yaml.j2
    dest: /etc/gitea-docker/docker-compose.yaml
    mode: 0600
- name: Ensure gitea volume directories exists
  file:
    state: directory
    path: "/var/gitea/{{ item }}"
    owner: 1000
    group: 1000
  loop:
    - conf
    - data
    - logs
    - certs
    - db
- name: Write app.ini
  template:
    src: app.ini.j2
    dest: /var/gitea/conf/app.ini
- name: Write mariadb conn limit config file
  copy:
    src: 99-max_conn_my.cnf
    dest: /var/gitea/conf/99-max_conn_my.cnf
    owner: root
    group: root
    mode: 0644
- name: Install distro packages
  package:
    name:
      # TODO(clarkb) does the install-docker role handle these two packages?
      - docker-compose
      - python3-requests
      # Installed to make checking memcached stats easy in testing and for
      # human led debugging.
      - netcat-openbsd
    state: present

- name: Install reverse proxy
  include_tasks: proxy.yaml

- name: Get list of image IDs pre pull
  # The --quiet flag prints out only image IDs
  command: docker image list --quiet
  register: pre_pull_image_ids

- name: Run docker-compose pull
  shell:
    cmd: docker-compose pull
    chdir: /etc/gitea-docker/

- name: Get list of image IDs post pull
  # The --quiet flag prints out only image IDs
  command: docker image list --quiet
  register: post_pull_image_ids

- name: Stop/Start gitea safely for Gerrit replication
  when: pre_pull_image_ids.stdout_lines|sort != post_pull_image_ids.stdout_lines|sort
  block:
    - name: Run docker-compose stop
      shell:
        cmd: docker-compose stop --timeout 60
        chdir: /etc/gitea-docker/
    - name: Run docker-compose up mariadb memcached gitea-web
      shell:
        cmd: docker-compose up -d --timeout 60 mariadb memcached gitea-web
        chdir: /etc/gitea-docker/

    # We wait here for the main gitea service to start before starting
    # the ssh service. This is friendly to gerrit replication.
    - name: Wait until the web service is sufficiently up to start ssh
      uri:
        url: "https://localhost:3000/api/v1/users/root"
        validate_certs: false
        status_code: 200, 404
      register: root_user_check
      delay: 1
      retries: 300
      until: root_user_check and root_user_check.status in (200, 404)

    - name: Run docker-compose up gitea-ssh
      shell:
        cmd: docker-compose up -d --timeout 60 gitea-ssh
        chdir: /etc/gitea-docker/
    - name: Run docker prune to cleanup unneeded images
      shell:
        # Keep images around for 3 days before pruning them. Allows for
        # easy rollback if necessary. Note "3d" seems to be rejected by
        # docker but 72 hours == 3 days.
        cmd: docker image prune -f --filter "until=72h"

# User management outside of service bringup to avoid confusion between
# the two stages.
- name: Check if root user exists
  uri:
    url: "https://localhost:3000/api/v1/users/root"
    validate_certs: false
    status_code: 200, 404
  register: root_user_check
  delay: 1
  retries: 300
  until: root_user_check and root_user_check.status in (200, 404)
- name: Create root user
  when: root_user_check.status==404
  block:
    - name: Create root user
      command: >
        /usr/local/bin/docker-compose -f /etc/gitea-docker/docker-compose.yaml exec -T gitea-web
        gitea admin user create --name root --password {{ gitea_root_password }} --email {{ gitea_root_email }} --admin
      no_log: "{{ gitea_no_log }}"
- name: Check if gerrit user exists
  uri:
    url: "https://localhost:3000/api/v1/users/gerrit"
    validate_certs: false
    status_code: 200, 404
  register: gerrit_user_check
- name: Create gerrit user
  when: gerrit_user_check.status==404
  no_log: true
  uri:
    url: "https://localhost:3000/api/v1/admin/users"
    validate_certs: false
    method: POST
    user: root
    password: "{{ gitea_root_password }}"
    force_basic_auth: true
    status_code: 201
    body_format: json
    body:
      email: "gerrit@review.opendev.org"
      full_name: Gerrit
      login_name: gerrit
      must_change_password: false
      password: "{{ gitea_gerrit_password }}"
      send_notify: false
      source_id: 0
      username: gerrit
- name: List keys to determine which updates are necessary.
  uri:
    user: root
    password: "{{ gitea_root_password }}"
    force_basic_auth: true
    url: "https://localhost:3000/api/v1/users/gerrit/keys"
    validate_certs: false
    status_code: 200
  register: gerrit_key_check
  no_log: true
# We want to allow for multiple keys in order to do key rotations.
# Check if both keys are present. If a key is not present then add it
# to Gitea. Keep in mind the two keys may be the same in which case
# we can skip the second key creation. Finally clean up any keys
# that don't match the two keys. This allows us to do key rotations.
- name: Determine if key A and key B are already present
  set_fact:
    key_A_present: "{{ gerrit_key_check.json | selectattr('key', 'equalto', gitea_gerrit_public_key_A ) | list | length > 0 }}"
    key_B_present: "{{ gerrit_key_check.json | selectattr('key', 'equalto', gitea_gerrit_public_key_B ) | list | length > 0 }}"
- name: Add gerrit ssh key A
  when: not key_A_present
  no_log: true
  uri:
    user: root
    password: "{{ gitea_root_password }}"
    force_basic_auth: true
    url: "https://localhost:3000/api/v1/admin/users/gerrit/keys"
    validate_certs: false
    method: POST
    status_code: 201
    body_format: json
    body:
      key: "{{ gitea_gerrit_public_key_A }}"
      read_only: false
      title: "Gerrit replication key A"
- name: Add gerrit ssh key B
  when: not key_B_present and gitea_gerrit_public_key_A != gitea_gerrit_public_key_B
  no_log: true
  uri:
    user: root
    password: "{{ gitea_root_password }}"
    force_basic_auth: true
    url: "https://localhost:3000/api/v1/admin/users/gerrit/keys"
    validate_certs: false
    method: POST
    status_code: 201
    body_format: json
    body:
      key: "{{ gitea_gerrit_public_key_B }}"
      read_only: false
      title: "Gerrit replication key B"
- name: List keys again to ensure key ids are correct for deletion.
  uri:
    user: root
    password: "{{ gitea_root_password }}"
    force_basic_auth: true
    url: "https://localhost:3000/api/v1/users/gerrit/keys"
    validate_certs: false
    status_code: 200
  register: gerrit_key_check
  no_log: true
- name: Delete old gerrit ssh keys
  when: existing_pubkey.key != gitea_gerrit_public_key_A and existing_pubkey.key != gitea_gerrit_public_key_B
  no_log: true
  uri:
    user: root
    password: "{{ gitea_root_password }}"
    force_basic_auth: true
    url: "https://localhost:3000/api/v1/user/keys/{{ existing_pubkey.id }}"
    validate_certs: false
    method: DELETE
    status_code: 204
  loop: "{{ gerrit_key_check.json }}"
  loop_control:
    loop_var: existing_pubkey
- name: Set up cron job to pack git refs
  cron:
    name: pack-git-refs
    state: present
    job: >
      /usr/local/bin/docker-compose -f /etc/gitea-docker/docker-compose.yaml exec -T gitea-web
      find /data/git/repositories/ -maxdepth 2 -name *.git -type d -execdir git --git-dir={} gc --quiet \;
    minute: '{{ 59 | random(seed=inventory_hostname) }}'
    hour: '{{ 23 | random(seed=inventory_hostname) }}'
    weekday: '*/2'
- name: Create db backup dest
  file:
    state: directory
    path: /var/backups/gitea-mariadb
    mode: 0700
    owner: root
    group: root
- name: Set up cron job to backup the database
  cron:
    name: gitea-db-backup
    state: present
    user: root
    job: >
      /usr/local/bin/docker-compose -f /etc/gitea-docker/docker-compose.yaml exec -T mariadb
      bash -c '/usr/bin/mysqldump --opt --databases gitea --single-transaction -uroot -p"$MYSQL_ROOT_PASSWORD"' |
      gzip -9 > /var/backups/gitea-mariadb/gitea-mariadb.sql.gz
    minute: 42
    hour: 4
- name: Rotate db backups
  include_role:
    name: logrotate
  vars:
    logrotate_file_name: /var/backups/gitea-mariadb/gitea-mariadb.sql.gz
    logrotate_compress: false
- name: Setup db backup streaming job
  block:
    - name: Create backup streaming config dir
      file:
        path: /etc/borg-streams
        state: directory

    - name: Create db streaming file
      copy:
        content: >-
            /usr/local/bin/docker-compose -f /etc/gitea-docker/docker-compose.yaml exec -T mariadb
            bash -c '/usr/bin/mysqldump --skip-extended-insert --databases gitea --single-transaction -uroot -p"$MYSQL_ROOT_PASSWORD"'
        dest: /etc/borg-streams/mysql