Skip to content

Add cloud-connector as a native foremanctl feature#569

Open
jeremylenz wants to merge 11 commits into
theforeman:masterfrom
jeremylenz:cloud-connector-feature
Open

Add cloud-connector as a native foremanctl feature#569
jeremylenz wants to merge 11 commits into
theforeman:masterfrom
jeremylenz:cloud-connector-feature

Conversation

@jeremylenz

@jeremylenz jeremylenz commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Re-implements the upstream satellite_operations.cloud_connector role natively in foremanctl (SAT-45966 / SAT-44641)
  • New cloud-connector feature gated behind --add-feature cloud-connector, with rh-cloud dependency
  • Installs rhc and yggdrasil-worker-forwarder, configures the worker, starts rhcd, sets rhc_instance_id via the Foreman API, and announces to Sources
  • Works with both foremanctl deploy and forge deploy-dev (dev uses separate admin credentials)
  • Early pre-checks validate package availability and mutual exclusion with the iop feature
  • Adds Foreman CA to system trust store so the worker binary can verify Foreman's certificate
  • Optional --cloud-connector-http-proxy flag for environments without direct internet access

Companion PR: theforeman/foreman_rh_cloud#1214 (adds the announce_to_sources API endpoint)

Test plan

  • foremanctl deploy --add-feature cloud-connector completes successfully
  • foremanctl features --list-enabled includes cloud-connector
  • systemctl status rhcd shows active/running
  • /etc/rhc/workers/foreman_rh_cloud.toml has correct content
  • hammer settings info --name rhc_instance_id shows the consumer cert CN
  • Second foremanctl deploy is idempotent
  • forge deploy-dev --add-feature cloud-connector completes successfully
  • Enabling both cloud-connector and iop fails early with a clear error
  • Deploying without yggdrasil-worker-forwarder repo enabled fails early with a clear error

🤖 Generated with Claude Code

jeremylenz and others added 5 commits June 10, 2026 15:32
Re-implements the upstream satellite_operations.cloud_connector role
natively in foremanctl so users can enable it via:
  foremanctl deploy --add-feature cloud-connector

The new role installs rhc and yggdrasil-worker-forwarder, templates
the worker config, starts the rhcd service, and sets rhc_instance_id
via the Foreman API. Optional HTTP proxy support is included.

Works with both foremanctl deploy and forge deploy-dev (with
appropriate credential overrides for the dev environment).

Enforces mutual exclusion with the iop feature at runtime.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move iop mutual exclusion and package availability checks into a
new check_cloud_connector role that runs in the checks phase, before
any services are deployed. This avoids a long deploy-dev run failing
late when it reaches the cloud_connector role.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use ca_path with the Foreman CA certificate instead of validate_certs,
matching the pattern used by other roles (foreman, check_foreman_api).
The self-signed CA cert is always available in the deploy context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After setting the rhc_instance_id, POST to the new
/api/v2/rh_cloud/announce_to_sources endpoint to register the
Satellite in Sources on console.redhat.com. This replaces the
Ruby-side CloudConnectorAnnounceTask that previously triggered
on REX job completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The yggdrasil-worker-forwarder binary uses the OS trust store and
doesn't accept a CA path argument. Add the Foreman CA certificate
to the system trust store so the worker can verify Foreman's
self-signed certificate when forwarding cloud requests.

Also fix Content-Type header on the announce_to_sources POST.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jeremylenz and others added 2 commits June 12, 2026 14:22
Set allow_auto_inventory_upload to true via the Foreman API,
matching the previous cloud connector setup behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verify /etc/pki/consumer/cert.pem exists early in the checks
phase, since the cloud_connector role needs it to derive the
rhc_instance_id from the certificate CN.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- name: Verify cloud-connector is not used with iop
ansible.builtin.assert:
that:
- "'iop' not in enabled_features"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should be able to enforce this at the parameter level that these two features cannot co-exist.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What exactly do you mean by parameter? I attemped to solve this with a new PR which I am now realizing I hadn't raised yet, stand by..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is #570 what you had in mind?

path: /etc/pki/consumer/cert.pem
register: __cloud_connector_consumer_cert

- name: Verify consumer certificate exists

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this sufficient? What if the system is registered to somewhere else for example? What if the system has a consumer cert but it's stale or cannot reach console.redhat.com ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

All of those things will cause it to break later, either during deploy or after. But this is the same thing the original role does. (I actually added this check so that it will fail earlier than it would otherwise.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would a subscription-manager status tell us more?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

subscription-manager status gives us this:

# subscription-manager status
+-------------------------------------------+
   System Status Details
+-------------------------------------------+
Overall Status: Registered
Content Access Mode is set to Simple Content Access. This host has access to content, regardless of subscription status.

subscription-manager identity gives us the actual consumer uuid:

# subscription-manager identity
system identity: cf46f013-6455-4ceb-913e-b1af651f098b
name: ip-10-0-198-13.rhos-01.prod.psi.rdu2.redhat.com
org name: 11949999
org ID: 11949999

But that command does make an API call.

Comment thread src/roles/check_cloud_connector/tasks/main.yaml Outdated
Comment thread src/roles/cloud_connector/defaults/main.yaml Outdated
Comment thread src/roles/cloud_connector/handlers/main.yaml
- name: Install rhc and yggdrasil-worker-forwarder
ansible.builtin.package:
name:
- rhc

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this available in CentOS stream 9 now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am not sure. My dev box is RHEL.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Welp, this has to work in both places or use constraints to limit where it can be enabled.

@jeremylenz jeremylenz Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Seems rhc is available in Centos Stream 9 - AppStream:

$ vagrant ssh quadlet
Last login: Mon Jun 15 16:57:33 2026 from 192.168.122.1
[vagrant@quadlet ~]$ dnf info rhc
CentOS Stream 9 - BaseOS                                                                                                                                     742 kB/s | 8.9 MB     00:12    
CentOS Stream 9 - AppStream                                                                                                                                  3.5 MB/s |  28 MB     00:07    
CentOS Stream 9 - Extras packages                                                                                                                            133 kB/s |  21 kB     00:00    
Foreman nightly                                                                                                                                               13 MB/s | 2.0 MB     00:00    
Foreman plugins nightly                                                                                                                                       15 MB/s | 1.9 MB     00:00    
Katello Nightly                                                                                                                                              318 kB/s | 118 kB     00:00    
Candlepin: an open source entitlement management system.                                                                                                     375 kB/s |  32 kB     00:00    
pulpcore: Fetch, Upload, Organize, and Distribute Software Packages.                                                                                         780 kB/s | 306 kB     00:00    
Puppet 8 Repository el 9 - x86_64                                                                                                                            7.7 MB/s | 3.7 MB     00:00    
Available Packages
Name         : rhc
Epoch        : 1
Version      : 0.2.7
Release      : 1.el9
Architecture : x86_64
Size         : 11 M
Source       : rhc-0.2.7-1.el9.src.rpm
Repository   : appstream

Comment thread src/roles/cloud_connector/tasks/main.yaml Outdated
Comment thread src/roles/cloud_connector/tasks/main.yaml Outdated
group: root
mode: '0755'

- name: Add Foreman CA to system trust store

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this how we do it today? This is an anti-pattern we have been trying to avoid.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

foreman-installer does the same thing via puppet-certs:

We need it here because yggdrasil-worker-forwarder is a Go binary that uses the OS trust store — no way to pass it a CA path. That said, we could move this into the certificates role so it's done once globally instead of per-feature. Would that be cleaner?

@jeremylenz jeremylenz Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I was apprehensive about this too. I figured we can change it later..
Claude reply above

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These are both 404s because they were moved quite a bit ago. I think we need to consider udpating yggdrasil-worker-forwarder vs. starting this trend of relying on the system store. Let's phone a friend for another opinion. @evgeni ☎️

@jeremylenz jeremylenz Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We need to update it anyway to use DBUS (so it can run on RHEL 10), so we can probably just tack that change on there.

Comment thread src/roles/cloud_connector/tasks/main.yaml
Comment thread src/roles/cloud_connector/tasks/main.yaml Outdated
protocol = "grpc"
env = [
"FORWARDER_USER={{ cloud_connector_user }}",
"FORWARDER_PASSWORD={{ cloud_connector_password }}",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is the part we still need to work out how to properly handle, that is, the authentication.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As I see it our options are
1 - Keep things as they are here, feeding admin/changeme to rhc - Not acceptable because the admin password ends up in the worker config.toml.
2 - Create a service user with limited permissions - Doable in the existing PRs here and in foreman_rh_cloud.
3 - Create a service user + personal access token and use that, same as the previous architecture - FAM doesn't have a module for personal access tokens, so this would be a fair bit of work

I think Option 2 seems promising and am pushing that update now.

- Remove cross-role variable references from defaults (use standalone
  fallback values; base.yaml provides the real overrides)
- Rename task "Configure rhc-cloud-connector-worker" for consistency
- Rename "Announce Satellite to Sources" to "Announce to Sources"
- Fix var-naming lint: use role-prefixed variable names instead of
  double-underscore prefix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use ansible.builtin.systemd_service instead of service for handler
- Remove redundant workers directory task (rhc package creates it)
- Use theforeman.foreman.setting module instead of raw uri for settings
- Add noqa for static secret in role defaults

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jeremylenz

Copy link
Copy Markdown
Contributor Author

Pushed updates addressing review feedback (1bfe640):

  • Handler: switched from ansible.builtin.service to ansible.builtin.systemd_service since we use daemon_reload
  • Workers directory: removed the task — confirmed rhc package on EL9 creates /etc/rhc/workers/
  • Settings API: replaced raw uri calls with theforeman.foreman.setting module for rhc_instance_id and allow_auto_inventory_upload
  • Lint fix: added noqa: no-static-secrets on the defaults password (same pattern as foreman_development)

Instead of storing admin credentials in the worker config, create a
dedicated cloud_connector_user with a limited role that only grants
dispatch_cloud_requests permission. The service user password is
generated and persisted like other foremanctl secrets.

Admin credentials are still used for the FAM calls to create the
user/role and manage settings, but they no longer end up on disk
in the worker config file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jeremylenz jeremylenz force-pushed the cloud-connector-feature branch from 908e8b1 to 95a9cc9 Compare June 15, 2026 18:13
Clarifies that these are the admin credentials used for Foreman API
calls during setup, distinct from cloud_connector_service_user/password
which are the limited-permission credentials baked into the worker config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants