|
@@ -0,0 +1,423 @@
|
|
|
|
+---
|
|
|
|
+# Tasks required by 10-quay-deploy.adoc.
|
|
|
|
+- name: Issue a new Cert for Quay if necessary.
|
|
|
|
+ hosts: workstation.lab.example.com
|
|
|
|
+ gather_subset: min
|
|
|
|
+ tasks:
|
|
|
|
+ - name: Check if Quay key exists to save time
|
|
|
|
+ ansible.builtin.stat:
|
|
|
|
+ path: "{{ ansible_facts['user_dir'] }}/ca/quay-key.pem"
|
|
|
|
+ get_attributes: no
|
|
|
|
+ get_checksum: no
|
|
|
|
+ get_mime: no
|
|
|
|
+ register: qkey_file
|
|
|
|
+
|
|
|
|
+ - name: Check if Quay cert exists to save time
|
|
|
|
+ ansible.builtin.stat:
|
|
|
|
+ path: "{{ ansible_facts['user_dir'] }}/ca/quay-cert.pem"
|
|
|
|
+ get_attributes: no
|
|
|
|
+ get_checksum: no
|
|
|
|
+ get_mime: no
|
|
|
|
+ register: qcert_file
|
|
|
|
+
|
|
|
|
+ - name: Create a new private key for Quay, if it does not exist yet.
|
|
|
|
+ community.crypto.openssl_privatekey:
|
|
|
|
+ path: "{{ ansible_facts['user_dir'] }}/ca/quay-key.pem"
|
|
|
|
+ type: RSA
|
|
|
|
+ size: 4096
|
|
|
|
+ mode: 0600
|
|
|
|
+ when: qkey_file.stat.exists == false
|
|
|
|
+
|
|
|
|
+ - name: Create a CSR for Quay
|
|
|
|
+ community.crypto.openssl_csr:
|
|
|
|
+ path: "{{ ansible_facts['user_dir'] }}/ca/quay-csr.pem"
|
|
|
|
+ privatekey_path: "{{ ansible_facts['user_dir'] }}/ca/quay-key.pem"
|
|
|
|
+ subject:
|
|
|
|
+ C: US
|
|
|
|
+ ST: North Carolina
|
|
|
|
+ L: Raleigh
|
|
|
|
+ O: Red Hat
|
|
|
|
+ OU: RHT
|
|
|
|
+ CN: registry.ocp4.example.com
|
|
|
|
+ use_common_name_for_san: yes
|
|
|
|
+ mode: 0600
|
|
|
|
+ when: qcert_file.stat.exists == false
|
|
|
|
+
|
|
|
|
+ - name: Issue a certificate for Quay if one isn't there yet.
|
|
|
|
+ ansible.builtin.command:
|
|
|
|
+ cmd: openssl ca -config {{ ansible_facts['user_dir'] }}/ca/openssl.cnf -passin pass:verysecret -in {{ ansible_facts['user_dir'] }}/ca/quay-csr.pem -out {{ ansible_facts['user_dir'] }}/ca/quay-cert.pem -batch -notext
|
|
|
|
+ creates: "{{ ansible_facts['user_dir'] }}/ca/quay-cert.pem"
|
|
|
|
+
|
|
|
|
+ - name: Load CA cert and Quay cert.
|
|
|
|
+ ansible.builtin.set_fact:
|
|
|
|
+ ca_cert: "{{ lookup('file', ansible_facts['user_dir'] + '/ca/ca-cert.pem') }}"
|
|
|
|
+ quay_cert: "{{ lookup('file', ansible_facts['user_dir'] + '/ca/lab-ca/newcerts/00.pem') }}"
|
|
|
|
+
|
|
|
|
+ - name: Concatenate Quay and CA certs.
|
|
|
|
+ ansible.builtin.copy:
|
|
|
|
+ dest: "{{ ansible_facts['user_dir'] }}/ca/quay-cert.pem"
|
|
|
|
+ content: |
|
|
|
|
+ {{ quay_cert }}
|
|
|
|
+ {{ ca_cert }}
|
|
|
|
+
|
|
|
|
+- name: Prepare registry VM to run Quay services.
|
|
|
|
+ hosts: registry.ocp4.example.com
|
|
|
|
+ gather_subset: min
|
|
|
|
+ tasks:
|
|
|
|
+ - name: Ensure firewall allows HTTP/HTTPS.
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.posix.firewalld:
|
|
|
|
+ immediate: yes
|
|
|
|
+ permanent: yes
|
|
|
|
+ zone: public
|
|
|
|
+ service: "{{ item }}"
|
|
|
|
+ state: enabled
|
|
|
|
+ loop:
|
|
|
|
+ - http
|
|
|
|
+ - https
|
|
|
|
+
|
|
|
|
+ - name: Ensure unpriv users can open ports from 80 onwards.
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.posix.sysctl:
|
|
|
|
+ name: net.ipv4.ip_unprivileged_port_start
|
|
|
|
+ value: "80"
|
|
|
|
+ state: present
|
|
|
|
+ sysctl_file: /etc/sysctl.d/quay-low-ports.conf
|
|
|
|
+ reload: yes
|
|
|
|
+
|
|
|
|
+ - name: Ensure user quay exists.
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.builtin.user:
|
|
|
|
+ name: quay
|
|
|
|
+ create_home: yes
|
|
|
|
+ state: present
|
|
|
|
+
|
|
|
|
+ - name: Have the quay user accept student's SSH key.
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.posix.authorized_key:
|
|
|
|
+ key: "{{ lookup('ansible.builtin.file', '/home/student/.ssh/lab_rsa.pub') }}"
|
|
|
|
+ user: quay
|
|
|
|
+ state: present
|
|
|
|
+
|
|
|
|
+ - name: Ensure user quay will linger.
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.builtin.command:
|
|
|
|
+ cmd: loginctl enable-linger quay
|
|
|
|
+ creates: /var/lib/systemd/linger/quay
|
|
|
|
+
|
|
|
|
+ - name: Ensure data directories are there.
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.builtin.file:
|
|
|
|
+ path: "{{ item }}"
|
|
|
|
+ mode: 0770
|
|
|
|
+ owner: quay
|
|
|
|
+ group: quay
|
|
|
|
+ state: directory
|
|
|
|
+ loop:
|
|
|
|
+ - /local/quay-pg
|
|
|
|
+ - /local/quay
|
|
|
|
+
|
|
|
|
+ - name: Ensure .docker directory is there
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.builtin.file:
|
|
|
|
+ path: "/home/quay/.docker"
|
|
|
|
+ mode: 0700
|
|
|
|
+ owner: quay
|
|
|
|
+ group: quay
|
|
|
|
+ state: directory
|
|
|
|
+
|
|
|
|
+ # TODO: figure out how to customise this with registry host changes
|
|
|
|
+ - name: Ensure podman will be able to log into the upstream registry
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.builtin.copy:
|
|
|
|
+ dest: "/home/quay/.docker/config.json"
|
|
|
|
+ content: |
|
|
|
|
+ {"auths":{"registry.redhat.io":{"auth":"fHVoYy1wb29sLTlmMDA1Mzc2LTM2YTItNDJhMS1hNTQwLTA0NzNkYzg3MzYzMzpleUpoYkdjaU9pSlNVelV4TWlKOS5leUp6ZFdJaU9pSTVPRGc1WVdFeFl6Qm1PV0kwWmpVM1lqazNObUk1WldFeU16SXdaalUwTUNKOS5zWmQ5VE1RbzBXREc2NUc5Qk1ObmtuYlBjRkIzNmhyRFhkMThfdTNLeHFaczdlOG1hQ19QeEFReGpwdVk0YVM2VERIbkxDNWpGYjRRNXFYVEpWbjJCOGE4cDFuY08tM24ySG5QdDg3NmktVUFDU3lldWtpb3k4aHI0V3d1ZkhReFVYMmxxWFhYdjN6blE3am1URUNBc25rWkNRSFU1dFNpRnNUZHhFZGZkeU42Z20xN3VqY2thZG5NbFBZcTZfU1I2bUtLaUpUdFQ3SFlDWXJBVk5zZ0tfNGFkZ2MtRXBlbEtHbGNERWkzNGhYbzFqbEIzRERyUWkxSUxCV0UwZkdXb1czZy1ZUzFGMFlEXzc0bm1XSU5mUE1jM25UOERaQWl0OEw0VlFPTnZnUE51YnVfTVVGUGhqX29VUjF3VUR0a1BRNktJdm82UWYyRkdwMndLM1B6YnRBRFFzRVZTZDlITzQ3a0RKdGFobk95YTFmRmdqZVk1bFNxLW1vT2RqUldCZ3U2XzNIX25lZExJR1lQRHRBZnp5cGJ1eHZ1cEd1M2hYWnVzeWN0aURtR203SkR5RW5KdjF1RFZmYVduU2EzSV9NcFRSVVcyZWU1RF9CanJleTdlU2I0bEpGcmp1eC1nY2JVaHFsWGJZc2l6azdXWHpvRmtrVFlMdXFDQ1FvS1J0OFdSN1UzTmh3c3Q2ckV3eEFOaWJFTlNzUVB3MGg4X0NDRm5qTHFSTl82cWpTc0tpeWRGT2tHVFliT0taTktaSVVhYkZFTjRhYVRVYmlYTVdPS2Eyak1xLUhwazBMNEowUmtOM2JkQVVqWmtERHE0ZFY1ZVFjdXNIeV9LY29nd1VKSjZ4MDNObnM4b0xBdjRJZ3RKeXlxcmE1YUJHSkxReHNjRXVSNzQwWQ=="}}}
|
|
|
|
+ mode: 0600
|
|
|
|
+ owner: quay
|
|
|
|
+ group: quay
|
|
|
|
+
|
|
|
|
+- name: Configure containers and their environment on registry VM.
|
|
|
|
+ hosts: registry.ocp4.example.com
|
|
|
|
+ gather_subset: min
|
|
|
|
+ remote_user: quay
|
|
|
|
+ tasks:
|
|
|
|
+ - name: Create a podman network, if necessary.
|
|
|
|
+ containers.podman.podman_network:
|
|
|
|
+ name: quay
|
|
|
|
+ state: present
|
|
|
|
+
|
|
|
|
+ - name: Pull all the images if necessary.
|
|
|
|
+ containers.podman.podman_image:
|
|
|
|
+ name: "{{ registry_host }}/{{ item }}"
|
|
|
|
+ pull: yes
|
|
|
|
+ state: present
|
|
|
|
+ loop:
|
|
|
|
+ - rhel9/postgresql-15:latest
|
|
|
|
+ - rhel9/redis-7:latest
|
|
|
|
+ - quay/quay-rhel8:v{{ quay_version }}
|
|
|
|
+ - quay/clair-rhel8:v{{ quay_version }}
|
|
|
|
+
|
|
|
|
+ # TODO: recursive!
|
|
|
|
+ - name: Ensure PG datadir is owned by the correct user.
|
|
|
|
+ become_method: containers.podman.podman_unshare
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.builtin.file:
|
|
|
|
+ path: /local/quay-pg
|
|
|
|
+ state: directory
|
|
|
|
+ owner: 26
|
|
|
|
+ mode: 0770
|
|
|
|
+
|
|
|
|
+ - name: Start postgres container if necessary.
|
|
|
|
+ containers.podman.podman_container:
|
|
|
|
+ name: postgresql
|
|
|
|
+ image: "{{ registry_host }}/rhel9/postgresql-15:latest"
|
|
|
|
+ rm: yes
|
|
|
|
+ detach: yes
|
|
|
|
+ env:
|
|
|
|
+ POSTGRESQL_USER: quay
|
|
|
|
+ POSTGRESQL_PASSWORD: secret
|
|
|
|
+ POSTGRESQL_DATABASE: quay
|
|
|
|
+ POSTGRESQL_ADMIN_PASSWORD: verysecret
|
|
|
|
+ network:
|
|
|
|
+ - quay
|
|
|
|
+ volumes:
|
|
|
|
+ - /local/quay-pg:/var/lib/pgsql/data:Z
|
|
|
|
+ state: started
|
|
|
|
+ register: pg_started
|
|
|
|
+
|
|
|
|
+ - name: Wait for the PostgreSQL container to become ready if it was changed in any way.
|
|
|
|
+ containers.podman.podman_container_info:
|
|
|
|
+ name: postgresql
|
|
|
|
+ when: pg_started.changed
|
|
|
|
+ register: pg_info
|
|
|
|
+ until: pg_info.containers[0].State.Running
|
|
|
|
+ retries: 12
|
|
|
|
+ delay: 5
|
|
|
|
+
|
|
|
|
+ - name: Wait for the server inside container to start up.
|
|
|
|
+ containers.podman.podman_container_exec:
|
|
|
|
+ name: postgresql
|
|
|
|
+ command: 'psql -d quay -U postgres -c "SELECT 1"'
|
|
|
|
+ when: pg_started.changed
|
|
|
|
+ changed_when: no
|
|
|
|
+ register: pg_rdy
|
|
|
|
+ until: pg_rdy.rc == 0
|
|
|
|
+ retries: 10
|
|
|
|
+ delay: 3
|
|
|
|
+
|
|
|
|
+ - name: Create the trigram extension if necessary.
|
|
|
|
+ containers.podman.podman_container_exec:
|
|
|
|
+ name: postgresql
|
|
|
|
+ command: 'psql -d quay -U postgres -c "CREATE EXTENSION IF NOT EXISTS pg_trgm"'
|
|
|
|
+ register: pg_ext
|
|
|
|
+ changed_when:
|
|
|
|
+ - not "already exists" in pg_ext.stderr
|
|
|
|
+
|
|
|
|
+ - name: If we started the PG container and created the extension, stop the container now.
|
|
|
|
+ containers.podman.podman_container:
|
|
|
|
+ name: postgresql
|
|
|
|
+ state: stopped
|
|
|
|
+ when:
|
|
|
|
+ - pg_started.changed
|
|
|
|
+ - pg_ext.changed
|
|
|
|
+
|
|
|
|
+ - name: Create Quay config directory if necessary.
|
|
|
|
+ ansible.builtin.file:
|
|
|
|
+ path: "{{ ansible_facts['user_dir'] }}/config"
|
|
|
|
+ state: directory
|
|
|
|
+ mode: 0770
|
|
|
|
+
|
|
|
|
+ - name: Publish Quay key on registry.
|
|
|
|
+ ansible.builtin.copy:
|
|
|
|
+ src: /home/student/ca/quay-key.pem
|
|
|
|
+ dest: "{{ ansible_facts['user_dir'] }}/config/ssl.key"
|
|
|
|
+ mode: 0440
|
|
|
|
+
|
|
|
|
+ - name: Publish Quay cert on registry.
|
|
|
|
+ ansible.builtin.copy:
|
|
|
|
+ src: /home/student/ca/quay-cert.pem
|
|
|
|
+ dest: "{{ ansible_facts['user_dir'] }}/config/ssl.cert"
|
|
|
|
+ mode: 0440
|
|
|
|
+
|
|
|
|
+ - name: Publish Quay config file.
|
|
|
|
+ ansible.builtin.copy:
|
|
|
|
+ dest: "{{ ansible_facts['user_dir'] }}/config/config.yaml"
|
|
|
|
+ content: |
|
|
|
|
+ BUILDLOGS_REDIS:
|
|
|
|
+ host: redis
|
|
|
|
+ password: verysecret
|
|
|
|
+ port: 6379
|
|
|
|
+ CREATE_NAMESPACE_ON_PUSH: true
|
|
|
|
+ DATABASE_SECRET_KEY: 410c87de-8ad8-4f4c-9670-2ec25bc87191
|
|
|
|
+ DB_URI: postgresql://quay:secret@postgresql:5432/quay
|
|
|
|
+ DISTRIBUTED_STORAGE_CONFIG:
|
|
|
|
+ default:
|
|
|
|
+ - LocalStorage
|
|
|
|
+ - storage_path: /registry
|
|
|
|
+ DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS: []
|
|
|
|
+ DISTRIBUTED_STORAGE_PREFERENCE:
|
|
|
|
+ - default
|
|
|
|
+ FEATURE_MAILING: false
|
|
|
|
+ SECRET_KEY: 7ce58d4d-b6f5-4400-ba6b-77b9f728a115
|
|
|
|
+ SERVER_HOSTNAME: registry.ocp4.example.com
|
|
|
|
+ PREFERRED_URL_SCHEME: https
|
|
|
|
+ SETUP_COMPLETE: true
|
|
|
|
+ SUPER_USERS:
|
|
|
|
+ - admin
|
|
|
|
+ TESTING: false
|
|
|
|
+ USER_EVENTS_REDIS:
|
|
|
|
+ host: redis
|
|
|
|
+ password: verysecret
|
|
|
|
+ port: 6379
|
|
|
|
+ mode: 0660
|
|
|
|
+
|
|
|
|
+ # TODO: recursive!
|
|
|
|
+ - name: Ensure Quay data dirs are owned by the correct user.
|
|
|
|
+ become_method: containers.podman.podman_unshare
|
|
|
|
+ become: yes
|
|
|
|
+ ansible.builtin.file:
|
|
|
|
+ path: "{{ item }}"
|
|
|
|
+ state: directory
|
|
|
|
+ owner: 1001
|
|
|
|
+ loop:
|
|
|
|
+ - /local/quay
|
|
|
|
+ - "{{ ansible_facts['user_dir'] }}/config"
|
|
|
|
+
|
|
|
|
+ - name: Ensure systemd user dir is there.
|
|
|
|
+ ansible.builtin.file:
|
|
|
|
+ path: "{{ ansible_facts['user_dir'] }}/.config/systemd/user"
|
|
|
|
+ state: directory
|
|
|
|
+
|
|
|
|
+ - name: Deploy service units.
|
|
|
|
+ ansible.builtin.template:
|
|
|
|
+ dest: "{{ ansible_facts['user_dir'] }}/.config/systemd/user/{{ item }}"
|
|
|
|
+ src: "templates/{{ item }}.j2"
|
|
|
|
+ loop:
|
|
|
|
+ - quay-pg.service
|
|
|
|
+ - quay-redis.service
|
|
|
|
+ - quay.service
|
|
|
|
+
|
|
|
|
+ - name: Reload systemd.
|
|
|
|
+ ansible.builtin.systemd_service:
|
|
|
|
+ daemon_reload: yes
|
|
|
|
+ scope: user
|
|
|
|
+
|
|
|
|
+ - name: Enable services and start them.
|
|
|
|
+ ansible.builtin.systemd_service:
|
|
|
|
+ name: "{{ item }}"
|
|
|
|
+ scope: user
|
|
|
|
+ state: started
|
|
|
|
+ enabled: yes
|
|
|
|
+ loop:
|
|
|
|
+ - quay-pg
|
|
|
|
+ - quay-redis
|
|
|
|
+ - quay
|
|
|
|
+ register: startup
|
|
|
|
+
|
|
|
|
+ - name: Wait a bit if the Quay container was just started.
|
|
|
|
+ ansible.builtin.uri:
|
|
|
|
+ method: GET
|
|
|
|
+ url: https://registry.ocp4.example.com/
|
|
|
|
+ headers:
|
|
|
|
+ Accept: application/json
|
|
|
|
+ Content-Type: application/json
|
|
|
|
+ validate_certs: no
|
|
|
|
+ status_code:
|
|
|
|
+ - 200
|
|
|
|
+ - 404
|
|
|
|
+ - 502
|
|
|
|
+ when: startup.results[2].changed
|
|
|
|
+ register: startup_wait
|
|
|
|
+ until: startup_wait.status == 200
|
|
|
|
+ retries: 30
|
|
|
|
+ delay: 5
|
|
|
|
+
|
|
|
|
+ - name: Check if the admin user exists already.
|
|
|
|
+ ansible.builtin.uri:
|
|
|
|
+ method: GET
|
|
|
|
+ url: https://registry.ocp4.example.com/api/v1/users/admin
|
|
|
|
+ headers:
|
|
|
|
+ Accept: application/json
|
|
|
|
+ Content-Type: application/json
|
|
|
|
+ validate_certs: no
|
|
|
|
+ status_code:
|
|
|
|
+ - 200
|
|
|
|
+ - 404
|
|
|
|
+ return_content: yes
|
|
|
|
+ register: adminuser_is_there
|
|
|
|
+
|
|
|
|
+ - name: Create an admin user if not yet there.
|
|
|
|
+ block:
|
|
|
|
+ - name: Obtain an encoded CSRF token.
|
|
|
|
+ ansible.builtin.uri:
|
|
|
|
+ method: GET
|
|
|
|
+ url: https://registry.ocp4.example.com/
|
|
|
|
+ headers:
|
|
|
|
+ Accept: application/json
|
|
|
|
+ Content-Type: application/json
|
|
|
|
+ validate_certs: no
|
|
|
|
+ return_content: yes
|
|
|
|
+ ignore_errors: yes
|
|
|
|
+ register: csrf_token_payload
|
|
|
|
+
|
|
|
|
+ - ansible.builtin.assert:
|
|
|
|
+ that:
|
|
|
|
+ - csrf_token_payload.cookies['_csrf_token'] is defined
|
|
|
|
+ fail_msg: "No CSRF token returned by registry. Can not proceed."
|
|
|
|
+ success_msg: "Good, CSRF token found in response."
|
|
|
|
+
|
|
|
|
+ # In case of issues, run with -v and this will show the raw cookie.
|
|
|
|
+ - ansible.builtin.debug:
|
|
|
|
+ var: csrf_token_payload.cookies
|
|
|
|
+ verbosity: 1
|
|
|
|
+
|
|
|
|
+ - name: Store the cookie as a new fact. We need it later.
|
|
|
|
+ ansible.builtin.set_fact:
|
|
|
|
+ csrf_cookie: "{{ csrf_token_payload.cookies['_csrf_token'] }}"
|
|
|
|
+
|
|
|
|
+ # In case of issues, run with -v and this will show the cookie payload.
|
|
|
|
+ - ansible.builtin.debug:
|
|
|
|
+ var: csrf_cookie
|
|
|
|
+ verbosity: 1
|
|
|
|
+
|
|
|
|
+ # Must chop out the part of the token before the first dot (the rest is control shit).
|
|
|
|
+ # Next, and pad it (==) at the end to have 112 characters (no checking done here).
|
|
|
|
+ # Lastly, convert that from JSON to a dict and obtain the value of the token (_csrf_token).
|
|
|
|
+ - name: Store CSRF token as a new fact.
|
|
|
|
+ ansible.builtin.set_fact:
|
|
|
|
+ csrf_token: "{{ (csrf_token_payload.cookies['_csrf_token'] | ansible.builtin.regex_replace('^(\\w+)\\..*$', '\\1==') | ansible.builtin.b64decode | ansible.builtin.from_json)['_csrf_token'] }}"
|
|
|
|
+
|
|
|
|
+ # In case of issues, run with -v and this will show the decoded token.
|
|
|
|
+ - ansible.builtin.debug:
|
|
|
|
+ var: csrf_token
|
|
|
|
+ verbosity: 1
|
|
|
|
+
|
|
|
|
+ - name: Send a POST request to registry API to create the admin user.
|
|
|
|
+ ansible.builtin.uri:
|
|
|
|
+ method: POST
|
|
|
|
+ url: https://registry.ocp4.example.com/api/v1/user/
|
|
|
|
+ headers:
|
|
|
|
+ Accept: application/json
|
|
|
|
+ Content-Type: application/json
|
|
|
|
+ Cookie: _csrf_token={{ csrf_cookie }}
|
|
|
|
+ X-CSRF-Token: "{{ csrf_token }}"
|
|
|
|
+ body: |
|
|
|
|
+ {
|
|
|
|
+ "username": "admin",
|
|
|
|
+ "password": "redhat123",
|
|
|
|
+ "repeatPassword": "redhat123",
|
|
|
|
+ "email": "admin@example.com"
|
|
|
|
+ }
|
|
|
|
+ body_format: json
|
|
|
|
+ validate_certs: no
|
|
|
|
+ return_content: yes
|
|
|
|
+ register: admin_user_response
|
|
|
|
+
|
|
|
|
+ # In case of issues, run with -v and this will show the response.
|
|
|
|
+ - ansible.builtin.debug:
|
|
|
|
+ var: admin_user_response
|
|
|
|
+ verbosity: 1
|
|
|
|
+
|
|
|
|
+ when: adminuser_is_there.status == 404
|
|
|
|
+...
|