Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add pulp services #34

Merged
merged 18 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion playbooks/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,21 @@
httpd_client_ca_certificate: "{{ certificates_ca_directory }}/certs/ca.crt"
httpd_server_certificate: "{{ certificates_ca_directory }}/certs/{{ certificates_server }}.crt"
httpd_server_key: "{{ certificates_ca_directory }}/private/{{ certificates_server }}.key"
pulp_db_password: "CHANGEME"
postgresql_databases:
- name: candlepin
owner: candlepin
- name: foreman
owner: foreman
- name: pulp
owner: pulp
postgresql_users:
- name: candlepin
password: "{{ candlepin_db_password }}"
- name: foreman
password: "{{ foreman_db_password }}"
- name: pulp
password: "{{ pulp_db_password }}"
postgresql_hba_entries:
- { type: local, database: all, user: postgres, auth_method: ident }
- { type: local, database: all, user: all, auth_method: ident }
Expand All @@ -47,9 +52,9 @@
roles:
- certificates
- geerlingguy.postgresql
- redis
- candlepin
- httpd
- pulp
- foreman_proxy
- redis
- foreman
4 changes: 2 additions & 2 deletions roles/httpd/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
httpd_ssl_dir: /etc/pki/httpd
httpd_pulp_api_backend: http://localhost:8080
httpd_pulp_content_backend: http://localhost:8080
httpd_pulp_api_backend: http://localhost:24817
httpd_pulp_content_backend: http://localhost:24816
httpd_foreman_backend: http://localhost:3000
24 changes: 16 additions & 8 deletions roles/pulp/defaults/main.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
---
pulp_image: quay.io/pulp/pulp:stable
pulp_ports:
- "8080:80"
pulp_image: quay.io/pulp/pulp-minimal:stable
pulp_api_image: "{{ pulp_image }}"
pulp_content_image: "{{ pulp_image }}"
pulp_worker_image: "{{ pulp_image }}"

pulp_api_ports:
- "24817:80"
pulp_content_ports:
- "24816:80"
ehelms marked this conversation as resolved.
Show resolved Hide resolved
ehelms marked this conversation as resolved.
Show resolved Hide resolved
pulp_worker_count: 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also unused, but I think this could still be needed to tune the worker count.


pulp_volumes:
- /var/lib/pulp/settings:/etc/pulp:Z
- /var/lib/pulp/pulp_storage:/var/lib/pulp:Z
- /var/lib/pulp/pgsql:/var/lib/pgsql:Z
- /var/lib/pulp/containers:/var/lib/containers:Z
pulp_container_name: pulp
- /var/lib/pulp/pulp_storage:/var/lib/pulp
archanaserver marked this conversation as resolved.
Show resolved Hide resolved

pulp_api_container_name: pulp-api
pulp_content_container_name: pulp-content
pulp_worker_container_name: pulp-worker
140 changes: 128 additions & 12 deletions roles/pulp/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
- name: Pull the Pulp container image
- name: Pull the Pulp API container image
containers.podman.podman_image:
name: "{{ pulp_image }}"
name: "{{ pulp_api_image }}"
state: present

- name: Pull the Pulp Content container image
containers.podman.podman_image:
name: "{{ pulp_content_image }}"
state: present

- name: Pull the Pulp Worker container image
containers.podman.podman_image:
name: "{{ pulp_worker_image }}"
state: present

- name: Create Pulp storage
Expand All @@ -10,44 +20,150 @@
mode: "0755"
loop: "{{ pulp_volumes }}"

- name: Create Pulp storage subdirs
ansible.builtin.file:
path: "/var/lib/pulp/pulp_storage/{{ item }}"
ehelms marked this conversation as resolved.
Show resolved Hide resolved
state: directory
mode: "0755"
loop:
- tmp
- assets

- name: Create settings config secret
containers.podman.podman_secret:
state: present
name: pulp-settings-py
data: "{{ lookup('ansible.builtin.template', 'settings.py.j2') }}"

