Skip to content

Support capsule deploy via foremanctl with dedicated test and fixture updates - #22278

Open
Gauravtalreja1 wants to merge 3 commits into
SatelliteQE:masterfrom
Gauravtalreja1:capsule-setup-with-foremanctl
Open

Support capsule deploy via foremanctl with dedicated test and fixture updates#22278
Gauravtalreja1 wants to merge 3 commits into
SatelliteQE:masterfrom
Gauravtalreja1:capsule-setup-with-foremanctl

Conversation

@Gauravtalreja1

@Gauravtalreja1 Gauravtalreja1 commented Jul 27, 2026

Copy link
Copy Markdown
Member

Problem Statement

Robottelo currently only supports capsule setup via satellite-installer. With the introduction of foremanctl the test framework needs to support deploying and testing capsules using satellitectl deploy-proxy alongside the traditional installer path.

Solution

  • Extended Capsule.capsule_setup() in robottelo/hosts.py to support both InstallMethod.INSTALLER and InstallMethod.FOREMANCTL paths:
    • FOREMANCTL path: uses satellitectl package, sets up satellite repos, enables Packit PRs, and runs satellitectl deploy-proxy
    • INSTALLER path: unchanged existing behavior
  • Updated Satellite.capsule_certs_generate() to use satellitectl auth-bundle for certificate generation under FOREMANCTL, returning the appropriate cert path and deploy command
  • Added container_rpm_name = 'satellitectl' class attribute to both Capsule and Satellite
  • Added a new test test_capsule_installation_with_foremanctl with fixtures for bare RHEL capsule deply, repo sync, and post-install assertions
  • Added @pytest.mark.e2e and @pytest.mark.pit_server markers to the satellite and capsule installation tests
  • Renamed certificate-bundle with auth-bundle in tests

Related Issues

theforeman/foremanctl#571
theforeman/foremanctl#698

Summary by Sourcery

Add support for deploying and testing capsules via foremanctl alongside the existing installer-based workflow.

New Features:

  • Introduce a foremanctl-based Capsule installation workflow using satellitectl deploy-proxy and auth bundles.
  • Add a new end-to-end test that installs a Capsule with foremanctl, including repo sync, registration, and health checks.

Enhancements:

  • Extend Capsule and Satellite host helpers to select the appropriate package (installer vs satellitectl) and handle both installation methods.
  • Update capsule certificate generation to use satellitectl auth-bundle and adapt tests to the new bundle contents and CLI.
  • Add shared assertions and fixtures for validating Capsule services, logs, and repository configuration in foremanctl scenarios.

Tests:

  • Add fixtures for provisioning bare RHEL Capsule hosts, syncing Capsule-related content, and running Capsule installation via foremanctl.
  • Mark Satellite and Capsule foremanctl installation tests as e2e and pit_server for targeted execution.
  • Rename and update certificate bundle tests to cover the new foremanctl auth-bundle generation and renewal flow.

@Gauravtalreja1 Gauravtalreja1 self-assigned this Jul 27, 2026
@Gauravtalreja1
Gauravtalreja1 requested a review from a team as a code owner July 27, 2026 11:04
@Gauravtalreja1 Gauravtalreja1 added the QETestCoverage Issues and PRs relating to a Satellite bug label Jul 27, 2026
@Gauravtalreja1
Gauravtalreja1 requested a review from a team as a code owner July 27, 2026 11:04
@Gauravtalreja1 Gauravtalreja1 added No-CherryPick PR doesnt need CherryPick to previous branches Stream Introduced in or relating directly to Satellite Stream/Master labels Jul 27, 2026
@sourcery-ai

sourcery-ai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds support for deploying capsules via foremanctl/satellitectl alongside the existing installer flow, updates certificate generation to use auth-bundles in foremanctl mode, and introduces new E2E tests and fixtures to validate capsule deployment and auth-bundle behavior.

Sequence diagram for capsule deployment via foremanctl

