Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bundle script for konflux #621

Merged
merged 3 commits into from
Jan 8, 2025
Merged
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
21 changes: 21 additions & 0 deletions bundle-hack/update_bundle_annotations.sh
Original file line number Diff line number Diff line change
@@ -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
173 changes: 173 additions & 0 deletions bundle-hack/update_csv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#!/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/compliance-operator"
CO_CONTENT_IMAGE_PULLSPEC = "quay.io/redhat-user-workloads/ocp-isc-tenant/compliance-operator-content"
CO_OPENSCAP_IMAGE_PULLSPEC = "quay.io/redhat-user-workloads/ocp-isc-tenant/compliance-operator-openscap"
CO_MUST_GATHER_IMAGE_PULLSPEC = "quay.io/redhat-user-workloads/ocp-isc-tenant/compliance-operator-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()

49 changes: 49 additions & 0 deletions bundle.openshift.Dockerfile
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>'

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

Binary file added bundle/icons/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading