Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
23 changes: 14 additions & 9 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,20 @@ and configured to be able to access the Foreman server.
4. The rhcd instance needs to be registered in the
`Sources` cloud registry as a valid listener.

This configuration process is initiated by the "Configure
cloud connector" button in the UI, and is implemented as a
REX job and a subscriber task. The REX job is initiated
with a specific feature, and executes a playbook from
`foreman-operations` collection. One of the steps in the
task is a request to Foreman server to persist the rhcd id.
The subscriber task waits for this task to finish and then
using the rhcd id from the REX job, it calls the Sources
API to properly subscribe the new rhcd instance.
This configuration process is handled by foremanctl
(`foremanctl deploy --add-feature cloud-connector`), which
installs the packages, templates the worker config, starts
the rhcd service, and sets the `rhc_instance_id` setting
via the Foreman API. After setup, foremanctl calls
`POST /api/v2/rh_cloud/announce_to_sources` to register
the rhcd instance in the Sources cloud registry.

A daily recurring task (`CloudConnectorAnnounceTask`) also
checks Sources registration for self-healing — if the
registration is ever lost, it re-registers automatically.
The task skips organizations that have had recent
cloud-initiated remediation jobs (proving connectivity)
or that lack a valid manifest.

Once the configuration phase is finished the remediation
request will be able to flow from the cloud, through the
Expand Down
8 changes: 6 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ Each namespace follows Rails directory structure (`app/{models,controllers,views
- `InventorySync::Async::InventoryScheduledSync` - Daily inventory sync
- `InsightsCloud::Async::InsightsScheduledSync` - Daily recommendations sync
- `InsightsCloud::Async::InsightsClientStatusAging` - Daily status cleanup
- `InsightsCloud::Async::CloudConnectorAnnounceTask` - Daily Sources registration check (self-healing)

**Key models:**
- `InsightsRule` - Problem definitions from Red Hat Insights
Expand Down Expand Up @@ -250,10 +251,13 @@ return (

### Cloud Connector (Cloud-Initiated Remediations)

Requires setup on Foreman server:
Setup via foremanctl (`foremanctl deploy --add-feature cloud-connector`):
- `rhcd` service (listener)
- `yggdrasil-worker-forwarder` (worker)
- Registration in Red Hat Sources registry
- `rhc_instance_id` setting set via Foreman API
- Registration in Red Hat Sources registry via `POST /api/v2/rh_cloud/announce_to_sources`

Sources registration is also checked daily by `CloudConnectorAnnounceTask` for self-healing.

Flow: Cloud UI → rhcd → worker → Foreman API → REX job → playbook execution

Expand Down
13 changes: 8 additions & 5 deletions app/controllers/api/v2/rh_cloud/inventory_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class InventoryController < ::Api::V2::BaseController
include InventoryUpload::TaskActions
include ForemanRhCloud::IopSmartProxyAccess

before_action :require_non_iop_smart_proxy, only: [:enable_cloud_connector]
before_action :require_non_iop_smart_proxy, only: [:announce_to_sources]

api :GET, "/organizations/:organization_id/rh_cloud/report", N_("Download latest report")
param :organization_id, Integer, required: true, desc: N_("Set the current organization context for the request")
Expand Down Expand Up @@ -70,10 +70,13 @@ def sync_inventory_status
render json: { message: error.message }, status: :bad_request
end

api :POST, "/rh_cloud/enable_connector", N_("Enable cloud connector")
def enable_cloud_connector
cloud_connector = ForemanRhCloud::CloudConnector.new
render json: cloud_connector.install.to_json
api :POST, "/rh_cloud/announce_to_sources", N_("Register in Red Hat Sources for cloud connector")
def announce_to_sources
task = ForemanTasks.async_task(InsightsCloud::Async::CloudConnectorAnnounceTask, true)

render json: {
task: task,
}, status: :ok
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ def index

render json: {
accounts: accounts,
CloudConnectorStatus: ForemanInventoryUpload::UploadsSettingsController.cloud_connector_status,
}, status: :ok
end

Expand Down
11 changes: 0 additions & 11 deletions app/controllers/foreman_inventory_upload/uploads_controller.rb
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
module ForemanInventoryUpload
class UploadsController < ::ApplicationController
include InventoryUpload::ReportActions
include ForemanRhCloud::IopSmartProxyAccess

before_action :require_non_iop_smart_proxy, only: [:enable_cloud_connector]

def download_file
filename, file = report_file(params[:organization_id])

send_file file, disposition: 'attachment', filename: filename
end

def enable_cloud_connector
# Set the autoupload to true, since it's required by the feature.
Setting[:allow_auto_inventory_upload] = true

cloud_connector = ForemanRhCloud::CloudConnector.new
render json: cloud_connector.install.to_json
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ def index
ipsObfuscationEnabled: Setting[:obfuscate_inventory_ips],
excludePackagesEnabled: Setting[:exclude_installed_packages],
allowAutoInsightsMismatchDelete: Setting[:allow_auto_insights_mismatch_delete],
CloudConnectorStatus: ForemanInventoryUpload::UploadsSettingsController.cloud_connector_status,
lastSyncTask: last_successful_inventory_sync_task,
}, status: :ok
end
Expand All @@ -19,13 +18,6 @@ def set_advanced_setting
index
end

def self.cloud_connector_status
cloud_connector = ForemanRhCloud::CloudConnector.new
job = cloud_connector&.latest_job
return nil unless job
{ id: job.id, task: ForemanTasks::Task.where(:id => job.task_id).first }
end

def last_successful_inventory_sync_task
task = ForemanTasks::Task.where(label: 'InventorySync::Async::InventoryFullSync', result: 'success')
.reorder('ended_at desc').first
Expand Down
74 changes: 0 additions & 74 deletions app/services/foreman_rh_cloud/cloud_connector.rb

This file was deleted.

23 changes: 23 additions & 0 deletions app/services/foreman_rh_cloud/cloud_presence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ def create_satellite_instance_source

def register_rhc_instance
raise Foreman::Exception.new('rhc_instance_id is empty, cannot register RHC to the cloud') if Setting[:rhc_instance_id].empty?

return :already_registered if rhc_connection_exists?

source_id = satellite_instance_source || create_satellite_instance_source

create_response = JSON.parse(
Expand All @@ -95,6 +98,22 @@ def register_rhc_instance
@satellite_instance_source = create_response['id']
end

def rhc_connection_exists?
response = JSON.parse(
execute_cloud_request(
method: :get,
url: rhc_connection_url,
headers: {
content_type: :json,
},
ssl_client_cert: OpenSSL::X509::Certificate.new(certs[:cert]),
ssl_client_key: OpenSSL::PKey.read(certs[:key])
)
)

response['data']&.any?
end

private

def sources_url(path)
Expand All @@ -113,6 +132,10 @@ def create_satellite_instance_source_url
sources_url('/sources')
end

def rhc_connection_url
sources_url("/rhc_connections?filter[rhc_id]=#{Setting[:rhc_instance_id]}")
end

def create_rhc_connections_url
sources_url('/rhc_connections')
end
Expand Down
4 changes: 1 addition & 3 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
get 'settings', to: 'uploads_settings#index'
post 'setting', to: 'uploads_settings#set_advanced_setting'

post 'cloud_connector', to: 'uploads#enable_cloud_connector'

resources :tasks, only: [:create, :show]

get 'status', to: 'cloud_status#index'
Expand Down Expand Up @@ -71,7 +69,7 @@
end

namespace 'rh_cloud' do
post 'enable_connector', to: 'inventory#enable_cloud_connector'
post 'announce_to_sources', to: 'inventory#announce_to_sources'
post 'cloud_request', to: 'cloud_request#update'
get 'advisor_engine_config', to: 'advisor_engine_config#show'

Expand Down
7 changes: 1 addition & 6 deletions lib/foreman_rh_cloud/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def self.register_scheduled_task(task_class, cronline)
ForemanRhCloud::Engine.register_scheduled_task(InventorySync::Async::InventoryScheduledSync, '0 0 * * *')
ForemanRhCloud::Engine.register_scheduled_task(InsightsCloud::Async::InsightsScheduledSync, '0 0 * * *')
ForemanRhCloud::Engine.register_scheduled_task(InsightsCloud::Async::InsightsClientStatusAging, '0 0 * * *')
ForemanRhCloud::Engine.register_scheduled_task(InsightsCloud::Async::CloudConnectorAnnounceTask, '0 0 * * *')
end
end
rescue ActiveRecord::NoDatabaseError
Expand All @@ -80,12 +81,6 @@ def self.register_scheduled_task(task_class, cronline)
host_action_button: false,
provided_inputs: ['playbook_url', 'report_url', 'correlation_id', 'report_interval']
)
RemoteExecutionFeature.register(
:ansible_configure_cloud_connector,
N_('Configure Cloud Connector on given hosts'),
:description => N_('Configure Cloud Connector on given hosts'),
:proxy_selector_override => ::RemoteExecutionProxySelector::INTERNAL_PROXY
)
end

# Ideally this code belongs to an initializer. The problem is that Katello controllers are not initialized completely until after the end of the to_prepare blocks
Expand Down
3 changes: 1 addition & 2 deletions lib/foreman_rh_cloud/plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ def self.register
:generate_foreman_rh_cloud,
'foreman_inventory_upload/reports': [:generate],
'foreman_inventory_upload/tasks': [:create],
'api/v2/rh_cloud/inventory': [:get_hosts, :remove_hosts, :sync_inventory_status, :download_file, :generate_report, :enable_cloud_connector],
'foreman_inventory_upload/uploads': [:enable_cloud_connector],
'api/v2/rh_cloud/inventory': [:get_hosts, :remove_hosts, :sync_inventory_status, :download_file, :generate_report, :announce_to_sources],
'foreman_inventory_upload/uploads_settings': [:set_advanced_setting],
'foreman_inventory_upload/missing_hosts': [:remove_hosts],
'insights_cloud/settings': [:update],
Expand Down
Loading
Loading