Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
16 changes: 16 additions & 0 deletions development/playbooks/deploy-dev/deploy-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
httpd_foreman_backend: "http://localhost:3000"
pulp_register_foreman_proxy: false
pre_tasks:
- name: Check cloud-connector prerequisites
ansible.builtin.include_role:
name: check_cloud_connector
when: "'cloud-connector' 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.

You can use the has_feature filter.


- name: Set development postgresql databases
ansible.builtin.set_fact:
postgresql_databases: >-
Expand All @@ -39,6 +44,14 @@
- name: Enable foreman_ansible plugin for iop
ansible.builtin.set_fact:
foreman_development_enabled_plugins: "{{ foreman_development_enabled_plugins + ['foreman_ansible'] }}"

- name: Setup cloud-connector requirements
when:
- "'cloud-connector' in enabled_features"
block:
- name: Enable foreman_rh_cloud plugin for cloud-connector
ansible.builtin.set_fact:
foreman_development_enabled_plugins: "{{ foreman_development_enabled_plugins + ['foreman_rh_cloud'] }}"
roles:
- role: pre_install
- role: certificates
Expand All @@ -58,6 +71,9 @@
vars:
iop_core_foreman_oauth_consumer_key: "{{ foreman_oauth_consumer_key }}"
iop_core_foreman_oauth_consumer_secret: "{{ foreman_oauth_consumer_secret }}"
- role: cloud_connector
when:
- "'cloud-connector' in enabled_features"
post_tasks:
- name: Stop Foreman development service
ansible.builtin.include_role:
Expand Down
3 changes: 3 additions & 0 deletions development/playbooks/deploy-dev/metadata.obsah.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ variables:
parameter: --manage-repos
help: Manage git repositories for Foreman and plugins. Set to false to skip cloning and checking out repositories.
type: Boolean
cloud_connector_http_proxy:
parameter: --cloud-connector-http-proxy
help: HTTP proxy URL for the cloud connector rhcd service.

include:
- _flavor_features
Expand Down
4 changes: 4 additions & 0 deletions src/features.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ rh-cloud:
hammer: foreman_rh_cloud
dependencies:
- katello
cloud-connector:
description: Cloud Connector for Red Hat Hybrid Cloud Console
dependencies:
- rh-cloud
iop:
description: iop services
dependencies:
Expand Down
3 changes: 3 additions & 0 deletions src/playbooks/deploy/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,7 @@
- role: hammer
when:
- "'hammer' in enabled_features"
- role: cloud_connector
when:
- "'cloud-connector' in enabled_features"
- post_install
3 changes: 3 additions & 0 deletions src/playbooks/deploy/metadata.obsah.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ variables:
- ipa_with_api
external_authentication_pam_service:
help: Name of the PAM service to use for IPA authentication
cloud_connector_http_proxy:
parameter: --cloud-connector-http-proxy
help: HTTP proxy URL for the cloud connector rhcd service.

include:
- _flavor_features
Expand Down
140 changes: 140 additions & 0 deletions src/plugins/modules/foremanctl_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
from __future__ import absolute_import, division, print_function

Check failure on line 1 in src/plugins/modules/foremanctl_api.py

View workflow job for this annotation

GitHub Actions / Python Lint

ruff (unsorted-imports)

src/plugins/modules/foremanctl_api.py:1:1: unsorted-imports: Import block is un-sorted or un-formatted help: Organize imports
__metaclass__ = type

DOCUMENTATION = '''
---
module: foremanctl_api
short_description: Make authenticated Foreman API calls using OAuth1
description:
- Make HTTP requests to the Foreman API using OAuth1 authentication.
- Useful for one-off API calls where no dedicated Foreman Ansible Module exists.
options:
server_url:
description: Foreman server URL
required: true
type: str
oauth1_consumer_key:
description: OAuth1 consumer key
required: true
type: str
oauth1_consumer_secret:
description: OAuth1 consumer secret
required: true
type: str
no_log: true
endpoint:
description: API endpoint path (e.g. /api/v2/rh_cloud/announce_to_sources)
required: true
type: str
method:
description: HTTP method
default: GET
type: str
choices: [GET, POST, PUT, DELETE, PATCH]
body:
description: Request body (sent as JSON)
type: dict
ca_path:
description: Path to CA certificate for SSL verification
type: str
status_code:
description: List of acceptable HTTP status codes
default: [200]
type: list
elements: int
'''

EXAMPLES = '''
- name: Announce to Sources
foremanctl_api:
server_url: "https://foreman.example.com"
oauth1_consumer_key: "{{ foreman_oauth_consumer_key }}"
oauth1_consumer_secret: "{{ foreman_oauth_consumer_secret }}"
endpoint: /api/v2/rh_cloud/announce_to_sources
method: POST
status_code: [200, 201]
'''

import json

from ansible.module_utils.basic import AnsibleModule

try:
import requests
from requests_oauthlib import OAuth1
HAS_DEPS = True
except ImportError:
HAS_DEPS = False


def run_module():
module = AnsibleModule(
argument_spec=dict(
server_url=dict(required=True, type='str'),
oauth1_consumer_key=dict(required=True, type='str'),
oauth1_consumer_secret=dict(required=True, type='str', no_log=True),
endpoint=dict(required=True, type='str'),
method=dict(default='GET', type='str', choices=['GET', 'POST', 'PUT', 'DELETE', 'PATCH']),
body=dict(type='dict'),
ca_path=dict(type='str'),
status_code=dict(default=[200], type='list', elements='int'),
),
supports_check_mode=True,
)

if not HAS_DEPS:
module.fail_json(msg='requests and requests-oauthlib are required for this module')

url = module.params['server_url'].rstrip('/') + module.params['endpoint']
method = module.params['method']
body = module.params['body']
ca_path = module.params['ca_path']
expected_status = module.params['status_code']

auth = OAuth1(
module.params['oauth1_consumer_key'],
client_secret=module.params['oauth1_consumer_secret'],
)

headers = {'Content-Type': 'application/json'}

try:
response = requests.request(
method=method,
url=url,
auth=auth,
headers=headers,
json=body,
verify=ca_path if ca_path else True,
)
except requests.exceptions.RequestException as e:
module.fail_json(msg=f'Request failed: {e}', url=url)

response_body = None
try:
response_body = response.json()
except (json.JSONDecodeError, ValueError):
response_body = response.text

if response.status_code not in expected_status:
module.fail_json(
msg=f'Unexpected status code {response.status_code} (expected {expected_status})',
url=url,
status_code=response.status_code,
body=response_body,
)

module.exit_json(
changed=method != 'GET',
url=url,
status_code=response.status_code,
body=response_body,
)


def main():
run_module()


if __name__ == '__main__':
main()
21 changes: 21 additions & 0 deletions src/roles/check_cloud_connector/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
- name: Verify cloud-connector is not used with iop
ansible.builtin.assert:
that:
- "'iop' not in enabled_features"
fail_msg: >-
The cloud-connector feature cannot be used together with the iop feature.
Remove one of them with --remove-feature before deploying.

- name: Check that consumer certificate exists
ansible.builtin.stat:
path: /etc/pki/consumer/cert.pem
register: check_cloud_connector_consumer_cert

- name: Verify consumer certificate exists
ansible.builtin.assert:
that:
- check_cloud_connector_consumer_cert.stat.exists
fail_msg: >-
/etc/pki/consumer/cert.pem not found.
The system must be registered with subscription-manager.
5 changes: 5 additions & 0 deletions src/roles/checks/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
ansible.builtin.include_tasks: execute_check.yml
loop: "{{ checks_to_execute }}"

- name: Run cloud connector checks
ansible.builtin.include_role:
name: check_cloud_connector
when: enabled_features | has_feature('cloud-connector')

- name: Run database index integrity checks
ansible.builtin.include_role:
name: check_database_index
Expand Down
5 changes: 5 additions & 0 deletions src/roles/cloud_connector/defaults/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
cloud_connector_url: "https://{{ ansible_facts['fqdn'] }}"
cloud_connector_service_user: cloud_connector_user
cloud_connector_service_password: changeme # noqa: no-static-secrets
cloud_connector_config_file: /etc/rhc/workers/foreman_rh_cloud.toml
12 changes: 12 additions & 0 deletions src/roles/cloud_connector/handlers/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
- name: Update system CA trust
ansible.builtin.command: update-ca-trust
changed_when: true # noqa: no-changed-when
listen: Foreman CA changed

- name: Restart rhcd
ansible.builtin.systemd_service:
name: rhcd
state: restarted
daemon_reload: true
Comment thread
jeremylenz marked this conversation as resolved.
listen: Foreman CA changed
1 change: 1 addition & 0 deletions src/roles/cloud_connector/library
17 changes: 17 additions & 0 deletions src/roles/cloud_connector/tasks/http_proxy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
- name: Create systemd drop-in directory for rhcd
ansible.builtin.file:
state: directory
path: /etc/systemd/system/rhcd.service.d
owner: root
group: root
mode: '0755'

- name: Deploy HTTP proxy systemd drop-in for rhcd
ansible.builtin.template:
src: proxy.conf.j2
dest: /etc/systemd/system/rhcd.service.d/proxy.conf
owner: root
group: root
mode: '0640'
notify: Restart rhcd
Comment thread
jeremylenz marked this conversation as resolved.
Loading
Loading