Skip to content

Commit d94eed2

Browse files
Support (automatic) operator upgrades with OLM (#67)
* wip: added spec.replaces property to the CSV * wip: add multi op catalog script * fix: the multi op catalog script * cleanup * feat(build-manifests): --quay-release allows to diverge from --release if needed * feat: automatic commons op upgrade * fix: update multi-op-catalog to include multiple ops and versions * fix: don't crash when there are no labels (some hdfs objects) * fix: small sanity check * feat(build-manifests): add --skips arg(s) * fix: more fixes for the spark operator * fix: typo * fix: typo * fix: update internal annotation and better usability. * feat: add --channel argument * Update olm/build-manifests.py Co-authored-by: Siegfried Weber <mail@siegfriedweber.net> * Update olm/multi-op-catalog.sh Co-authored-by: Siegfried Weber <mail@siegfriedweber.net> * Update olm/multi-op-catalog.sh Co-authored-by: Siegfried Weber <mail@siegfriedweber.net> * Update olm/multi-op-catalog.sh Co-authored-by: Siegfried Weber <mail@siegfriedweber.net> * Update olm/multi-op-catalog.sh Co-authored-by: Siegfried Weber <mail@siegfriedweber.net> * Update olm/multi-op-catalog.sh Co-authored-by: Siegfried Weber <mail@siegfriedweber.net> * Update olm/multi-op-catalog.sh Co-authored-by: Siegfried Weber <mail@siegfriedweber.net> * Added note on the purpose of the shell script. * fix python shebang --------- Co-authored-by: Siegfried Weber <mail@siegfriedweber.net>
1 parent cb3ac97 commit d94eed2

File tree

6 files changed

+204
-27
lines changed

6 files changed

+204
-27
lines changed

olm/build-bundles.sh

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,21 +194,22 @@ main() {
194194
exit 1
195195
fi
196196

197-
# this is the same folder that is also used by build-manifests.sh
198197
if [ "$OPERATOR" == "spark-k8s" ]; then
199-
cd "${OPENSHIFT_ROOT}/operators/stackable-spark-operator/${VERSION}"
200-
else
201-
cd "${OPENSHIFT_ROOT}/operators/stackable-${OPERATOR}-operator/${VERSION}"
198+
echo "Renaming operator from spark-k8s to spark"
199+
OPERATOR="spark"
202200
fi
203201

202+
# this is the same folder that is also used by build-manifests.sh
203+
cd "${OPENSHIFT_ROOT}/operators/stackable-${OPERATOR}-operator/${VERSION}"
204+
204205
# clean up any residual files from previous actions
205206
bundle-clean
206207
build-bundle
207208

208-
catalog-clean
209-
catalog
209+
#catalog-clean
210+
#catalog
210211

211-
deploy
212+
#deploy
212213
}
213214

214215
main "$@"

olm/build-manifests.py

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/bin/env python
1+
#!/usr/bin/env python
22
# vim: filetype=python syntax=python tabstop=4 expandtab
33

44
import argparse
@@ -55,6 +55,26 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
5555
required=True,
5656
)
5757

58+
parser.add_argument(
59+
"--quay-release",
60+
help="Use this release tag for quay images. Defaults to --release if not provided. Useful when issuing patch releases that use existing images.",
61+
type=cli_parse_release,
62+
)
63+
64+
parser.add_argument(
65+
"--replaces",
66+
help="CSV version that is replaced by this release. Example: 23.11.0",
67+
type=cli_parse_release,
68+
)
69+
70+
parser.add_argument(
71+
"--skips",
72+
nargs="*",
73+
help="CSV versions that are skipped by this release. Example: 24.3.0",
74+
default=list(),
75+
type=cli_parse_release,
76+
)
77+
5878
parser.add_argument(
5979
"-o",
6080
"--repo-operator",
@@ -92,8 +112,17 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
92112
action="store_true",
93113
)
94114

115+
parser.add_argument(
116+
"--channel",
117+
help="Channel name to use for the OLM bundle. Default: <major>.<minor> from the release number or 'alpha' for '0.0.0-dev'",
118+
)
119+
95120
args = parser.parse_args(argv)
96121

122+
# Default to the actual release if no quay release is given
123+
if not args.quay_release:
124+
args.quay_release = args.release
125+
97126
if not args.repo_certified_operators:
98127
args.repo_certified_operators = (
99128
args.repo_operator.parent / "openshift-certified-operators"
@@ -136,21 +165,31 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
136165
f"Certification repository path not found: {args.repo_certified_operators} or it's not a certified operator repository"
137166
)
138167

168+
### Set bundle channel
169+
if not args.channel:
170+
if args.release == "0.0.0-dev":
171+
args.channel = "alpha"
172+
else:
173+
args.channel = ".".join(args.release.split(".")[:2])
139174
return args
140175

141176

142177
def cli_validate_openshift_range(cli_arg: str) -> str:
143178
if not re.match(r"^v4\.\d{2}-v4\.\d{2}$", cli_arg):
144-
raise argparse.ArgumentTypeError("Invalid OpenShift version range")
179+
raise argparse.ArgumentTypeError(
180+
"Invalid OpenShift version range. Example: v4.11-v4.13"
181+
)
145182
return cli_arg
146183

147184

148185
def cli_parse_release(cli_arg: str) -> str:
149-
if re.match(r"^\d{2}\.([1-9]|1[0-2])\.\d+$", cli_arg) or re.match(
186+
if re.match(r"^\d{2}\.([1-9]|1[0-2])\.\d+(-\d*)?$", cli_arg) or re.match(
150187
r"^0\.0\.0-dev$", cli_arg
151188
):
152189
return cli_arg
153-
raise argparse.ArgumentTypeError("Invalid release")
190+
raise argparse.ArgumentTypeError(
191+
"Invalid version provided for release or replacement"
192+
)
154193

155194

156195
def cli_log_level(cli_arg: str) -> int:
@@ -189,7 +228,7 @@ def generate_csv_related_images(
189228
if c["name"] == args.op_name
190229
]
191230
else:
192-
return quay_image([(args.op_name, args.release)])
231+
return quay_image([(args.op_name, args.quay_release)])
193232

194233

195234
def generate_manifests(args: argparse.Namespace) -> list[dict]:
@@ -214,11 +253,20 @@ def generate_manifests(args: argparse.Namespace) -> list[dict]:
214253
)
215254

216255
if not args.use_helm_images:
217-
# patch the image of the operator container
218-
# with the quay.io image
256+
# patch the image of the operator container with the quay.io image
219257
for c in op_deployment["spec"]["template"]["spec"]["containers"]:
220258
if c["name"] == args.op_name:
221259
c["image"] = related_images[0]["image"]
260+
# patch the annotation image of the operator deployment with the quay.io image
261+
try:
262+
if op_deployment["spec"]["template"]["metadata"]["annotations"][
263+
"internal.stackable.tech/image"
264+
]:
265+
op_deployment["spec"]["template"]["metadata"]["annotations"][
266+
"internal.stackable.tech/image"
267+
] = related_images[0]["image"]
268+
except KeyError:
269+
pass
222270

223271
owned_crds = to_owned_crds(crds)
224272

@@ -358,10 +406,18 @@ def generate_csv(
358406

359407
result = load_resource("csv.yaml")
360408

409+
csv_name = (
410+
"spark-operator" if args.op_name == "spark-k8s-operator" else args.op_name
411+
)
412+
361413
result["spec"]["version"] = args.release
414+
result["spec"]["replaces"] = (
415+
f"{csv_name}.v{args.replaces}" if args.replaces else None
416+
)
417+
result["spec"]["skips"] = [f"{csv_name}.v{v}" for v in args.skips]
362418
result["spec"]["keywords"] = [args.product]
363419
result["spec"]["displayName"] = CSV_DISPLAY_NAME[args.product]
364-
result["metadata"]["name"] = f"{args.op_name}.v{args.release}"
420+
result["metadata"]["name"] = f"{csv_name}.v{args.release}"
365421
result["metadata"]["annotations"]["containerImage"] = related_images[0]["image"]
366422
result["metadata"]["annotations"]["description"] = CSV_DISPLAY_NAME[args.product]
367423
result["metadata"]["annotations"]["repository"] = (
@@ -437,13 +493,18 @@ def generate_helm_templates(args: argparse.Namespace) -> list[dict]:
437493
}
438494
)
439495
### Patch the version label
440-
if (
441-
crv := man["metadata"]["labels"]["app.kubernetes.io/version"]
442-
) != args.release:
443-
logging.warning(
444-
f"Version mismatch for '{man['metadata']['name']}'. Replacing '{crv}' with '{args.release}'"
445-
)
446-
man["metadata"]["labels"]["app.kubernetes.io/version"] = args.release
496+
try:
497+
if (
498+
crv := man["metadata"]["labels"]["app.kubernetes.io/version"]
499+
) != args.release:
500+
logging.warning(
501+
f"Version mismatch for '{man['metadata']['name']}'. Replacing '{crv}' with '{args.release}'"
502+
)
503+
man["metadata"]["labels"]["app.kubernetes.io/version"] = (
504+
args.release
505+
)
506+
except KeyError:
507+
pass
447508

