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
6 changes: 6 additions & 0 deletions .yamllint
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ yaml-files:
- "*.fmf"
- "*.profile"

# The Ansible role metadata template is filled by string substitution of
# @PLACEHOLDER@ tokens (including structural ones like @GALAXY_TAGS@), so it is
# not valid standalone YAML and cannot be linted.
ignore: |
utils/ansible_galaxy_meta_template.yml

# https://yamllint.readthedocs.io/en/stable/rules.html
rules:
truthy: disable # do not check for strict true / false boolean values
Expand Down
25 changes: 21 additions & 4 deletions docs/release_process.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,10 @@ the collection is published as a tarball uploaded directly to Ansible Galaxy.
--roles-dir $OUTPUT_DIR/$version/ansible_roles \
--output-dir $OUTPUT_DIR/$version \

# 3. Verify no unrewritten FQCNs remain in the bundled roles
grep -r "community\.c\.\|ansible\.posix\." \
# 3. Verify no unrewritten FQCNs remain in the bundled roles.
# Only the vendored collection (community.general) must be rewritten;
# ansible.posix is a declared dependency and is intentionally kept.
grep -r "community\.general\." \
$OUTPUT_DIR/$version/ansible_collections/redhatofficial/rhel_hardening_roles/roles/ \
&& echo FAIL || echo OK
done
Expand All @@ -450,9 +452,24 @@ the collection is published as a tarball uploaded directly to Ansible Galaxy.
```

This will:
1. Download and vendor modules from `community.general` and `ansible.posix`.
1. Download and vendor modules from `community.general`; declare `ansible.posix` as a collection dependency.
2. Bundle all roles for the allowed products into the `redhatofficial.rhel_hardening_roles` collection.
3. Build the collection tarball in the `./ansible-collections-tarball` folder.
3. Generate the collection README, changelog, license, and Galaxy metadata.
4. Build the collection tarball in the `./ansible-collections-tarball` folder.

The default metadata and installation instructions target public Ansible Galaxy. To generate an
Automation Hub collection, select the `hub` target. This supplies the Red Hat namespace,
documentation, issue tracker, author, and installation server automatically:

```bash
python3 utils/ansible_roles_to_collection.py \

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.

Does --galaxy-server need to be included in this command here or not?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In this case we are only building the collection to manually upload it later. At this moment we are not automatically pushing any collections to galaxy hub. We do it manually by uploading the final tar.gz file generated by this command.

