1- #!/usr/bin/env python
2- # vim: filetype=python syntax=python tabstop=4 expandtab
1+ #!/usr/bin/env -S uv run --script
2+ # /// script
3+ # requires-python = ">=3.11"
4+ # dependencies = [
5+ # "pyyaml",
6+ # ]
7+ # ///
8+ """
9+ (Re)Generate Stackable operator manifests for the Operator Lifecycle Manager (OLM).
10+
11+ The script renders the Helm chart, looks up image digests on
12+ quay.io, and writes a complete OLM bundle under deploy/olm/<version>/.
13+
14+ Usage:
15+
16+ uv run --script olm/generate-olm.py --version 26.3.0 --repo-operator ~/repo/stackable/airflow-operator --output-dir deploy/olm
17+
18+ # Or directly with python3 (PyYAML must be installed):
19+ python3 olm/generate-olm.py --version 26.3.0 --repo-operator ~/repo/stackable/airflow-operator --openshift-versions v4.18-v4.21
20+
21+ Requirements:
22+ - uv (https://docs.astral.sh/uv/) — installs PyYAML automatically
23+ - helm (https://helm.sh)
24+ """
325
426import argparse
527import json
1335import urllib .parse
1436import urllib .request
1537
16- try :
17- import yaml
18- except ModuleNotFoundError :
19- print (
20- "Module 'pyyaml' not found. Install using: pip install -r olm/requirements.txt"
21- )
22- sys .exit (1 )
38+ import yaml
2339
2440__version__ = "0.0.1"
2541
26- DESCRIPTION = """
27- (Re)Generate manifests for the Operator Lifecycle Manager (OLM).
28-
29- Example:
30- ./olm/build-manifests.py --release 24.3.0 --repo-operator ~/repo/stackable/airflow-operator
31- """
32-
33-
3442class ManifestException (Exception ):
3543 pass
3644
3745
3846def parse_args (argv : list [str ]) -> argparse .Namespace :
3947 """Parse command line args."""
4048 parser = argparse .ArgumentParser (
41- description = DESCRIPTION , formatter_class = argparse .RawDescriptionHelpFormatter
49+ description = "Generate OLM bundle manifests for Stackable operators." ,
50+ formatter_class = argparse .RawDescriptionHelpFormatter ,
51+ epilog = __doc__ ,
4252 )
4353 parser .add_argument (
4454 "--version" ,
@@ -219,8 +229,6 @@ def generate_csv_related_images(
219229
220230def generate_manifests (args : argparse .Namespace ) -> list [dict ]:
221231 logging .debug ("start generate_manifests" )
222- # Parse CRDs as generated by Rust serde.
223- crds = generate_crds (args .repo_operator )
224232
225233 # Parse Helm manifests
226234 manifests = generate_helm_templates (args )
@@ -254,19 +262,16 @@ def generate_manifests(args: argparse.Namespace) -> list[dict]:
254262 except KeyError :
255263 pass
256264
257- owned_crds = to_owned_crds (crds )
258-
259265 # Generate the CSV
260266 csv = generate_csv (
261267 args ,
262- owned_crds ,
263268 cluster_permissions ,
264269 deployments ,
265270 related_images ,
266271 )
267272
268273 logging .debug ("finish generate_manifests" )
269- return [csv , * crds , * manifests ]
274+ return [csv , * manifests ]
270275
271276
272277def filter_op_objects (args : argparse .Namespace , manifests ) -> tuple [dict , dict , dict ]:
@@ -304,7 +309,6 @@ def write_manifests(args: argparse.Namespace, manifests: list[dict]) -> None:
304309 os .makedirs (manifests_dir )
305310
306311 # The following objects are written as separate files:
307- # - crds
308312 # - cluster service version
309313 # - cluster roles
310314 # - services
@@ -349,45 +353,8 @@ def write_manifests(args: argparse.Namespace, manifests: list[dict]) -> None:
349353 except FileExistsError :
350354 raise ManifestException ("Destintation directory already exists" )
351355
352-
353- def to_owned_crds (crds : list [dict ]) -> list [dict ]:
354- logging .debug ("start to_owned_crds" )
355- owned_crd_dicts = []
356- for c in crds :
357- for v in c ["spec" ]["versions" ]:
358- ### Extract CRD description from different properties
359- description = "No description available"
360- try :
361- # we use this field instead of schema.openAPIV3Schema.description
362- # because that one is not set by the Rust->CRD serialization
363- description = v ["schema" ]["openAPIV3Schema" ]["properties" ]["spec" ][
364- "description"
365- ]
366- except KeyError :
367- pass
368- try :
369- # The OPA CRD has this field set
370- description = v ["schema" ]["openAPIV3Schema" ]["description" ]
371- except KeyError :
372- pass
373-
374- owned_crd_dicts .append (
375- {
376- "name" : c ["metadata" ]["name" ],
377- "displayName" : c ["metadata" ]["name" ],
378- "kind" : c ["spec" ]["names" ]["kind" ],
379- "version" : v ["name" ],
380- "description" : description ,
381- }
382- )
383-
384- logging .debug ("finish to_owned_crds" )
385- return owned_crd_dicts
386-
387-
388356def generate_csv (
389357 args : argparse .Namespace ,
390- owned_crds : list [dict ],
391358 cluster_permissions : list [tuple [str , dict ]],
392359 deployments : list [dict ],
393360 related_images : list [dict [str , str ]],
@@ -414,12 +381,6 @@ def generate_csv(
414381 f"https://github.com/stackabletech/{ args .op_name } "
415382 )
416383
417- # Commented out as it caused problems with the certification pipeline.
418- # result["metadata"]["annotations"]["olm.skipRange"] = f'< {args.release}'
419-
420- ### 1. Add list of owned crds
421- result ["spec" ]["customresourcedefinitions" ]["owned" ] = owned_crds
422-
423384 ### 2. Add list of related images
424385 result ["spec" ]["relatedImages" ] = related_images
425386
@@ -522,26 +483,6 @@ def generate_helm_templates(args: argparse.Namespace) -> list[dict]:
522483 )
523484
524485
525- def generate_crds (repo_operator : pathlib .Path ) -> list [dict ]:
526- logging .debug (f"start generate_crds for { repo_operator } " )
527- crd_path = (
528- repo_operator / "deploy" / "helm" / repo_operator .name / "crds" / "crds.yaml"
529- )
530-
531- logging .info (f"Reading CRDs from { crd_path } " )
532- crds = list (yaml .load_all (crd_path .read_text (), Loader = yaml .SafeLoader ))
533- for crd in crds :
534- if crd ["kind" ] == "CustomResourceDefinition" :
535- # Remove the helm.sh/resource-policy annotation
536- del crd ["metadata" ]["annotations" ]["helm.sh/resource-policy" ]
537- else :
538- raise ManifestException (
539- f'Expected "CustomResourceDefinition" but found kind "{ crd ["kind" ]} " in CRD file "{ crd_path } "'
540- )
541- logging .debug ("finish generate_crds" )
542- return crds
543-
544-
545486def quay_image (images : list [tuple [str , str ]]) -> list [dict [str , str ]]:
546487 """Get the images for the operator from quay.io. See: https://docs.quay.io/api/swagger"""
547488 logging .debug ("start op_image" )
0 commit comments