sequenceDiagram
    autonumber
    actor Operator
    participant Capsule as Capsule.capsule_setup
    participant Satellite as Satellite.capsule_certs_generate
    participant Broker as Broker.upstream-pr-install
    participant Satellitectl as satellitectl

    Operator->>Capsule: capsule_setup(release, capsule_cert_opts)
    Capsule->>Capsule: register_to_cdn()
    Capsule->>Capsule: setup_rhel_repos()
    Capsule->>Capsule: is_foremanctl_available()
    alt InstallMethod.FOREMANCTL
        Capsule->>Capsule: setup_satellite_repos()
        Capsule->>Broker: execute() [pull_requests present]
        Capsule->>Capsule: execute('dnf install -y satellitectl')
        Capsule->>Capsule: execute('rpm -q satellitectl')
        Capsule->>Satellite: capsule_certs_generate(capsule)
        Satellite->>Satellitectl: execute('satellitectl auth-bundle hostname')
        Satellite-->>Capsule: cert_file_path, install_cmd('satellitectl deploy-proxy ...')
        Capsule->>Capsule: execute(install_cmd)
        Capsule->>Capsule: execute('systemctl status foreman-proxy.service foreman.target')
    else InstallMethod.INSTALLER
        Capsule->>Capsule: setup_capsule_repos(release)
        Capsule->>Satellite: capsule_certs_generate(capsule)
        Satellite->>Satellite: install(InstallerCommand(... 'capsule-certs-generate'))
        Satellite-->>Capsule: cert_file_path, InstallerCommand
        Capsule->>Capsule: install(InstallerCommand)
        Capsule->>Capsule: execute('satellite-maintain service status')
    end
Loading

File-Level Changes

Change Details Files
Extend capsule_setup to support both installer- and foremanctl-based capsule deployment using satellitectl.
  • Add container_rpm_name='satellitectl' to Capsule and use it as the product RPM when foremanctl is available.
  • Branch capsule_setup logic on settings.server.install_method to either use traditional capsule repos and installer or satellite repos plus satellitectl deployment.
  • Install satellitectl (or product_rpm_name) on the capsule, verify the package is present, and handle Packit PR repo enablement when using foremanctl.
  • Split post-install behavior: keep installer-based flow (capsule-certs-generate and satellite-maintain) and add a new foremanctl deploy-proxy path with enhanced log collection and service checks.
robottelo/hosts.py
Update Satellite.capsule_certs_generate to support foremanctl auth-bundle and deploy-proxy while preserving the installer path.
  • Introduce container_rpm_name='satellitectl' on Satellite for containerized deployments.
  • In foremanctl mode, call satellitectl auth-bundle to generate capsule auth bundles and construct a satellitectl deploy-proxy command using the generated tarball and Satellite FQDN.
  • Retain existing capsule-certs-generate flow for installer mode and keep returning the cert path, raw result, and installer command object.
robottelo/hosts.py
Add E2E capsule installation test via foremanctl and shared capsule assertions, including repo sync and health checks.
  • Introduce common_cap_install_assertions to validate foreman-proxy and related services, journald errors, and httpd logs on capsule hosts.
  • Add sync_capsule_repos helper to configure and sync required RHEL and Capsule repos (CDN/internal/nightly/dogfood) and update activation key content overrides.
  • Create module_cap_ready_rhel fixture that provisions a bare RHEL Capsule host, configures IPv6 proxies, ensures podman, unregisters from CDN, and installs satellitectl for tests.
  • Implement test_capsule_installation_with_foremanctl that wires Satellite and Capsule, generates auth bundle/certs, syncs content, registers capsule, runs deploy-proxy, asserts capsule registration and health, and runs satellitectl health.
  • Mark satellite and capsule installation tests with @pytest.mark.e2e and @pytest.mark.pit_server and adjust capsule hostname/flags for build_sanity runs.
