diff --git a/.gitignore b/.gitignore index 5872f3607..a1396ff10 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,8 @@ kind-logs-*/ # Other gke_gcloud_auth_plugin_cache + +projects/ +installers/olm/operator_*.yaml +installers/olm/bundles +installers/olm/bundles/tools diff --git a/installers/olm/Makefile b/installers/olm/Makefile new file mode 100644 index 000000000..90cea459e --- /dev/null +++ b/installers/olm/Makefile @@ -0,0 +1,177 @@ +# ============================================================================== +# Percona Server for MySQL Operator - OLM Bundle Generation +# ============================================================================== + +# Default target +.DEFAULT_GOAL := help +.SUFFIXES: +SHELL := /bin/bash + +# ============================================================================== +# Configuration Variables +# ============================================================================== + +# Project configuration +NAME ?= percona-server-mysql-operator +IMAGE_TAG_OWNER ?= perconalab +IMAGE_TAG_BASE ?= $(IMAGE_TAG_OWNER)/$(NAME) +MODE ?= cluster + +# Version detection +SED := $(shell which gsed || which sed) +VERSION ?= $(shell git rev-parse --abbrev-ref HEAD | $(SED) -e 's^/^-^g; s^[.]^-^g;' | tr '[:upper:]' '[:lower:]') +IMAGE := $(IMAGE_TAG_BASE):$(VERSION) + +# Bundle configuration +OPENSHIFT_VERSIONS ?= v4.16-v4.19 +PACKAGE_CHANNEL ?= stable +MIN_KUBE_VERSION ?= "" +DOCKER_DEFAULT_PLATFORM ?= linux/amd64 + +# Paths +REPO_ROOT := $(shell git rev-parse --show-toplevel) +KUSTOMIZE := $(REPO_ROOT)/bin/kustomize + +# Tool versions +OPERATOR_SDK_VERSION := v1.41.1 + +# Bundle image configuration +BUNDLE_IMG ?= $(IMAGE_TAG_BASE):community-bundle-$(VERSION) + +# System detection for tool downloads +UNAME_S := $(shell uname -s) +UNAME_M := $(shell uname -m) +OS_KERNEL := $(shell echo "$(UNAME_S)" | tr '[:upper:]' '[:lower:]') +OS_MACHINE := $(UNAME_M) + +# Display colors +GREEN := $(shell tput setaf 2) +RESET := $(shell tput sgr0) + +# Export variables for generate.sh +export VERSION OPENSHIFT_VERSIONS PACKAGE_CHANNEL MIN_KUBE_VERSION DOCKER_DEFAULT_PLATFORM MODE + +# ============================================================================== +# Bundle Targets +# ============================================================================== + +DISTROS := community redhat marketplace + +.PHONY: bundles +bundles: ## Build all OLM bundles (community, redhat, marketplace) +bundles: check-prereqs $(DISTROS:%=bundles/%) + +.PHONY: $(DISTROS:%=bundles/%) +$(DISTROS:%=bundles/%): bundles/%: tools/operator-sdk + @echo "$(GREEN)Building $* bundle...$(RESET)" + ./generate.sh $* + ./tools/operator-sdk bundle validate $@ --select-optional='suite=operatorframework' + $(if $(filter community,$*),./tools/operator-sdk bundle validate $@ --select-optional='name=community' --optional-values='index-path=$@/Dockerfile') + @echo "$(GREEN)✓ Bundle stored in installers/olm/bundles/$*$(RESET)" + +# ============================================================================== +# Docker Build & Push Targets +# ============================================================================== + +.PHONY: build +build: ## Build community bundle Docker image +build: + @echo "$(GREEN)Building bundle Docker image...$(RESET)" + docker build -f bundles/community/Dockerfile -t $(BUNDLE_IMG) --platform=linux/amd64 bundles/community + @echo "$(GREEN)✓ Bundle image built: $(BUNDLE_IMG)$(RESET)" + +.PHONY: push +push: ## Push bundle Docker image to registry + @echo "$(GREEN)Pushing bundle image to registry...$(RESET)" + docker push $(BUNDLE_IMG) + @echo "$(GREEN)✓ Bundle image pushed: $(BUNDLE_IMG)$(RESET)" + +# ============================================================================== +# Utility Targets +# ============================================================================== + +.PHONY: check-prereqs +check-prereqs: check-version check-git check-tools + +.PHONY: check-version +check-version: +ifndef VERSION + $(error VERSION is not set) +endif + +.PHONY: check-git +check-git: + @if ! git rev-parse --git-dir > /dev/null 2>&1; then \ + echo "Error: Not in a git repository"; \ + exit 1; \ + fi + +.PHONY: check-tools +check-tools: + @for cmd in gawk gcsplit yq; do \ + if ! command -v $$cmd >/dev/null 2>&1; then \ + echo "Error: $$cmd is required but not installed"; \ + exit 1; \ + fi; \ + done + +.PHONY: install-olm +install-olm: ## Install OLM in Kubernetes cluster +install-olm: tools/operator-sdk + ./tools/operator-sdk olm install + +.PHONY: clean +clean: ## Remove generated files and downloaded tools + rm -rf ./bundles ./projects ./tools ./package + +.PHONY: help +help: ## Show this help message + @awk 'BEGIN {FS = ": ## "; printf "\n$(GREEN)Usage:$(RESET)\n make [target]\n\n$(GREEN)Targets:$(RESET)\n"} /^[a-zA-Z_-]+: ## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +# ============================================================================== +# Tool Management +# ============================================================================== + +.PHONY: tools +tools: ## Download required tools +tools: tools/operator-sdk + +# Download operator-sdk +tools/operator-sdk: + @echo "Downloading operator-sdk $(OPERATOR_SDK_VERSION)..." + @install -d tools + @curl -fSL --fail -o '$@' \ + 'https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$(OS_KERNEL)_$(OS_MACHINE)' \ + || { rm -f '$@'; echo "Failed to download operator-sdk"; exit 1; } + @chmod +x '$@' + @echo "✓ operator-sdk installed" + +# ============================================================================== +# Development Targets +# ============================================================================== + +.PHONY: validate +validate: ## Validate existing bundles without rebuilding + @for distro in $(DISTROS); do \ + if [ -d "bundles/$$distro" ]; then \ + echo "Validating $$distro bundle..."; \ + ./tools/operator-sdk bundle validate "bundles/$$distro" --select-optional='suite=operatorframework' || exit 1; \ + fi; \ + done + @echo "$(GREEN)✓ All bundles validated$(RESET)" + +.PHONY: list-versions +list-versions: ## Show current version information + @echo "Current configuration:" + @echo " VERSION: $(VERSION)" + @echo " IMAGE: $(IMAGE)" + @echo " MODE: $(MODE)" + @echo " OPENSHIFT_VERSIONS: $(OPENSHIFT_VERSIONS)" + @echo " MIN_KUBE_VERSION: $(MIN_KUBE_VERSION)" + +# ============================================================================== +# Kustomize +# ============================================================================== + +$(KUSTOMIZE): + $(MAKE) -C $(REPO_ROOT) kustomize diff --git a/installers/olm/README.md b/installers/olm/README.md new file mode 100644 index 000000000..2ac0004d2 --- /dev/null +++ b/installers/olm/README.md @@ -0,0 +1,22 @@ +1. To generate bundle correctly please set env variables (default values for these variables you can check in makefile): +```bash +# operator version +export VERSION=1.0.0 +# By default we use perconalab for tag owner. Please update this variable to use another repo +export IMAGE_TAG_OWNER=percona +# Min k8s version +export MIN_KUBE_VERSION=1.27.0 +# Openshift versions: +export OPENSHIFT_VERSIONS="v4.16-v4.20" +# Set namespace or cluster (to generate bundles for cluster-wide) +export MODE=namespace +``` +2. Also it could be useful to check variable in makefile and update if you need something extra. For the most cases to update these variables is enough +3. Update spec.description in bundle.csv.yaml with features added in this release. +4. Run bundle generation: +```bash +# Generate all bundles community redhat and marketplace: +make bundles +# Generate only specific bundle: +make bundles/community +``` diff --git a/installers/olm/bundle.Dockerfile b/installers/olm/bundle.Dockerfile new file mode 100644 index 000000000..1f6c0c95d --- /dev/null +++ b/installers/olm/bundle.Dockerfile @@ -0,0 +1,15 @@ +# Used to build the bundle image. This file is ignored by the community operator +# registries which work with bundle directories instead. +# https://operator-framework.github.io/community-operators/packaging-operator/ + +FROM scratch AS builder + +COPY manifests/ /build/manifests/ +COPY metadata/ /build/metadata/ + +FROM scratch + +# LABELS is replaced with bundle.annotations.yaml +${LABELS} + +COPY --from=builder /build/ / diff --git a/installers/olm/bundle.annotations.yaml b/installers/olm/bundle.annotations.yaml new file mode 100644 index 000000000..f11ea9034 --- /dev/null +++ b/installers/olm/bundle.annotations.yaml @@ -0,0 +1,12 @@ +--- +annotations: + operators.operatorframework.io.bundle.mediatype.v1: registry+v1 + operators.operatorframework.io.bundle.manifests.v1: manifests/ + operators.operatorframework.io.bundle.metadata.v1: metadata/ + operators.operatorframework.io.bundle.package.v1: percona-server-mysql-operator + operators.operatorframework.io.bundle.channels.v1: stable + operators.operatorframework.io.bundle.channel.default.v1: stable + com.redhat.openshift.versions: 'v4.13' + org.opencontainers.image.authors: info@percona.com + org.opencontainers.image.url: https://percona.com + org.opencontainers.image.vendor: Percona diff --git a/installers/olm/bundle.csv.yaml b/installers/olm/bundle.csv.yaml new file mode 100644 index 000000000..8117e1efb --- /dev/null +++ b/installers/olm/bundle.csv.yaml @@ -0,0 +1,275 @@ +# https://olm.operatorframework.io/docs/concepts/crds/clusterserviceversion/ +# https://docs.openshift.com/container-platform/4.7/operators/operator_sdk/osdk-generating-csvs.html +# https://redhat-connect.gitbook.io/certified-operator-guide/ocp-deployment/operator-metadata/creating-the-csv +# https://pkg.go.dev/github.com/operator-framework/api@v0.10.1/pkg/operators/v1alpha1#ClusterServiceVersion +--- +apiVersion: operators.coreos.com/v1 +kind: ClusterServiceVersion +metadata: + name: '' + namespace: default + annotations: + support: Percona + # The following affect how the package is indexed at OperatorHub.io: + # https://operatorhub.io/?category=Database + # https://sdk.operatorframework.io/docs/advanced-topics/operator-capabilities/operator-capabilities/ + categories: Database + capabilities: Full Lifecycle + certified: 'true' + description: >- + Percona Operator for MySQL based on Percona Server for MySQL automates the creation and management + of highly available, enterprise-ready MySQL database clusters on Kubernetes. + + # The following appear on the details page at OperatorHub.io: + createdAt: "%Y-%m-%dT%H:%M:%S.%3Z" + repository: 'https://github.com/percona/percona-server-mysql-operator' + alm-examples: >- + +spec: + # The following affect how the package is indexed at OperatorHub.io: + # https://operatorhub.io/ + displayName: Percona Operator for MySQL based on Percona Server for MySQL + provider: + # These values become labels on the PackageManifest. + name: Percona + url: https://www.percona.com/ + labels: {} + keywords: + - mysql + - percona + - database + - sql + - operator + - group-replication + - asynchronous-replication + + # The following appear on the details page at OperatorHub.io: + # https://operatorhub.io/operator/percona-server-mysql-operator + description: |- + + ## Percona is Cloud Native + + Percona Operator for MySQL based on Percona Server for MySQL follows our best + practices for deployment and configuration of highly-available, fault-tolerant + MySQL instances in a Kubernetes-based environment on-premises or in the cloud. + It provides the following capabilities: + + + * Deploy group replication MySQL clusters with HAProxy or MySQL Router + + * Deploy asynchronous replication MySQL clusters with Orchestrator and HAProxy + + * Expose clusters with regular Kubernetes Services + + * Monitor the cluster with Percona Monitoring and Management + + * Customize MySQL configuration + + * Manage system user passwords + + + Consult the + [documentation](https://docs.percona.com/percona-operator-for-mysql/ps/) + on the Percona Operator for MySQL based on Percona Server for MySQL for complete + details on capabilities and options. + + + ### Supported Features + + + * **Scale Your Cluster** change the `size` parameter to add or remove + members of the cluster. Three is the minimum recommended size for a + functioning cluster. + + + * **Manage Your Users** add, remove, or + change the privileges of database users + + + * **Automate Your Backups** configure cluster backups to run on a + scheduled basis. Backups can be stored on a persistent volume or + S3-compatible storage. + + + * **Proxy integration** choose HAProxy or MySQL Router as a proxy in front of + the MySQL cluster. Proxies are deployed and configured automatically + with the Operator. + + + ### Common Configurations + + + * **Set Resource Limits** - set limitation on requests to CPU and memory + resources. + + + * **Customize Storage** - set the desired Storage Class and Access Mode for + your database cluster data. + + + * **Control Scheduling** - define how your MySQL Pods are scheduled onto the + K8S cluster with tolerations, pod disruption budgets, node selector and + affinity settings. + + * Group Replication and Asynchronous Replication support + + * HAProxy and MySQL Router support + + * MySQL Orchestrator for asynchronous replication topology management + + * Fully automated minor version updates (Smart Update) + + * Support MySQL versions 8.0 and 8.4 + + ### Before You Start + + + Add the MySQL user `Secret` to Kubernetes. User information must be placed in + the data section of the `secrets.yaml` + + file with Base64-encoded logins and passwords for the user accounts. + + + Below is a sample `secrets.yaml` file for the correct formatting. + + + ``` + apiVersion: v1 + kind: Secret + metadata: + name: ps-cluster1-secrets + type: Opaque + data: + root: cm9vdF9wYXNzd29yZA== + xtrabackup: YmFja3VwX3Bhc3N3b3Jk + monitor: bW9uaXRvcg== + clustercheck: Y2x1c3RlcmNoZWNrcGFzc3dvcmQ= + proxyadmin: YWRtaW5fcGFzc3dvcmQ= + pmmserver: c3VwYXxefHBheno= + operator: b3BlcmF0b3JhZG1pbg== + orchestrator: b3JjaGVzdHJhdG9yX3Bhc3N3b3Jk + replication: cmVwbF9wYXNzd29yZAo= + ``` + + ### Release Highlights + + * Initial release of Percona Operator for MySQL based on Percona Server for MySQL + version: + links: + - name: Percona + url: 'https://www.percona.com/' + - name: Percona Kubernetes Operators Landing Page + url: 'https://www.percona.com/software/percona-kubernetes-operators' + - name: Documentation + url: 'https://docs.percona.com/percona-operator-for-mysql/ps/' + - name: Github + url: 'https://github.com/percona/percona-server-mysql-operator' + maintainers: + - name: Percona + email: info@percona.com + icon: + - base64data: >- + PHN2ZyB3aWR0aD0iMjI3IiBoZWlnaHQ9IjE5NCIgdmlld0JveD0iMCAwIDIyNyAxOTQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik01OS4yODk5IDE5My40M0w0My41NDk5IDE2NS4xMkw1Ny42NDk5IDEzOS43MUw0NS4wNjk5IDExNi45NUgxNi4zMjk5TDAuNjI5ODgzIDg5LjAxTDE2LjA1OTkgNjAuNzlINDkuMDA5OUw2Mi44Mjk5IDg1LjgzSDg4LjAxOTlMMTAyLjQxIDYwLjU2SDEzNC45NkwxNTAuMzUgODguNTJMMTM2LjI3IDExNC4wM0wxNDkuMjEgMTM2Ljc2TDE3OC4yMiAxMzYuODRMMTkzLjYzIDE2NC44OEwxNzguMSAxOTMuMDhMMTQ1Ljg4IDE5My4wNEwxMzEuMTQgMTY3LjQ4SDEwNi4wOUMxMDYuMDkgMTY3LjQ4IDkzLjAwOTkgMTkwLjQ4IDkxLjMxOTkgMTkzLjQzSDU5LjI2OTlINTkuMjg5OVpNNTEuNTA5OSAxNjguMTJMNjIuNTY5OSAxODcuNTdIODQuNjg5OUw3My41Nzk5IDE2OC4wNEw1MS41MTk5IDE2OC4xMkg1MS41MDk5Wk0xODMuODYgMTY3Ljk1QzE4Mi4xOCAxNjcuOTcgMTgwLjU3IDE2Ny45OSAxNzguOTYgMTY4TDE3My4wNSAxNjguMDVDMTY5Ljk5IDE2OC4wOCAxNjYuOTIgMTY4LjEgMTYzLjg2IDE2OC4xMkgxNjMuMzlMMTYzLjE2IDE2OC41M0MxNjAuNDEgMTczLjM5IDE1Ny42NSAxNzguMjUgMTU0Ljg4IDE4My4xMUwxNTIuNDggMTg3LjMzSDE3NC42TDE3NC44MyAxODYuOTJDMTc3LjAxIDE4My4wNiAxNzkuMjIgMTc5LjE5IDE4MS40MiAxNzUuMzFMMTgxLjM0IDE3NS4zNUMxODEuNTggMTc1LjAyIDE4MS45NCAxNzQuMzggMTgyLjYzIDE3My4xOEwxODUuNjIgMTY3LjkzTDE4My44NyAxNjcuOTVIMTgzLjg2Wk03OC40OTk5IDE2NC45Nkw4OS42OTk5IDE4NC41OUwxMDAuOTcgMTY0LjgzTDg5LjkyOTkgMTQ1LjQ4TDc4LjQ5OTkgMTY0Ljk2Wk0xMzYuMzggMTY0LjkzTDE0Ny40NSAxODQuMzRMMTU4LjM3IDE2NS4xOUwxNDcuMyAxNDUuNzdMMTM2LjM4IDE2NC45M1pNNTEuNDk5OSAxNjIuMjZMNzAuMTg5OSAxNjIuMTdMNjkuNDk5OSAxNjAuOTdDNjkuNDk5OSAxNjAuOTcgNjQuMzQ5OSAxNTEuOTMgNjEuNzQ5OSAxNDcuMzdMNjAuODY5OSAxNDUuODJMNTEuNDk5OSAxNjIuMjdWMTYyLjI2Wk0xNjYuNzggMTYyLjIyTDE4NS42NCAxNjIuMDZMMTg0Ljk3IDE2MC44N0MxODIuOCAxNTYuOTkgMTc5LjIgMTUwLjY5IDE3Ni45NSAxNDYuODFMMTc2LjI0IDE0NS41OUwxNjYuNzcgMTYyLjIySDE2Ni43OFpNMTA5LjI5IDE2MS42NkgxMzEuMjdMMTQyLjMzIDE0Mi4yMUgxMjAuOThMMTA5LjMgMTYxLjY2SDEwOS4yOVpNMTYxLjcyIDE1OS4zTDE3MS4yMiAxNDIuNkgxNTIuMkwxNjEuNzEgMTU5LjNIMTYxLjcyWk02Ni4zNTk5IDE0My40MUM2Ny4wNjk5IDE0NC42NyA2Ny45Mzk5IDE0Ni4yNSA2OC44Nzk5IDE0Ny45NEM3MC43ODk5IDE1MS4zOCA3Mi45NDk5IDE1NS4yOCA3NC40Njk5IDE1Ny44OUw3NS4xNjk5IDE1OS4wOUw3NS44Njk5IDE1Ny45Qzc3LjQxOTkgMTU1LjI1IDg0LjI3OTkgMTQzLjQzIDg0LjI3OTkgMTQzLjQzTDg0Ljk4OTkgMTQyLjIxSDY1LjY3OTlMNjYuMzU5OSAxNDMuNDJWMTQzLjQxWk0xMDQuMjEgMTU4LjhMMTE0LjE1IDE0Mi4yQzExNC4xNSAxNDIuMiAxMDkuMDQgMTQyLjIgMTA3LjIxIDE0Mi4ySDk0Ljc5OTlMMTA0LjIyIDE1OC43OUwxMDQuMjEgMTU4LjhaTTY2LjY0OTkgMTM0Ljc5TDY1LjcwOTkgMTM2LjY4TDg4LjAyOTkgMTM2LjRMOTkuMTE5OSAxMTYuOTFINzUuNDc5OUM3NS40Nzk5IDExNi45MSA2OS41Mjk5IDEyOC45NyA2Ni42NDk5IDEzNC43OVpNMTIwLjk3IDEzNi4zNkgxNDIuMzJMMTQxLjYzIDEzNS4xNUMxMzguMjUgMTI5LjIxIDEzNC44NyAxMjMuMjggMTMxLjUgMTE3LjM0TDEzMS4yNyAxMTYuOTNIMTA5LjMxTDEyMC45NyAxMzYuMzZaTTEwMy41IDEyMC45NUMxMDEuMjQgMTI0Ljg2IDk3LjYyOTkgMTMxLjIzIDk1LjQ1OTkgMTM1LjE1TDk0Ljc4OTkgMTM2LjM2SDk2LjE2OTlDMTAxLjcgMTM2LjM1IDEwNy4yMiAxMzYuMzUgMTEyLjczIDEzNi4zNUgxMTQuMTZMMTA0LjE5IDExOS43N0wxMDMuNSAxMjAuOTZWMTIwLjk1Wk02MC44Mjk5IDEzMy40Mkw2OC45Njk5IDExNi45OEg1MS43NTk5TDYwLjgyOTkgMTMzLjQyWk0yMi45OTk5IDExMS4wOUw0MS43MDk5IDExMS4wNEwzMi4zNDk5IDk0LjYyTDIyLjk4OTkgMTExLjFMMjIuOTk5OSAxMTEuMDlaTTEwOS45MiAxMTEuMDVIMTMxLjI4TDE0Mi4xNCA5MS42SDEyMS4wMkwxMDkuOTIgMTExLjA1Wk03NS40NTk5IDExMS4wNUg5OS4xMjk5TDg4LjA2OTkgOTEuNkg2NS44NTk5TDc1LjQ1OTkgMTExLjA1Wk01MS44NTk5IDExMS4wMkg2OC45Mzk5TDYxLjAxOTkgOTQuOTFMNTEuODU5OSAxMTEuMDJaTTk0Ljc3OTkgOTEuNjhMMTA0LjUgMTA4Ljc3TDExNC4zIDkxLjZMOTQuNzc5OSA5MS42OFpNNi45Mzk4OCA0OC43MkwxNy45ODk5IDEwOC4xTDI5LjA1OTkgODguNjhMMTcuOTg5OSA2OS4yOEw2LjkzOTg0IDg0LjcyWk00Ni43ODk5IDEwOC4wOUw1Ni4xNDk5IDkxLjY1SDM3LjQxOTlMNDYuNzc5OSAxMDguMDlINDYuNzg5OVpNMzQuMDg5OSA4NS43OUg1Ni4xNzk5TDQ1LjExOTkgNjYuMzRIMjMuMDM5OUwzNC4wODk5IDg1Ljc5Wk0xMjAuOTkgODUuNzZIMTQyLjM0TDEzMS4yOSA2Ni4zM0gxMDkuMzRMMTIwLjk5IDg1Ljc2Wk05NC44MDk5IDg1LjczSDExNC4xOEwxMDQuMjQgNjkuMTdMOTQuODA5OSA4NS43M1oiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8xMjAyXzExNDU3KSIvPgo8cGF0aCBvcGFjaXR5PSIwLjUiIGQ9Ik0xMDkuNzcgMTc1LjA5SDEyNy41OEwxMzMuNDMgMTg1LjIySDEwNC4wNEwxMDkuNzcgMTc1LjA5WiIgZmlsbD0iIzYyQUVGRiIvPgo8cGF0aCBvcGFjaXR5PSIwLjUiIGQ9Ik00OS45NDk5IDE0MC4wNkwyOS43Mjk5IDE3NS4wOUg0MS4wNjk5TDQ2Ljc1OTkgMTg1LjIySDEyLjE1OTlMNDQuMjA5OSAxMjkuNzNMNDkuOTQ5OSAxNDAuMDZaIiBmaWxsPSIjNjJBRUZGIi8+CjxwYXRoIG9wYWNpdHk9IjAuNSIgZD0iTTE5NS45NiAxNzUuMDlIMjA4LjVMMTE5LjEyIDIwLjI3TDg1LjI5OTggNzguODRMNzMuNTk5OSA3OC44M0wxMTkuMTIgMEwyMjYuMDYgMTg1LjIySDE5MC4zNEwxOTUuOTYgMTc1LjA5WiIgZmlsbD0iIzYyQUVGRiIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzEyMDJfMTE0NTciIHgxPSIxNjMuNjIiIHkxPSI2MC40IiB4Mj0iMjAuOTE5OSIgeTI9IjIwMy4xMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBvZmZzZXQ9IjAuMDMiIHN0b3AtY29sb3I9IiM0MzlFRkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjE2IiBzdG9wLWNvbG9yPSIjM0I5NEY0Ii8+CjxzdG9wIG9mZnNldD0iMC4zOSIgc3RvcC1jb2xvcj0iIzI2N0NENyIvPgo8c3RvcCBvZmZzZXQ9IjAuNjIiIHN0b3AtY29sb3I9IiMwRTVGQjUiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4= + mediatype: image/svg+xml + + customresourcedefinitions: + owned: + - description: Instance of a Percona Server for MySQL + displayName: PerconaServerMySQL + kind: PerconaServerMySQL + name: perconaservermysqls.ps.percona.com + version: v1 + specDescriptors: [ ] + statusDescriptors: [ ] + resources: + - version: v1 + kind: Deployment + name: '' + - version: v1 + kind: Service + name: '' + - version: v1 + kind: ReplicaSet + name: '' + - version: v1 + kind: Pod + name: '' + - version: v1 + kind: Secret + name: '' + - version: v1 + kind: ConfigMap + name: '' + - description: Instance of a Percona Server for MySQL Backup + displayName: PerconaServerMySQLBackup + kind: PerconaServerMySQLBackup + name: perconaservermysqlbackups.ps.percona.com + version: v1 + specDescriptors: [ ] + statusDescriptors: [ ] + resources: + - version: v1 + kind: Deployment + name: '' + - version: v1 + kind: Service + name: '' + - version: v1 + kind: ReplicaSet + name: '' + - version: v1 + kind: Pod + name: '' + - version: v1 + kind: Secret + name: '' + - version: v1 + kind: ConfigMap + name: '' + - description: Instance of a Percona Server for MySQL Restore + displayName: PerconaServerMySQLRestore + kind: PerconaServerMySQLRestore + name: perconaservermysqlrestores.ps.percona.com + version: v1 + specDescriptors: [ ] + statusDescriptors: [ ] + resources: + - version: v1 + kind: Deployment + name: '' + - version: v1 + kind: Service + name: '' + - version: v1 + kind: ReplicaSet + name: '' + - version: v1 + kind: Pod + name: '' + - version: v1 + kind: Secret + name: '' + - version: v1 + kind: ConfigMap + name: '' + required: [ ] + # https://olm.operatorframework.io/docs/best-practices/common/ + # Note: The minKubeVersion must correspond to the lowest supported OCP version + maturity: stable + # https://olm.operatorframework.io/docs/advanced-tasks/operator-scoping-with-operatorgroups/ + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: true + type: MultiNamespace + - supported: true + type: AllNamespaces + + install: + strategy: deployment + spec: + clusterPermissions: + permissions: + deployments: diff --git a/installers/olm/bundle.relatedImages.yaml b/installers/olm/bundle.relatedImages.yaml new file mode 100644 index 000000000..501873304 --- /dev/null +++ b/installers/olm/bundle.relatedImages.yaml @@ -0,0 +1,22 @@ +- name: mysql8.4 + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: mysql8.0 + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: router8.4 + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: router8.0 + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: backup8.4 + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: backup8.0 + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: toolkit + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: pmm3 + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: haproxy + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: orchestrator + image: registry.connect.redhat.com/percona/percona-server-mysql-operator-containers@sha256: +- name: operator + image: registry.connect.redhat.com/percona/percona-server-mysql-operator@sha256: diff --git a/installers/olm/generate.sh b/installers/olm/generate.sh new file mode 100755 index 000000000..0ff2c4009 --- /dev/null +++ b/installers/olm/generate.sh @@ -0,0 +1,266 @@ +#!/usr/bin/env bash + +# Install +# brew install gawk coreutils +for command in gawk gcsplit; do + if ! command -v $command &>/dev/null; then + echo "Error: $command is not installed. Please install it: brew install $command" >&2 + exit 1 + fi +done + +set -eu + +DISTRIBUTION="$1" + +cd "${BASH_SOURCE[0]%/*}" + +bundle_directory="bundles/${DISTRIBUTION}" +project_directory="projects/${DISTRIBUTION}" + +# The 'operators.operatorframework.io.bundle.package.v1' package name for each +# bundle (updated for the 'certified' and 'marketplace' bundles). +package_name='percona-server-mysql-operator' + +# The project name used by operator-sdk for initial bundle generation. +project_name='percona-server-mysql-operator' + +# The prefix for the 'clusterserviceversion.yaml' file. +# Per OLM guidance, the filename for the clusterserviceversion.yaml must be prefixed +# with the Operator's package name for the 'redhat' and 'marketplace' bundles. +# https://github.com/redhat-openshift-ecosystem/certification-releases/blob/main/4.9/ga/troubleshooting.md#get-supported-versions +file_name='percona-server-mysql-operator' + +if [ "${MODE}" == "cluster" ]; then + suffix="-cw" + rbac_file="../../deploy/cw-rbac.yaml" + operator_file="../../deploy/cw-operator.yaml" +elif [ "${MODE}" == "namespace" ]; then + suffix="" + rbac_file="../../deploy/rbac.yaml" + operator_file="../../deploy/operator.yaml" +else + echo "Please add MODE variable. It could be either namespace or cluster" + exit 1 +fi + +# Parse deploy files directly into temporary files (don't modify config/ originals) +yq eval '. | select(.kind == "Deployment")' "$operator_file" >operator_deployments.yaml +yq eval '. | select(.kind == "ServiceAccount")' "$rbac_file" >operator_accounts.yaml +yq eval '. | select(.kind == "ClusterRole")' "$rbac_file" >operator_cluster_roles.yaml +yq eval '. | select(.kind == "Role")' "$rbac_file" >operator_ns_roles.yaml + +update_yaml_images() { + local yaml_file="$1" + + if [ ! -f "$yaml_file" ]; then + echo "Error: File '$yaml_file' does not exist." + return 1 + fi + + local temp_file + temp_file=$(mktemp) + + sed -E 's/(("image":|containerImage:|image:)[ ]*"?)([^"]+)("?)/\1docker.io\/\3\4/g' "$yaml_file" >"$temp_file" + mv "$temp_file" "$yaml_file" + + echo "File '$yaml_file' updated successfully." +} + +## Create the Operator SDK project (for bundle metadata only). + +[ ! -d "${project_directory}" ] || rm -r "${project_directory}" +install -d "${project_directory}" +( + cd "${project_directory}" + operator-sdk init --fetch-deps='false' --project-name=${project_name} +) + +# Recreate the OLM bundle. +[ ! -d "${bundle_directory}" ] || rm -r "${bundle_directory}" +install -d \ + "${bundle_directory}/manifests" \ + "${bundle_directory}/metadata" + +# Render bundle annotations and strip comments. +# Per Red Hat we should not include the org.opencontainers annotations in the +# 'redhat' & 'marketplace' annotations.yaml file, so only add them for 'community'. +# - https://coreos.slack.com/team/UP1LZCC1Y + +export package="${package_name}" +export package_channel="${PACKAGE_CHANNEL}${suffix}" +export openshift_supported_versions="${OPENSHIFT_VERSIONS}" + +yq eval '.annotations["operators.operatorframework.io.bundle.channels.v1"] = env(package_channel) | + .annotations["operators.operatorframework.io.bundle.channel.default.v1"] = env(package_channel) | + .annotations["com.redhat.openshift.versions"] = env(openshift_supported_versions)' \ + bundle.annotations.yaml >"${bundle_directory}/metadata/annotations.yaml" + +if [ "${DISTRIBUTION}" == 'community' ]; then + # community-operators + yq eval --inplace ' + .annotations["operators.operatorframework.io.bundle.package.v1"] = "percona-server-mysql-operator" | + .annotations["org.opencontainers.image.authors"] = "info@percona.com" | + .annotations["org.opencontainers.image.url"] = "https://percona.com" | + .annotations["org.opencontainers.image.vendor"] = "Percona"' \ + "${bundle_directory}/metadata/annotations.yaml" + +# certified-operators +elif [ "${DISTRIBUTION}" == 'redhat' ]; then + yq eval --inplace ' + .annotations["operators.operatorframework.io.bundle.package.v1"] = "percona-server-mysql-operator-certified" ' \ + "${bundle_directory}/metadata/annotations.yaml" + +# redhat-marketplace +elif [ "${DISTRIBUTION}" == 'marketplace' ]; then + yq eval --inplace ' + .annotations["operators.operatorframework.io.bundle.package.v1"] = "percona-server-mysql-operator-certified-rhmp" ' \ + "${bundle_directory}/metadata/annotations.yaml" +fi + +# Copy annotations into Dockerfile LABELs. +# TODO fix tab for labels. + +labels=$(yq eval -r '.annotations | to_entries | map("LABEL " + .key + "=" + (.value | tojson)) | join("\n")' \ + "${bundle_directory}/metadata/annotations.yaml") + +labels="${labels} +LABEL com.redhat.delivery.backport=true +LABEL com.redhat.delivery.operator.bundle=true" + +LABELS="${labels}" envsubst "${bundle_directory}/Dockerfile" + +awk '{gsub(/^[ \t]+/, " "); print}' "${bundle_directory}/Dockerfile" >"${bundle_directory}/Dockerfile.new" && mv "${bundle_directory}/Dockerfile.new" "${bundle_directory}/Dockerfile" + +# Include CRDs as manifests. +crd_names=$(yq eval -o=tsv '.metadata.name' ../../deploy/crd.yaml) + +gawk -v names="${crd_names}" -v bundle_directory="${bundle_directory}" ' +BEGIN { + split(names, name_array, " "); + idx=1; +} +/apiVersion: apiextensions.k8s.io\/v1/ { + if (idx in name_array) { + current_file = bundle_directory "/manifests/" name_array[idx] ".crd.yaml"; + idx++; + } else { + current_file = bundle_directory "/unnamed_" idx ".yaml"; + idx++; + } +} +{ + if (current_file != "") { + print > current_file; + } +} +' ../../deploy/crd.yaml + +find "${bundle_directory}/manifests" -type f -name "*.crd.yaml" -exec sed -i '' '1s/^/---\n/; ${/^---$/d;}' {} + + +abort() { + echo >&2 "$@" + exit 1 +} +dump() { yq --color-output; } + +# The first command render yaml correctly and the second extract data. + +yq eval -i '[.]' operator_deployments.yaml && yq eval 'length == 1' operator_deployments.yaml --exit-status >/dev/null || abort "too many deployments!" $'\n'"$(yq eval . operator_deployments.yaml)" + +yq eval -i '[.]' operator_accounts.yaml && yq eval 'length == 1' operator_accounts.yaml --exit-status >/dev/null || abort "too many service accounts!" $'\n'"$(yq eval . operator_accounts.yaml)" + +# Wrap roles into arrays +yq eval-all '[.]' operator_cluster_roles.yaml >operator_cluster_roles_arr.yaml && mv operator_cluster_roles_arr.yaml operator_cluster_roles.yaml +yq eval-all '[.]' operator_ns_roles.yaml >operator_ns_roles_arr.yaml && mv operator_ns_roles_arr.yaml operator_ns_roles.yaml + +# Render bundle CSV and strip comments. +export stem=$(yq -r '.projectName' "${project_directory}/PROJECT") +export version="${VERSION}${suffix}" +export timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +export name="${stem}.v${VERSION}${suffix}" +export name_certified="${stem}-certified.v${VERSION}${suffix}" +export name_certified_rhmp="${stem}-certified-rhmp.v${VERSION}${suffix}" +export skip_range=""${bundle_directory}/manifests/${file_name}.v${VERSION}.clusterserviceversion.yaml" + +# Patch WATCH_NAMESPACE to use OLM targetNamespaces annotation +yq eval --inplace ' + (.spec.install.spec.deployments[].spec.template.spec.containers[].env[] | select(.name == "WATCH_NAMESPACE")) |= + {"name": "WATCH_NAMESPACE", "valueFrom": {"fieldRef": {"fieldPath": "metadata.annotations['"'"'olm.targetNamespaces'"'"']"}}} +' "${bundle_directory}/manifests/${file_name}.v${VERSION}.clusterserviceversion.yaml" + +if [ "${DISTRIBUTION}" == "community" ]; then + update_yaml_images "bundles/$DISTRIBUTION/manifests/${file_name}.v${VERSION}.clusterserviceversion.yaml" +elif [ "${DISTRIBUTION}" == "redhat" ]; then + yq eval --inplace ' + .metadata.annotations["features.operators.openshift.io/disconnected"] = "true" | + .metadata.annotations["features.operators.openshift.io/fips-compliant"] = "false" | + .metadata.annotations["features.operators.openshift.io/proxy-aware"] = "false" | + .metadata.annotations["features.operators.openshift.io/tls-profiles"] = "false" | + .metadata.annotations["features.operators.openshift.io/token-auth-aws"] = "false" | + .metadata.annotations["features.operators.openshift.io/token-auth-azure"] = "false" | + .metadata.annotations["features.operators.openshift.io/token-auth-gcp"] = "false" | + .metadata.annotations["features.operators.openshift.io/cnf"] = "false" | + .metadata.annotations["features.operators.openshift.io/cni"] = "false" | + .metadata.annotations["features.operators.openshift.io/csi"] = "false" | + .spec.relatedImages = env(relatedImages) | + .metadata.annotations.certified = "true" | + .metadata.annotations["containerImage"] = "registry.connect.redhat.com/percona/percona-server-mysql-operator@sha256:" | + .metadata.name = strenv(name_certified)' \ + "${bundle_directory}/manifests/${file_name}.v${VERSION}.clusterserviceversion.yaml" + +elif [ "${DISTRIBUTION}" == "marketplace" ]; then + # Annotations needed when targeting Red Hat Marketplace + export package_url="https://marketplace.redhat.com/en-us/operators/${file_name}" + yq --inplace ' + .metadata.annotations["features.operators.openshift.io/disconnected"] = "true" | + .metadata.annotations["features.operators.openshift.io/fips-compliant"] = "false" | + .metadata.annotations["features.operators.openshift.io/proxy-aware"] = "false" | + .metadata.annotations["features.operators.openshift.io/tls-profiles"] = "false" | + .metadata.annotations["features.operators.openshift.io/token-auth-aws"] = "false" | + .metadata.annotations["features.operators.openshift.io/token-auth-azure"] = "false" | + .metadata.annotations["features.operators.openshift.io/token-auth-gcp"] = "false" | + .metadata.annotations["features.operators.openshift.io/cnf"] = "false" | + .metadata.annotations["features.operators.openshift.io/cni"] = "false" | + .metadata.annotations["features.operators.openshift.io/csi"] = "false" | + .metadata.name = env(name_certified_rhmp) | + .metadata.annotations["containerImage"] = "registry.connect.redhat.com/percona/percona-server-mysql-operator@sha256:" | + .metadata.annotations["marketplace.openshift.io/remote-workflow"] = + "https://marketplace.redhat.com/en-us/operators/percona-server-mysql-operator-certified-rhmp/pricing?utm_source=openshift_console" | + .metadata.annotations["marketplace.openshift.io/support-workflow"] = + "https://marketplace.redhat.com/en-us/operators/percona-server-mysql-operator-certified-rhmp/support?utm_source=openshift_console" | + .spec.relatedImages = env(relatedImages)' \ + "${bundle_directory}/manifests/${file_name}.v${VERSION}.clusterserviceversion.yaml" +fi + +# Delete comments +sed -i '' '/^[[:space:]]*# [^#]/d' "${bundle_directory}/manifests/${file_name}.v${VERSION}.clusterserviceversion.yaml" + +# Lint the bundle YAML files. +yamllint -d '{extends: default, rules: {line-length: disable, indentation: disable}}' bundles/"$DISTRIBUTION" + +if >/dev/null command -v tree; then tree -C "${bundle_directory}"; fi