- name: Deploy Pulp Container
- name: Generate database symmetric key
ansible.builtin.command: "bash -c 'openssl rand -base64 32 | tr \"+/\" \"-_\" > /var/lib/pulp/database_fields.symmetric.key'"
args:
creates: /var/lib/pulp/database_fields.symmetric.key

- name: Load database symmetric key
ansible.builtin.slurp:
src: /var/lib/pulp/database_fields.symmetric.key
register: pulp_key

- name: Create database symmetric key secret
containers.podman.podman_secret:
state: present
name: pulp-symmetric-key
data: "{{ pulp_key['content'] | b64decode }}"

- name: Wait for PostgreSQL to be ready
ansible.builtin.wait_for:
host: "localhost"
port: 5432
timeout: 300

- name: Deploy Pulp API Container
containers.podman.podman_container:
name: "{{ pulp_container_name }}"
image: "{{ pulp_image }}"
name: "{{ pulp_api_container_name }}"
image: "{{ pulp_api_image }}"
state: quadlet
archanaserver marked this conversation as resolved.
Show resolved Hide resolved
ports: "{{ pulp_ports }}"
command: pulp-api
network: host
volumes: "{{ pulp_volumes }}"
security_opt:
- "label=disable"
secrets:
- 'pulp-settings-py,type=mount,target=/etc/pulp/settings.py'
- 'pulp-symmetric-key,type=mount,target=/etc/pulp/certs/database_fields.symmetric.key'
quadlet_options:
- |
[Install]
WantedBy=default.target
archanaserver marked this conversation as resolved.
Show resolved Hide resolved
Wants=postgresql.service
[Service]
Restart=always
RestartSec=3

- name: Deploy Pulp Content Container
containers.podman.podman_container:
name: "{{ pulp_content_container_name }}"
image: "{{ pulp_content_image }}"
state: quadlet
command: pulp-content
network: host
volumes: "{{ pulp_volumes }}"
security_opt:
- "label=disable"
secrets:
- 'pulp-settings-py,type=mount,target=/etc/pulp/settings.py'
- 'pulp-symmetric-key,type=mount,target=/etc/pulp/certs/database_fields.symmetric.key'
quadlet_options:
- |
[Install]
WantedBy=default.target
Wants=postgresql.service
[Service]
Restart=always
RestartSec=3

- name: Deploy Pulp Worker Container
containers.podman.podman_container:
name: "{{ pulp_worker_container_name }}"
image: "{{ pulp_worker_image }}"
state: quadlet
command: pulp-worker
network: host
volumes: "{{ pulp_volumes }}"
security_opt:
- "label=disable"
secrets:
- 'pulp-settings-py,type=mount,target=/etc/pulp/settings.py'
- 'pulp-symmetric-key,type=mount,target=/etc/pulp/certs/database_fields.symmetric.key'
quadlet_options:
- |
[Install]
evgeni marked this conversation as resolved.
Show resolved Hide resolved
WantedBy=default.target
Wants=postgresql.service
[Service]
Restart=always
RestartSec=3

- name: Run daemon reload to make Quadlet create the service files
ansible.builtin.systemd:
daemon_reload: true

- name: Start the Pulp Service
- name: Start the Pulp API services
ansible.builtin.systemd:
name: pulp
name: pulp-api
enabled: true
state: restarted
state: started

- name: Migrate the Pulp database
ehelms marked this conversation as resolved.
Show resolved Hide resolved
containers.podman.podman_container_exec:
name: "{{ pulp_api_container_name }}"
command: pulpcore-manager migrate --noinput
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should be an own command (like now), an own service (like https://github.com/pulp/pulp-oci-images/blob/latest/images/compose/compose.yml does it) or whether we should use pulpcore-manager migrate --noinput && pulp-api as the command for the "normal" API container startup (like we do for Foreman)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess while combining the migration and API startup commands seems convenient, but it will create challenges in error handling and clearly not flexible.
And I think having a dedicated migration service would be a better approach for us. It allows for more flexible scaling of the migration process independently from the API and other services, making everything easier to manage in the long run.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see some benefits in having tighter control, but you must migrate the DB before starting any of the Pulp services.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for now but I think we should understand and decide how we handle this in a universal way given we have multiple applications having to do the same workflow: wait on database, apply outstanding migrations. Candlepin does this via the application startup procedure and so this will happen within the container itself.

Pulp's images have a wait for database and wait for migrations script that they currenly rely upon before startup: https://github.com/pulp/pulp-oci-images/blob/d74b7bbb9550152c0de1e45f16eec73f674e8f15/images/assets/pulp-api#L3-L4

The Foreman container being used takes a similar approach:

CMD /usr/share/foreman/bin/rails db:migrate && /usr/share/foreman/bin/rails db:seed && /usr/share/foreman/bin/rails server --environment production


- name: Wait for Pulp service to be accessible
- name: Wait for Pulp API service to be accessible
ansible.builtin.wait_for:
host: "{{ ansible_hostname }}"
port: 8080
port: 24817
timeout: 300

- name: Start the Pulp Content services
ansible.builtin.systemd:
name: pulp-content
enabled: true
state: started

- name: Wait for Pulp Content service to be accessible
ansible.builtin.wait_for:
host: "{{ ansible_hostname }}"
port: 24816
timeout: 600

- name: Start the Pulp Worker service
ansible.builtin.systemd:
name: pulp-worker
enabled: true
state: started

# Only needed until we have cert auth configured
- name: Set Pulp admin password
containers.podman.podman_container_exec:
name: "{{ pulp_container_name }}"
name: "{{ pulp_api_container_name }}"
command: pulpcore-manager reset-admin-password --password CHANGEME
15 changes: 14 additions & 1 deletion roles/pulp/templates/settings.py.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
CONTENT_ORIGIN="http://{{ ansible_hostname }}:8080"
CONTENT_ORIGIN="http://{{ ansible_hostname }}:24816"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK this should be wherever Apache (httpd) presents the content, so end user clients talk to. They shouldn't talk to the internal endpoint.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand, would you mind expanding a little more?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quoting https://pulpproject.org/pulpcore/docs/admin/reference/settings/#content_origin

A required string containing the protocol, fqdn, and port where the content app is reachable by users. This is used by pulpcore and various plugins when referring users to the content app. For example if the API should refer users to content at using http to pulp.example.com on port 24816, (the content default port), you would set: https://pulp.example.com:24816.

In our deployment we have httpd in front of Pulp which presents it at on https://{{ ansible_hostname }}. In other words, this was always wrong, but I'm noticing it now.

Copy link
Collaborator Author

@archanaserver archanaserver Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ekohl so we basically have to set it up without port? if we want user to point at the right place? to this CONTENT_ORIGIN="https://{{ ansible_hostname }}" (sorry pinging on this again, i'm not good at this part yet)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what we do in puppet-pulpcore, yeah:
https://github.com/theforeman/puppet-pulpcore/blob/master/templates/settings.py.erb#L20

You could also argue, that this sort-of blurs the lines between the roles (the plain, port 443, HTTPS is not available w/o the httpd role), and the pulp role should have a default for the content origin being http://{{ ansible_fqdn }}:port, and then we override this in deploy.yaml with https://{{ ansible_fqdn }} as that's the value the overall deployment uses

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it should be a variable on the role that the playbook sets (because that knows both httpd and pulp)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/variable/default/, but yeah, that's how I'd do it.

CACHE_ENABLED=True
REDIS_HOST="localhost"
REDIS_PORT=6379
ekohl marked this conversation as resolved.
Show resolved Hide resolved
REDIS_DB=8

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'pulp',
'USER': 'pulp',
'PASSWORD': '{{ pulp_db_password }}',
'HOST': 'localhost',
evgeni marked this conversation as resolved.
Show resolved Hide resolved
'PORT': '5432',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.djangoproject.com/en/5.1/ref/settings/#std-setting-PORT says an empty string (default) means the default port. IMHO there's no value in hardcoding the default port

Suggested change
'PORT': '5432',

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit unsure why my suggestion wasn't followed. IMHO the config should be as short as possible and not copy all the defaults.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, i did updated it 3d1f220

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion was to remove the line.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, i understand now! sorry, updating it in another PR

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

AUTHENTICATION_BACKENDS=['pulpcore.app.authentication.PulpNoCreateRemoteUserBackend']
REMOTE_USER_ENVIRON_NAME="HTTP_REMOTE_USER"
REST_FRAMEWORK__DEFAULT_AUTHENTICATION_CLASSES=('rest_framework.authentication.SessionAuthentication', 'pulpcore.app.authentication.PulpRemoteUserAuthentication')
44 changes: 25 additions & 19 deletions tests/pulp_test.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
import json

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This empty line is common in Python. It separates the built in modules (json) from third party ones (pytest). isort will place it back.

import pytest


PULP_HOST = 'localhost'
PULP_PORT = 8080

PULP_API_PORT = 24817
PULP_CONTENT_PORT = 24816

@pytest.fixture(scope="module")
def pulp_status_curl(host):
return host.run(f"curl -k -s -w '%{{stderr}}%{{http_code}}' http://{PULP_HOST}:{PULP_PORT}/pulp/api/v3/status/")

return host.run(f"curl -k -s -w '%{{stderr}}%{{http_code}}' http://{PULP_HOST}:{PULP_API_PORT}/pulp/api/v3/status/")

@pytest.fixture(scope="module")
def pulp_status(pulp_status_curl):
return json.loads(pulp_status_curl.stdout)

def test_pulp_api_service(host):
pulp_api = host.service("pulp-api")
assert pulp_api.is_running
assert pulp_api.is_enabled

def test_pulp_service(host):
pulp = host.service("pulp")
assert pulp.is_running
assert pulp.is_enabled
def test_pulp_content_service(host):
pulp_content = host.service("pulp-content")
assert pulp_content.is_running
assert pulp_content.is_enabled

def test_pulp_worker_service(host):
pulp_worker = host.service("pulp-worker")
assert pulp_worker.is_running
assert pulp_worker.is_enabled

def test_pulp_port(host):
pulp = host.addr(PULP_HOST)
assert pulp.port(PULP_PORT).is_reachable


def test_pulp_status(pulp_status_curl):
assert pulp_status_curl.succeeded
assert pulp_status_curl.stderr == '200'
ehelms marked this conversation as resolved.
Show resolved Hide resolved
def test_pulp_api_port(host):
pulp_api = host.addr(PULP_HOST)
assert pulp_api.port(PULP_API_PORT).is_reachable

def test_pulp_content_port(host):
pulp_content = host.addr(PULP_HOST)
assert pulp_content.port(PULP_CONTENT_PORT).is_reachable

def test_pulp_status_database_connection(pulp_status):
assert pulp_status['database_connection']['connected']
Expand All @@ -54,6 +57,9 @@ def test_pulp_status_workers(pulp_status):

@pytest.mark.xfail(reason='password auth is deactivated when we use cert auth')
def test_pulp_admin_auth(host):
cmd = host.run(f"curl --silent --write-out '%{{stderr}}%{{http_code}}' --user admin:CHANGEME http://{PULP_HOST}:{PULP_PORT}/pulp/api/v3/users/")
cmd = host.run(f"curl --silent --write-out '%{{stderr}}%{{http_code}}' --user admin:CHANGEME http://{PULP_HOST}:{PULP_API_PORT}/pulp/api/v3/users/")
assert cmd.succeeded
assert cmd.stderr == '200'

def test_pulp_volumes(host):
assert host.file("/var/lib/pulp").is_directory