tests/foreman/foremanctl/test_install_foremanctl.py
robottelo/hosts.py
Rename and extend certificate-bundle tests to auth-bundle, ensuring OAuth credentials and renew/validation behavior for capsules in foremanctl mode.
  • Update helper foremanctl_capsule_cert_paths to validate presence of OAuth consumer key/secret and adjust messaging for auth bundles.
  • Rename test_positive_foremanctl_certificate_bundle to test_positive_foremanctl_auth_bundle and update docstring steps/expected results to refer to auth-bundle instead of certificate-bundle.
  • Switch test logic from foremanctl certificate-bundle to foremanctl auth-bundle and adjust renewal command, error messages, and validation steps accordingly.
tests/foreman/foremanctl/test_install_foremanctl.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In Satellite.capsule_certs_generate, the FOREMANCTL branch ignores extra_kwargs and always uses a hardcoded deploy-proxy command; if callers rely on passing options via extra_kwargs this will behave differently than the installer path—consider either supporting equivalent arguments or clearly separating/renaming the APIs to avoid confusion.
  • The logic in Capsule.capsule_setup mixes settings.server.install_method with is_foremanctl_available() to derive product_rpm_name, but only install_method drives the repo setup and installer flow; this can lead to situations where rpm -q satellitectl is expected even for INSTALLER-based runs—consider basing the package name solely on install_method or making the decision path more explicit.
  • There is duplicated satellitectl setup between the module_cap_ready_rhel fixture and the new FOREMANCTL branch in Capsule.capsule_setup (both configure repos and install satellitectl); consolidating this into a single helper will reduce divergence and make it clearer which layer is responsible for preparing the capsule host.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `Satellite.capsule_certs_generate`, the FOREMANCTL branch ignores `extra_kwargs` and always uses a hardcoded `deploy-proxy` command; if callers rely on passing options via `extra_kwargs` this will behave differently than the installer path—consider either supporting equivalent arguments or clearly separating/renaming the APIs to avoid confusion.
- The logic in `Capsule.capsule_setup` mixes `settings.server.install_method` with `is_foremanctl_available()` to derive `product_rpm_name`, but only `install_method` drives the repo setup and installer flow; this can lead to situations where `rpm -q satellitectl` is expected even for INSTALLER-based runs—consider basing the package name solely on `install_method` or making the decision path more explicit.
- There is duplicated satellitectl setup between the `module_cap_ready_rhel` fixture and the new FOREMANCTL branch in `Capsule.capsule_setup` (both configure repos and install `satellitectl`); consolidating this into a single helper will reduce divergence and make it clearer which layer is responsible for preparing the capsule host.

## Individual Comments

### Comment 1
<location path="robottelo/hosts.py" line_range="2006-2008" />
<code_context>
         self.register_to_cdn()
         self.setup_rhel_repos()
-        self.setup_capsule_repos(release=release)
+        product_rpm_name = (
+            self.container_rpm_name if self.is_foremanctl_available() else self.product_rpm_name
+        )
+        # TODO: Remove this condition once foremanctl is available in capsule repos
+        if settings.server.install_method == InstallMethod.INSTALLER:
</code_context>
<issue_to_address>
**issue (bug_risk):** Align product RPM selection with install method to avoid mismatched package checks

`product_rpm_name` is currently derived only from `is_foremanctl_available()`, not from `settings.server.install_method`. However, you always run `rpm -q {product_rpm_name}` while only installing it in the non-INSTALLER path. If `is_foremanctl_available()` returns `True` while `install_method` is `INSTALLER`, the Installer flow could end up verifying `satellitectl` without ever installing it.

Consider either binding RPM selection directly to `install_method` (FOREMANCTL → `container_rpm_name`, INSTALLER → `product_rpm_name`) or defining separate variables (e.g. `capsule_rpm_name` vs `container_rpm_name`) and using each explicitly per branch to avoid this coupling.
</issue_to_address>

### Comment 2
<location path="robottelo/hosts.py" line_range="2069-2078" />
<code_context>
+                raise CapsuleHostError(
+                    f'A core service is not running at capsule host\n{result.stdout}'
+                )
+        if settings.server.install_method == InstallMethod.FOREMANCTL:
+            result = self.execute(installer)
+            if result.status:
</code_context>
<issue_to_address>
**issue (bug_risk):** Respect the `cert_path` argument or clarify its semantics in FOREMANCTL mode