--roles-dir $OUTPUT_DIR/rhel8/ansible_roles \
--roles-dir $OUTPUT_DIR/rhel9/ansible_roles \
--roles-dir $OUTPUT_DIR/rhel10/ansible_roles \
--output-dir $OUTPUT_DIR/ansible-collections-tarball \
--target hub \
--build
```

> **_NOTE:_** The collection version is read automatically from `CMakeLists.txt` at the
> checked-out tag, so it will match the release version without needing to be specified manually.
Expand Down
4 changes: 2 additions & 2 deletions products/almalinux9/profiles/hipaa.profile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ reference: https://www.hhs.gov/hipaa/for-professionals/index.html
title: 'Health Insurance Portability and Accountability Act (HIPAA)'

description: |-
The HIPAA Security Rule establishes U.S. national standards to protect individuals’
The HIPAA Security Rule establishes U.S. national standards to protect individuals'
electronic personal health information that is created, received, used, or
maintained by a covered entity. The Security Rule requires appropriate
administrative, physical and technical safeguards to ensure the
Expand All @@ -18,7 +18,7 @@ description: |-

This profile configures AlmaLinux OS 9 to the HIPAA Security
Rule identified for securing of electronic protected health information.
Use of this profile in no way guarantees or makes claims against legal compliance against the HIPAA Security Rule(s).
Use of this profile in no way guarantees or makes claims against legal compliance against the HIPAA Security Rule(s).

selections:
- grub2_password
Expand Down
2 changes: 1 addition & 1 deletion products/ol7/profiles/hipaa.profile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ reference: https://www.hhs.gov/hipaa/for-professionals/index.html
title: 'Health Insurance Portability and Accountability Act (HIPAA)'

description: |-
The HIPAA Security Rule establishes U.S. national standards to protect individuals’
The HIPAA Security Rule establishes U.S. national standards to protect individuals'
electronic personal health information that is created, received, used, or
maintained by a covered entity. The Security Rule requires appropriate
administrative, physical and technical safeguards to ensure the
Expand Down
2 changes: 1 addition & 1 deletion products/ol8/profiles/hipaa.profile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ reference: https://www.hhs.gov/hipaa/for-professionals/index.html
title: 'Health Insurance Portability and Accountability Act (HIPAA)'

description: |-
The HIPAA Security Rule establishes U.S. national standards to protect individuals’
The HIPAA Security Rule establishes U.S. national standards to protect individuals'
electronic personal health information that is created, received, used, or
maintained by a covered entity. The Security Rule requires appropriate
administrative, physical and technical safeguards to ensure the
Expand Down
2 changes: 1 addition & 1 deletion products/ol9/profiles/hipaa.profile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ reference: https://www.hhs.gov/hipaa/for-professionals/index.html
title: 'Health Insurance Portability and Accountability Act (HIPAA)'

description: |-
The HIPAA Security Rule establishes U.S. national standards to protect individuals’
The HIPAA Security Rule establishes U.S. national standards to protect individuals'
electronic personal health information that is created, received, used, or
maintained by a covered entity. The Security Rule requires appropriate
administrative, physical and technical safeguards to ensure the
Expand Down
2 changes: 1 addition & 1 deletion products/rhel8/profiles/hipaa.profile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ reference: https://www.hhs.gov/hipaa/for-professionals/index.html
title: 'Health Insurance Portability and Accountability Act (HIPAA)'

description: |-
The HIPAA Security Rule establishes U.S. national standards to protect individuals’
The HIPAA Security Rule establishes U.S. national standards to protect individuals'
electronic personal health information that is created, received, used, or
maintained by a covered entity. The Security Rule requires appropriate
administrative, physical and technical safeguards to ensure the
Expand Down
2 changes: 1 addition & 1 deletion products/rhel9/profiles/hipaa.profile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ reference: https://www.hhs.gov/hipaa/for-professionals/index.html
title: 'Health Insurance Portability and Accountability Act (HIPAA)'

description: |-
The HIPAA Security Rule establishes U.S. national standards to protect individuals’
The HIPAA Security Rule establishes U.S. national standards to protect individuals'
electronic personal health information that is created, received, used, or
maintained by a covered entity. The Security Rule requires appropriate
administrative, physical and technical safeguards to ensure the
Expand Down
2 changes: 1 addition & 1 deletion products/sle15/profiles/hipaa.profile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ reference: https://www.hhs.gov/hipaa/for-professionals/index.html
title: 'Health Insurance Portability and Accountability Act (HIPAA)'

description: |-
The HIPAA Security Rule establishes U.S. national standards to protect individuals’
The HIPAA Security Rule establishes U.S. national standards to protect individuals'
electronic personal health information that is created, received, used, or
maintained by a covered entity. The Security Rule requires appropriate
administrative, physical and technical safeguards to ensure the
Expand Down
2 changes: 1 addition & 1 deletion ssg/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
OSCAP_PROFILE_ALL_ID = "(all)"
XCCDF11_NS = "http://checklists.nist.gov/xccdf/1.1"
XCCDF12_NS = "http://checklists.nist.gov/xccdf/1.2"
min_ansible_version = "2.9"
min_ansible_version = "2.16"
ansible_version_requirement_pre_task_name = \
"Verify Ansible meets SCAP-Security-Guide version requirements."
standard_profiles = ['standard', 'pci-dss', 'desktop', 'server']
Expand Down
5 changes: 5 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ endmacro()

mypy_test("utils/import_srg_spreadsheet.py" "skip")
mypy_test("utils/check_eof.py" "normal")
mypy_test("utils/check_smart_quotes.py" "normal")
mypy_test("utils/import_disa_stig.py" "skip")
mypy_test("tests/cces-removed.py" "normal")
mypy_test("utils/build_control_from_reference.py" "skip")
Expand Down Expand Up @@ -286,6 +287,10 @@ if(Python_VERSION_MAJOR GREATER 2)
NAME "test-check-eof"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/utils/check_eof.py" "${CMAKE_SOURCE_DIR}/ssg" "${CMAKE_SOURCE_DIR}/linux_os" "${CMAKE_SOURCE_DIR}/utils" "${CMAKE_SOURCE_DIR}/tests" "${CMAKE_SOURCE_DIR}/products" "${CMAKE_SOURCE_DIR}/shared" "${CMAKE_SOURCE_DIR}/docs" "${CMAKE_SOURCE_DIR}/applications" "${CMAKE_SOURCE_DIR}/build-scripts" "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_SOURCE_DIR}/Dockerfiles" "${CMAKE_SOURCE_DIR}/tests" "${CMAKE_SOURCE_DIR}/controls" "${CMAKE_SOURCE_DIR}/.github"
)
add_test(
NAME "test-check-smart-quotes"
COMMAND env "PYTHONPATH=$ENV{PYTHONPATH}" "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/utils/check_smart_quotes.py" "--extensions=profile" "${CMAKE_SOURCE_DIR}/products"
)
endif()

if(PY_CMAKELINT)
Expand Down
193 changes: 193 additions & 0 deletions tests/unit/utils/test_ansible_roles_to_collection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import sys
from pathlib import Path

import yaml

from utils.ansible_roles_to_collection import (
COLLECTION_DESCRIPTION,
COLLECTION_DEPENDENCIES,
COLLECTIONS_TO_VENDOR,
COLLECTION_ISSUES,
COLLECTION_INSTALL_SERVER,
COLLECTION_MIN_PYTHON_VERSION,
COLLECTION_TARGETS,
_make_vendored_module_self_contained,
generate_collection_docs,
generate_galaxy_yml,
generate_runtime_yml,
detect_modules_to_bundle,
parse_args,
)
from ssg.constants import min_ansible_version


REPO_ROOT = Path(__file__).resolve().parents[3]


def test_generate_collection_docs(tmp_path):
issues = "https://example.test/issues"
installation_server = "https://cloud.example.test/api/automation-hub/"

generate_collection_docs(
tmp_path,
"redhat",
"rhel_hardening_roles",
"0.1.82",
["rhel10_stig", "rhel9_cis"],
issues,
installation_server,
"Red Hat",
"Custom-License",
)

readme = (tmp_path / "README.md").read_text()
assert "redhat.rhel_hardening_roles.rhel10_stig" in readme
assert f"--server {installation_server}" in readme
assert "redhat-rhel_hardening_roles-0.1.82.tar.gz" in readme
assert f"ansible-core >= {min_ansible_version}" in readme
assert f"Python >= {COLLECTION_MIN_PYTHON_VERSION}" in readme
assert "ansible.posix >= 2.2.0" in readme
assert "https://github.com/ComplianceAsCode/content/releases" in readme
assert "Create issue button" in readme
assert "top right corner of Automation Hub" in readme
assert f"[collection issue tracker]({issues})" in readme
assert "Custom-License" in readme
assert "Red Hat" in readme
assert (tmp_path / "CHANGELOG.md").read_text().startswith("# Changelog\n")
assert (tmp_path / "changelogs" / "README.md").is_file()
assert (tmp_path / "LICENSE").read_text() == (REPO_ROOT / "LICENSE").read_text()


def test_generate_galaxy_yml_uses_generic_defaults(tmp_path):
generate_galaxy_yml(tmp_path, "redhatofficial", "rhel_hardening_roles", "0.1.82")

metadata = yaml.safe_load((tmp_path / "galaxy.yml").read_text())
assert metadata["description"] == COLLECTION_DESCRIPTION
assert metadata["issues"] == COLLECTION_ISSUES
assert "documentation" not in metadata


def test_generate_galaxy_yml_accepts_automation_hub_metadata(tmp_path):
documentation = "https://docs.example.test/rhel10"
issues = "https://redhat.example.test/issues"

generate_galaxy_yml(
tmp_path,
"redhat",
"rhel_hardening_roles",
"0.1.82",
description="RHEL hardening roles",
documentation=documentation,
issues=issues,
authors=["Red Hat"],
licenses=["BSD-3-Clause"],
)

metadata = yaml.safe_load((tmp_path / "galaxy.yml").read_text())
assert metadata["description"] == "RHEL hardening roles"
assert metadata["documentation"] == documentation
assert metadata["issues"] == issues
assert metadata["authors"] == ["Red Hat"]
assert metadata["license"] == ["BSD-3-Clause"]
assert metadata["dependencies"] == COLLECTION_DEPENDENCIES


def test_generate_runtime_yml_uses_collection_minimum(tmp_path):
(tmp_path / "meta").mkdir()
generate_runtime_yml(tmp_path)

runtime = yaml.safe_load((tmp_path / "meta" / "runtime.yml").read_text())
# Certification tooling expects a full X.Y.Z lower bound (e.g. >=2.16.0), so the
# X.Y min_ansible_version is padded with a patch level in the collection runtime.
padded_version = min_ansible_version + ".0" * (2 - min_ansible_version.count("."))
assert runtime["requires_ansible"] == f">={padded_version}"


def test_ansible_posix_is_not_vendored(tmp_path):
tasks = tmp_path / "tasks"
tasks.mkdir()
(tasks / "main.yml").write_text(
"- name: Use external collections\n"
" ansible.posix.sysctl:\n"
" name: kernel.pid_max\n"
" value: 4194304\n"
"- name: Use vendored collection\n"
" community.general.ini_file:\n"
" path: /etc/example\n"
)

detected = detect_modules_to_bundle([tmp_path], COLLECTIONS_TO_VENDOR)

assert detected == {"community.general": ["ini_file"]}


def test_vendored_module_inlines_external_documentation_fragment(tmp_path):
module_path = tmp_path / "ini_file.py"
module_path.write_text(
"DOCUMENTATION = r'''\n"
"attributes:\n"
" - files\n"
" - community.general.attributes\n"
" check_mode:\n"
" support: full\n"
" diff_mode:\n"
" support: full\n"
"'''\n"
)

_make_vendored_module_self_contained("ini_file", module_path)

content = module_path.read_text()
assert "community.general.attributes" not in content
assert "Can run in C(check_mode)" in content
assert "Returns details on what has changed" in content


def test_generic_installation_server_is_stable():
assert COLLECTION_INSTALL_SERVER == "https://galaxy.ansible.com/"


def test_hub_installation_server_uses_console_redhat_com():
# cloud.redhat.com is deprecated; Automation Hub is served from console.redhat.com.
assert (
COLLECTION_TARGETS["hub"]["installation_server"]
== "https://console.redhat.com/api/automation-hub/"
)


def test_parse_args_uses_galaxy_target_by_default(monkeypatch):
monkeypatch.setattr(
sys,
"argv",
["ansible_roles_to_collection.py", "--roles-dir", "roles", "--output-dir", "output"],
)

args = parse_args()

assert args.target == "galaxy"
assert args.namespace == COLLECTION_TARGETS["galaxy"]["namespace"]
assert args.installation_server == COLLECTION_TARGETS["galaxy"]["installation_server"]


def test_parse_args_uses_hub_target_metadata(monkeypatch):
monkeypatch.setattr(
sys,
"argv",
[
"ansible_roles_to_collection.py",
"--roles-dir",
"roles",
"--output-dir",
"output",
"--target",
"hub",
],
)

args = parse_args()

assert args.namespace == "redhat"
assert args.author == "Red Hat"
assert args.documentation == COLLECTION_TARGETS["hub"]["documentation"]
assert args.issues == COLLECTION_TARGETS["hub"]["issues"]
assert args.installation_server == COLLECTION_TARGETS["hub"]["installation_server"]
4 changes: 2 additions & 2 deletions utils/ansible_galaxy_meta_template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ galaxy_info:

license: BSD-3-Clause

min_ansible_version: @MIN_ANSIBLE_VERSION@
min_ansible_version: '@MIN_ANSIBLE_VERSION@'

platforms:
- name: EL
versions:
- @PLATFORM_VERSION@
- '@PLATFORM_VERSION@'

@GALAXY_TAGS@

Expand Down
15 changes: 10 additions & 5 deletions utils/ansible_playbook_to_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,17 @@ def _raw_playbook(self):
@memoize
def platform_version(self):
platform = self.product
# Check to see if this is RHEL product
# Report the specific EL major version the role targets, derived from
# the trailing digits of the product id (rhel10 -> "10", rhel8 -> "8",
# and likewise for other EL-family products such as ol9 -> "9").
# NOTE: ansible-lint's schema[meta] rejects EL versions newer than the
# enum bundled in the installed linter (24.12.2 caps at "9"), so RHEL 10
# roles fail that check until the linter schema catches up. Accurate
# platform metadata is intentionally preferred over passing that check.
if platform in PRODUCT_ALLOWLIST:
# For RHEL, we can get what version
if 'rhel' in platform:
return platform[-1]
return "7\n - 8"
match = re.search(r"\d+$", platform)
if match:
return match.group()
return "TBD"

@property
Expand Down
Loading
Loading