448509
logging.debug("finish generate_helm_templates")
449510

@@ -494,7 +555,7 @@ def quay_image(images: list[tuple[str, str]]) -> list[dict[str, str]]:
494555
data = json.load(response)
495556
if not data["tags"]:
496557
raise ManifestException(
497-
f"Could not find manifest digest for request {tag_url}"
558+
f"Could not find manifest digest for release '{release}' on quay.io. Pass '--use-helm-images' to use docker.stackable.tech instead."
498559
)
499560

500561
manifest_digest = [
@@ -523,6 +584,13 @@ def write_metadata(args: argparse.Namespace) -> None:
523584
)
524585
annos["annotations"]["com.redhat.openshift.versions"] = args.openshift_versions
525586

587+
annos["annotations"][
588+
"operators.operatorframework.io.bundle.channel.default.v1"
589+
] = args.channel
590+
annos["annotations"]["operators.operatorframework.io.bundle.channels.v1"] = (
591+
args.channel
592+
)
593+
526594
anno_file = metadata_dir / "annotations.yaml"
527595
logging.info(f"Writing {anno_file}")
528596
anno_file.write_text(yaml.dump(annos))

olm/build-manifests.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#!/usr/bin/env bash
22
#
3+
# NOTE: This script is intended to be used only with the secret and listener ops.
4+
# For all other operators, use the Python equivalent script: build-manifests.py
5+
#
36
# Helper script to (re)generate OLM package manifests (skeletons).
47
#
58
# Usage:

olm/multi-op-catalog.sh

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#
2+
# Build an operator catalog.
3+
#
4+
# An experimental script that builds a custom operator catalog containing one or
5+
# more operators and one or more versions of each operator.
6+
#
7+
# The catalog is built in ./olm/catalog and doesn't actually require any other OLM manifests
8+
# because it assumes the bundle images have been built and pushed to a registry.
9+
#
10+
# It takes as argument the name of an operator to add to the catalog.
11+
#
12+
# The operator versions and the upgrade paths are hard coded in the script (for now).
13+
#
14+
# Call it multiple times with different operators to build a catalog with multiple operators.
15+
#
16+
# To start fresh, delete the olm/catalog directory.
17+
#
18+
# To start fresh with a single operator remove the olm/catalog/${OPERATOR} directory.
19+
#
20+
# This script also generates a catalog source yaml file that you can apply to your cluster.
21+
# Once the catalog is loaded by OLM, you can update it and OLM will automatically
22+
# pull in the new versions.
23+
#
24+
# To install operators from this catalog, use the OperatorHub UI and filter by "Source: Stackable Catalog".
25+
#
26+
# Assumes the bundle images are built and pushed to a registry as follows:
27+
#
28+
#./olm/build-manifests.py --openshift-versions v4.11-v4.15 --release 23.11.0 --repo-operator /home/razvan/repo/stackable/${OPERATOR}-operator --replaces nothing
29+
#./olm/build-bundles.sh -r 23.11.0 -o ${OPERATOR} -c ~/repo/stackable/openshift-certified-operators -d
30+
#
31+
# The secret and listener op bundles are special and need to be built manually.
32+
#
33+
# See: https://olm.operatorframework.io/docs/reference/catalog-templates/#example
34+
# for an example catalog with multiple bundles
35+
#
36+
export OPERATOR="$1"
37+
38+
if [ -z "$OPERATOR" ]; then
39+
echo "Usage: $0 <operator>"
40+
exit 1
41+
fi
42+
43+
rm -rf olm/catalog/${OPERATOR}
44+
rm -rf olm/catalog.Dockerfile
45+
46+
mkdir -p olm/catalog/${OPERATOR}
47+
opm generate dockerfile olm/catalog
48+
49+
opm init "stackable-${OPERATOR}-operator" \
50+
--default-channel=24.4 \
51+
--output yaml >"olm/catalog/${OPERATOR}/stackable-${OPERATOR}-operator.yaml"
52+
53+
echo "Add operator to package: ${OPERATOR}"
54+
{
55+
echo "---"
56+
echo "schema: olm.channel"
57+
echo "package: stackable-${OPERATOR}-operator"
58+
echo "name: stable"
59+
echo "entries:"
60+
echo "- name: ${OPERATOR}-operator.v23.11.0"
61+
echo "- name: ${OPERATOR}-operator.v24.3.0"
62+
echo " replaces: ${OPERATOR}-operator.v23.11.0"
63+
echo "---"
64+
echo "schema: olm.channel"
65+
echo "package: stackable-${OPERATOR}-operator"
66+
echo "name: \"24.4\""
67+
echo "entries:"
68+
echo "- name: ${OPERATOR}-operator.v24.4.0-1"
69+
echo " replaces: ${OPERATOR}-operator.v23.11.0"
70+
echo " skips:"
71+
echo " - ${OPERATOR}-operator.v24.3.0"
72+
} >>"olm/catalog/${OPERATOR}/stackable-${OPERATOR}-operator.yaml"
73+
74+
echo "Render operator: ${OPERATOR}"
75+
opm render "docker.stackable.tech/sandbox/${OPERATOR}-bundle:23.11.0" --output=yaml >>"olm/catalog/${OPERATOR}/stackable-${OPERATOR}.v23.11.0-bundle.yaml"
76+
opm render "docker.stackable.tech/sandbox/${OPERATOR}-bundle:24.3.0" --output=yaml >>"olm/catalog/${OPERATOR}/stackable-${OPERATOR}-v24.3.0-bundle.yaml"
77+
opm render "docker.stackable.tech/sandbox/${OPERATOR}-bundle:24.4.0-1" --output=yaml >>"olm/catalog/${OPERATOR}/stackable-${OPERATOR}-v24.4.0-1-bundle.yaml"
78+
79+
echo "Validating catalog..."
80+
opm validate olm/catalog
81+
82+
echo "Build catalog..."
83+
docker build olm -f olm/catalog.Dockerfile -t "docker.stackable.tech/sandbox/stackable-catalog:multi"
84+
docker push "docker.stackable.tech/sandbox/stackable-catalog:multi"
85+
86+
export VERSION="multi"
87+
88+
echo "Generating catalog source..."
89+
{
90+
echo "---"
91+
echo "apiVersion: operators.coreos.com/v1alpha1"
92+
echo "kind: CatalogSource"
93+
echo "metadata:"
94+
echo " name: stackable-catalog"
95+
echo "spec:"
96+
echo " sourceType: grpc"
97+
echo " image: docker.stackable.tech/sandbox/stackable-catalog:${VERSION}"
98+
echo " displayName: Stackable Catalog"
99+
echo " publisher: Stackable GmbH"
100+
echo " updateStrategy:"
101+
echo " registryPoll:"
102+
echo " interval: 3m"
103+
} >catalog-source.yaml

olm/resources/annotations.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
22
annotations:
3-
operators.operatorframework.io.bundle.channel.default.v1: stable
4-
operators.operatorframework.io.bundle.channels.v1: stable
3+
operators.operatorframework.io.bundle.channel.default.v1: placeholder
4+
operators.operatorframework.io.bundle.channels.v1: placeholder
55
operators.operatorframework.io.bundle.manifests.v1: manifests/
66
operators.operatorframework.io.bundle.mediatype.v1: registry+v1
77
operators.operatorframework.io.bundle.metadata.v1: metadata/
88
operators.operatorframework.io.bundle.package.v1: placeholder stackable-airflow-operator
99

10-
com.redhat.openshift.versions: v4.11-v4.15 placeholder
10+
com.redhat.openshift.versions: placeholder
1111

olm/resources/csv.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ kind: ClusterServiceVersion
44
metadata:
55
name: placeholder
66
annotations:
7+
operatorframework.io/suggested-namespace: stackable-operators
78
features.operators.openshift.io/disconnected: "false"
89
features.operators.openshift.io/fips-compliant: "false"
910
features.operators.openshift.io/proxy-aware: "true"
@@ -21,7 +22,7 @@ metadata:
2122
spec:
2223
displayName: placeholder
2324
description: |
24-
The Stackable Data Platform was designed with openness and flexibility in mind. It provides a curated selection of the best open source data apps like Apache Kafka, Apache Druid, Trino and Apache Spark. All data apps work together seamlessly, and can be added or removed in no time. Based on Kubernetes, it runs everywhere on-prem or in the cloud.
25+
The Stackable Data Platform was designed with openness and flexibility in mind. It provides a curated selection of the best open source data apps like Apache Kafka, Apache Druid, Trino and Apache Spark. All data apps work together seamlessly, and can be added or removed in no time. Based on Kubernetes, it runs everywhere - on-prem or in the cloud.
2526
2627
For more information please visit our [web page](https://stackable.tech/en/open-source-data-platform)
2728
icon:
@@ -37,6 +38,7 @@ spec:
3738
name: Stackable GmbH
3839
url: https://stackable.tech
3940
version: placeholder
41+
replaces: placeholder
4042
minKubeVersion: 1.23.0
4143

4244
installModes:

0 commit comments

Comments
 (0)