In FOREMANCTL mode, the bundle path is hardcoded to `/var/lib/foremanctl/certs/bundles/{capsule.hostname}.tar.gz`, so the optional `cert_path` argument is effectively ignored, unlike in the Installer branch where it is used.

To avoid surprising callers that rely on `cert_path`, either:
- Use `cert_path` when provided, falling back to the FOREMANCTL default, or
- Reject a non-`None` `cert_path` in FOREMANCTL mode so misuse fails fast rather than being silently ignored.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread robottelo/hosts.py
Comment thread robottelo/hosts.py
@Gauravtalreja1

Copy link
Copy Markdown
Member Author
trigger: test-robottelo
pytest: tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl
deployment_type: container
theforeman:
  foremanctl: 698

Comment thread robottelo/hosts.py
Comment thread robottelo/hosts.py
@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16255
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl --external-logging
Test Result : ========== 21 deselected, 24 warnings, 1 error in 5225.65s (1:27:05) ===========

@Satellite-QE Satellite-QE added the PRT-Failed Indicates that latest PRT run is failed for the PR label Jul 27, 2026
@Gauravtalreja1

Copy link
Copy Markdown
Member Author
trigger: test-robottelo
pytest: tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl
deployment_type: container
theforeman:
  foremanctl: 698

Comment thread robottelo/hosts.py
Comment thread tests/foreman/foremanctl/test_install_foremanctl.py Outdated
@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16263
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl --external-logging
Test Result : ========== 21 deselected, 16 warnings, 1 error in 2860.97s (0:47:40) ===========

@evgeni

evgeni commented Jul 28, 2026

Copy link
Copy Markdown
Member
    This system is not registered with an entitlement server. You can use "rhc" or "subscription-manager" to register.
    
    Satellite Stream RHEL9 - Snap 202.0              19 MB/s | 892 kB     00:00    
    Satellite Maintenance Stream RHEL9 - Snap 202.0 785 kB/s |  26 kB     00:00    
    (try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages)
    
    stderr:
    Error: 
     Problem: package satellitectl-6.20.0-2.el9sat.noarch from Satellite_Engineering_Satellite_stream_Composes_Satellite_stream_RHEL9 requires foremanctl, but none of the providers can be installed
      - package foremanctl-3.0.0.develop-2.20260723010445205895.master.134.g204a35e.el9sat.noarch from Satellite_Engineering_Satellite_stream_Composes_Satellite_stream_RHEL9 requires python3.12-obsah >= 1.8.1, but none of the providers can be installed
      - conflicting requests
      - nothing provides /usr/bin/python3.12 needed by python3.12-obsah-1.10.0-1.el9sat.noarch from Satellite_Engineering_Satellite_stream_Composes_Satellite_stream_RHEL9
      - nothing provides python(abi) = 3.12 needed by python3.12-obsah-1.10.0-1.el9sat.noarch from Satellite_Engineering_Satellite_stream_Composes_Satellite_stream_RHEL9

Seems the system has no access to BaseOS repos and thus fails to install things?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR extends Robottelo’s host helpers and foremanctl test coverage to support deploying Capsules via the new satellitectl deploy-proxy/auth-bundle workflow, alongside the existing satellite-installer-based approach.

Changes:

  • Add a new end-to-end foremanctl Capsule installation test (with repo sync + health/log assertions).
  • Extend Capsule.capsule_setup() and Satellite.capsule_certs_generate() to support a FOREMANCTL install path using satellitectl auth-bundle and satellitectl deploy-proxy.
  • Update existing foremanctl certificate bundle tests to use auth-bundle semantics and validate oauth artifacts in the bundle.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
tests/foreman/foremanctl/test_install_foremanctl.py Adds Capsule foremanctl install E2E test/fixtures and renames bundle tests to auth-bundle.
robottelo/hosts.py Adds Capsule/Satellite satellitectl install support and foremanctl-based cert/bundle generation + deploy flow.
Comments suppressed due to low confidence (2)

