From 12b1bbbd14d7e7e183208ae1b774902e581f270a Mon Sep 17 00:00:00 2001 From: Vincent Shen Date: Thu, 21 Nov 2024 23:52:14 -0800 Subject: [PATCH] Add bundle script for konflux --- bundle-hack/update_bundle_annotations.sh | 21 +++ bundle-hack/update_csv.py | 172 +++++++++++++++++++++++ bundle.openshfit.Dockerfile | 49 +++++++ bundle/icons/icon.png | Bin 0 -> 7772 bytes 4 files changed, 242 insertions(+) create mode 100644 bundle-hack/update_bundle_annotations.sh create mode 100644 bundle-hack/update_csv.py create mode 100644 bundle.openshfit.Dockerfile create mode 100644 bundle/icons/icon.png diff --git a/bundle-hack/update_bundle_annotations.sh b/bundle-hack/update_bundle_annotations.sh new file mode 100644 index 000000000..9235e6417 --- /dev/null +++ b/bundle-hack/update_bundle_annotations.sh @@ -0,0 +1,21 @@ +# The base version determines the index that the build will be included in, but +# not exactly. It is based on semantics of +# https://docs.engineering.redhat.com/display/CFC/Delivery +# 4.10 means that we will be included in 4.10+ indexes +BASE_OCP_VERSION="4.10" +# We use the stable channel because the Compliance Operator adheres to Tier +# 3 operator lifecycle management +# https://docs.google.com/document/d/18BPe68jhk16-4eYGT6zV-iSNLrFVsuPdVIos24kWaaE/edit +CHANNEL="stable" +ANNOTATIONS_CONTENT=$(cat << EOM +annotations: + com.redhat.openshift.versions: "v${BASE_OCP_VERSION}" + operators.operatorframework.io.bundle.channel.default.v1: '${CHANNEL}' + operators.operatorframework.io.bundle.channels.v1: '${CHANNEL}' + operators.operatorframework.io.bundle.manifests.v1: manifests/ + operators.operatorframework.io.bundle.mediatype.v1: registry+v1 + operators.operatorframework.io.bundle.metadata.v1: metadata/ + operators.operatorframework.io.bundle.package.v1: compliance-operator +EOM +) +echo "$ANNOTATIONS_CONTENT" > ./metadata/annotations.yaml \ No newline at end of file diff --git a/bundle-hack/update_csv.py b/bundle-hack/update_csv.py new file mode 100644 index 000000000..987e02ac0 --- /dev/null +++ b/bundle-hack/update_csv.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 +# The syntax str | None was added in 3.10 +# Remove when UBI9 moves to python 3.10+ +from __future__ import annotations + +import argparse +import base64 +import io +import os +import sys +import json + +from ruamel.yaml import YAML + + +yaml = YAML() +yaml.preserve_quotes = True + + +parser = argparse.ArgumentParser() +parser.add_argument("manifest_directory", type=str, + help="Path to operator manifest directory.") +parser.add_argument("version", type=str, + help="Version string of the product to update in the manifest.") +args = parser.parse_args() + + +def main() -> None: + build_manifest_file_path = get_manifest_file_path_from_directory(args.manifest_directory) + if not build_manifest_file_path: + print(f"Failed to find manifest in {args.manifest_directory}") + sys.exit(1) + + print(f"Success to find manifest in {build_manifest_file_path}") + with open(build_manifest_file_path) as f: + manifest = yaml.load(f) + print(f"Successfully loaded {build_manifest_file_path} from build.") + + add_required_annotations(manifest) + replace_version(manifest) + replace_icon(manifest) + replace_images(manifest) + remove_related_images(manifest) + write_manifest(manifest) + print(f"Successfully updated CSV manifest for {args.version}.") + + + +def get_manifest_file_path_from_directory(d: str) -> str|None: + for filename in os.listdir(d): + if filename.endswith('clusterserviceversion.yaml'): + return os.path.join(d, filename) + +def add_required_annotations(m: dict) -> None: + """ + Adds required annotations to the CSV file content represented as a dictionary. + Errors out if 'metadata' or 'annotations' does not exist. + + :param m: A dictionary representing the operator CSV manifest. + """ + required_annotations = { + "features.operators.openshift.io/disconnected": "true", + "features.operators.openshift.io/fips-compliant": "true", + "features.operators.openshift.io/proxy-aware": "true", + "features.operators.openshift.io/tls-profiles": "false", + "features.operators.openshift.io/token-auth-aws": "false", + "features.operators.openshift.io/token-auth-azure": "false", + "features.operators.openshift.io/token-auth-gcp": "false" + } + + if 'metadata' not in m: + sys.exit("Error: 'metadata' does not exist in the CSV content.") + + if 'annotations' not in m['metadata']: + sys.exit("Error: 'annotations' does not exist within 'metadata' in the CSV content.") + + for key, value in required_annotations.items(): + m['metadata']['annotations'][key] = value + + print("Successfully added required annotations.") + +def replace_version(m: dict) -> None: + manifest_version = m['spec']['version'] + print(f"Updating version references from {manifest_version}", + f"to {args.version} in manifest.") + m['metadata']['name'] = m['metadata']['name'].replace(manifest_version, args.version) + m['metadata']['annotations']['olm.skipRange'] = m['metadata']['annotations']['olm.skipRange'].replace(manifest_version, args.version) + m['spec']['replaces'] = 'compliance-operator.v' + manifest_version + m['spec']['version'] = args.version + print(f"Successfully updated the operator version references from", + f"{manifest_version} to {args.version} in manifest.") + + +def replace_icon(m: dict) -> None: + """Replace the upstream icon with a Red Hat branded image. + + Perform an in-place update of the icon data on the manifest. + + manifest(dict): A dictionary representing the operator CSV manifest + """ + icon_path = "icons/icon.png" + with io.open(icon_path, "rb") as f: + base64_encoded_icon = base64.b64encode(f.read()) + icons = [{"base64data": base64_encoded_icon.decode(), "mediatype": "image/png"}] + m['spec']['icon'] = icons + print(f"Successfully updated the operator image to use icon in {icon_path}.") + + +def replace_images(m: dict) -> None: + + CO_OPERATOR_IMAGE_PULLSPEC = "quay.io/redhat-user-workloads/ocp-isc-tenant/operator" + CO_CONTENT_IMAGE_PULLSPEC = "quay.io/redhat-user-workloads/ocp-isc-tenant/content" + CO_OPENSCAP_IMAGE_PULLSPEC = "quay.io/redhat-user-workloads/ocp-isc-tenant/scanner" + CO_MUST_GATHER_IMAGE_PULLSPEC = "quay.io/redhat-user-workloads/ocp-isc-tenant/must-gather" + + m['spec']['install']['spec']['deployments'][0]['spec']['template']['spec']['containers'][0]['image'] = CO_OPERATOR_IMAGE_PULLSPEC + + container_env = m['spec']['install']['spec']['deployments'][0]['spec']['template']['spec']['containers'][0]['env'] + for e in container_env: + if e['name'] == "RELATED_IMAGE_OPERATOR": + e['value'] = CO_OPERATOR_IMAGE_PULLSPEC + if e['name'] == "RELATED_IMAGE_PROFILE": + e['value'] = CO_CONTENT_IMAGE_PULLSPEC + if e['name'] == "RELATED_IMAGE_OPENSCAP": + e['value'] = CO_OPENSCAP_IMAGE_PULLSPEC + if e['name'] == "RELATED_IMAGE_MUST_GATHER": + e['value'] = CO_MUST_GATHER_IMAGE_PULLSPEC + + # Decode alm-examples + alm_examples_json = json.loads(m['metadata']['annotations']['alm-examples']) + + # Iterate through each item in the list and update the contentImage field + for item in alm_examples_json: + if 'spec' in item and 'contentImage' in item['spec']: + item['spec']['contentImage'] = CO_CONTENT_IMAGE_PULLSPEC + # Update nested scans if present + if 'spec' in item and 'scans' in item['spec']: + for scan in item['spec']['scans']: + if 'contentImage' in scan: + scan['contentImage'] = CO_CONTENT_IMAGE_PULLSPEC + + # Encode alm-examples back to JSON string + m['metadata']['annotations']['alm-examples'] = json.dumps(alm_examples_json, indent=2) + print("Successfully updated the operator image to use the new image.") + + +def remove_related_images(m: dict) -> None: + # Remove relatedImages entirely from the CSV. OSBS will look for container + # images in the manifest and populate them as relatedImages when the bundle + # image is built, so that we don't have to. See + # https://osbs.readthedocs.io/en/latest/users.html#pullspec-locations for + # more information on how OSBS does this. + del m['spec']['relatedImages'] + print("Removed relatedImages from operator manifest.") + + +def write_manifest(m: dict) -> None: + old_csv_filename = get_manifest_file_path_from_directory('manifests') + if not old_csv_filename: + print(f"Failed to find manifest in {args.manifest_directory}") + sys.exit(1) + + new_csv_filename = os.path.join('manifests', f"compliance-operator.v{args.version}.clusterserviceversion.yaml") + os.rename(old_csv_filename, new_csv_filename) + print(f"Successfully moved {old_csv_filename} to {new_csv_filename}.") + with open(new_csv_filename, 'w') as f: + yaml.dump(m, f) + print(f"Successfully wrote updated manifest to {new_csv_filename}.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/bundle.openshfit.Dockerfile b/bundle.openshfit.Dockerfile new file mode 100644 index 000000000..b21acf365 --- /dev/null +++ b/bundle.openshfit.Dockerfile @@ -0,0 +1,49 @@ +FROM registry.access.redhat.com/ubi9/ubi-minimal:latest as builder-runner +RUN microdnf install -y python3 python3-pip +RUN pip3 install --upgrade pip && pip3 install ruamel.yaml==0.17.9 + +# Use a new stage to enable caching of the package installations for local development +FROM builder-runner as builder + +ARG CO_VERSION="1.6.1" + +COPY ./bundle-hack . +COPY ./bundle/icons ./icons +COPY ./bundle/manifests ./manifests +COPY ./bundle/metadata ./metadata + +RUN ./update_csv.py ./manifests ${CO_VERSION} +RUN ./update_bundle_annotations.sh + +FROM scratch + +LABEL name=openshift-compliance-operator-bundle +LABEL version=${CO_VERSION} +LABEL summary='OpenShift Compliance Operator' +LABEL maintainer='Infrastructure Security and Compliance Team ' + +LABEL io.k8s.display-name='Compliance Operator' +LABEL io.k8s.description='OpenShift Compliance Operator' + +LABEL com.redhat.component=openshift-compliance-operator-bundle-container +LABEL com.redhat.delivery.appregistry=false +LABEL com.redhat.delivery.operator.bundle=true +LABEL com.redhat.openshift.versions="v4.10" + + +LABEL io.openshift.maintainer.product='OpenShift Container Platform' +LABEL io.openshift.tags=openshift,security,compliance,openscap + +LABEL operators.operatorframework.io.bundle.channel.default.v1=stable +LABEL operators.operatorframework.io.bundle.channels.v1=stable +LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ +LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 +LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ +LABEL operators.operatorframework.io.bundle.package.v1=compliance-operator +LABEL License=GPLv2+ + +# Copy files to locations specified by labels. +COPY --from=builder /manifests /manifests/ +COPY --from=builder /metadata /metadata/ +COPY bundle/tests/scorecard /tests/scorecard + diff --git a/bundle/icons/icon.png b/bundle/icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..63e98d94905d8e4090137c5a1ab5fdd57de0dbc0 GIT binary patch literal 7772 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 literal 0 HcmV?d00001