robottelo/hosts.py:2051

  • For the foremanctl flow, capsule_certs_generate() returns a bundle path under /var/lib/foremanctl/certs/bundles/.... session.remote_copy() expects the destination directory to exist on the Capsule (see other call sites that mkdir -p before copying). On a fresh Capsule host this directory likely won’t exist yet, causing the copy (and then deploy) to fail.
        certs_tar, _, installer = self.satellite.capsule_certs_generate(self, **capsule_cert_opts)
        self.satellite.session.remote_copy(certs_tar, self)

robottelo/hosts.py:2087

  • Same string-join issue as above: "\n".join(result.stdout) will prevent detecting inactive (dead) in the systemctl status output.
            result = self.execute('systemctl status foreman-proxy.service foreman.target')
            if 'inactive (dead)' in '\n'.join(result.stdout):
                raise CapsuleHostError(
                    f'A core service is not running at capsule host\n{result.stdout}'
                )

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/foreman/foremanctl/test_install_foremanctl.py
Comment thread robottelo/hosts.py
Comment on lines +2006 to +2008
product_rpm_name = (
self.container_rpm_name if self.is_foremanctl_available() else self.product_rpm_name
)
Comment thread robottelo/hosts.py
… updates

Signed-off-by: Gaurav Talreja <gtalreja@redhat.com>
Signed-off-by: Gaurav Talreja <gtalreja@redhat.com>
@Gauravtalreja1
Gauravtalreja1 force-pushed the capsule-setup-with-foremanctl branch from 2bedf6d to 4475415 Compare July 28, 2026 21:27
@evgeni

evgeni commented Jul 29, 2026

Copy link
Copy Markdown
Member
trigger: test-robottelo
pytest: tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl
deployment_type: container
theforeman:
  foremanctl: 698

@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16285
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl --external-logging
Test Result : ========== 21 deselected, 12 warnings, 1 error in 4533.75s (1:15:33) ===========

@Gauravtalreja1

Copy link
Copy Markdown
Member Author
trigger: test-robottelo
pytest: tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl
deployment_type: container

@evgeni

evgeni commented Jul 30, 2026

Copy link
Copy Markdown
Member
trigger: test-robottelo
pytest: tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl
deployment_type: container

@evgeni

evgeni commented Jul 30, 2026

Copy link
Copy Markdown
Member

PRT Result

Build Number: 16285
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl --external-logging
Test Result : ========== 21 deselected, 12 warnings, 1 error in 4533.75s (1:15:33) ===========

This failed to deploy Candlepin, which is weird.

@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16297
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl --external-logging
Test Result : ========== 21 deselected, 20 warnings, 1 error in 5502.68s (1:31:42) ===========

@shubhamsg199
shubhamsg199 force-pushed the capsule-setup-with-foremanctl branch 2 times, most recently from 3fd7aae to 96e62e1 Compare July 30, 2026 11:43
Signed-off-by: Gaurav Talreja <gtalreja@redhat.com>
Signed-off-by: Shubham Ganar <shubhamsg123m@gmail.com>
@shubhamsg199
shubhamsg199 force-pushed the capsule-setup-with-foremanctl branch from 96e62e1 to 6847d6a Compare July 30, 2026 13:04
@shubhamsg199

Copy link
Copy Markdown
Contributor
trigger: test-robottelo
pytest: tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl
deployment_type: container
theforeman:
  foremanctl: 698

@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16303
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/foremanctl/test_install_foremanctl.py -k test_capsule_installation_with_foremanctl --external-logging
Test Result : ========== 21 deselected, 12 warnings, 1 error in 4558.21s (1:15:58) ===========

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

No-CherryPick PR doesnt need CherryPick to previous branches PRT-Failed Indicates that latest PRT run is failed for the PR QETestCoverage Issues and PRs relating to a Satellite bug Stream Introduced in or relating directly to Satellite Stream/Master

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants