├── .gitignore ├── Dockerfile ├── Makefile ├── PROJECT ├── README.adoc ├── bundle.Dockerfile ├── bundle ├── manifests │ ├── gitea-operator-controller-manager-metrics-service_v1_service.yaml │ ├── gitea-operator-gitea-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── gitea-operator-gitea-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── gitea-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── gitea-operator.clusterserviceversion.yaml │ └── pfe.rhpds.com_gitea.yaml ├── metadata │ └── annotations.yaml └── tests │ └── scorecard │ └── config.yaml ├── catalog_source.yaml ├── config ├── crd │ ├── bases │ │ └── pfe.rhpds.com_gitea.yaml │ ├── kustomization.yaml │ └── patches │ │ └── crd_openapi.yaml ├── default │ ├── kustomization.yaml │ ├── manager_metrics_patch.yaml │ └── metrics_service.yaml ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── manifests │ ├── bases │ │ └── gitea-operator.clusterserviceversion.yaml │ ├── kustomization.yaml │ └── patches │ │ └── csv.yaml ├── network-policy │ ├── allow-metrics-traffic.yaml │ └── kustomization.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── gitea_editor_role.yaml │ ├── gitea_viewer_role.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── metrics_auth_role.yaml │ ├── metrics_auth_role_binding.yaml │ ├── metrics_reader_role.yaml │ ├── role.yaml │ ├── role_binding.yaml │ └── service_account.yaml ├── samples │ ├── gitea-server.yaml │ ├── kustomization.yaml │ └── pfe_v1_gitea.yaml ├── scorecard │ ├── bases │ │ └── config.yaml │ ├── kustomization.yaml │ └── patches │ │ ├── basic.config.yaml │ │ └── olm.config.yaml └── testing │ ├── debug_logs_patch.yaml │ ├── kustomization.yaml │ ├── manager_image.yaml │ └── pull_policy │ ├── Always.yaml │ ├── IfNotPresent.yaml │ └── Never.yaml ├── gitea-base64.svg ├── gitea-catalog.Dockerfile ├── gitea-catalog └── index.yaml ├── molecule ├── default │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── kustomize.yml │ ├── molecule.yml │ ├── prepare.yml │ ├── tasks │ │ └── gitea_test.yml │ └── verify.yml └── kind │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ └── molecule.yml ├── playbooks └── gitea.yml ├── requirements.yml ├── roles ├── gitea-ocp │ ├── README.adoc │ ├── defaults │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── tasks │ │ ├── create_user.yml │ │ ├── main.yml │ │ └── migrate_repos.yml │ ├── templates │ │ ├── configmap.yaml.j2 │ │ ├── deployment.yaml.j2 │ │ ├── persistentvolumeclaim.yaml.j2 │ │ ├── route.yaml.j2 │ │ ├── service.yaml.j2 │ │ └── serviceaccount.yaml.j2 │ └── vars │ │ └── main.yml └── postgresql-ocp │ ├── README.adoc │ ├── defaults │ └── main.yml │ ├── meta │ └── main.yml │ ├── tasks │ └── main.yml │ └── templates │ ├── deployment.yaml.j2 │ ├── persistentvolumeclaim.yaml.j2 │ ├── secret.yaml.j2 │ └── service.yaml.j2 └── watches.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | bin 9 | 10 | # editor and IDE paraphernalia 11 | .idea 12 | *.swp 13 | *.swo 14 | *~ 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/operator-framework/ansible-operator:v1.37.2 2 | 3 | COPY requirements.yml ${HOME}/requirements.yml 4 | RUN ansible-galaxy collection install -r ${HOME}/requirements.yml \ 5 | && chmod -R ug+rwx ${HOME}/.ansible 6 | 7 | COPY watches.yaml ${HOME}/watches.yaml 8 | COPY roles/ ${HOME}/roles/ 9 | COPY playbooks/ ${HOME}/playbooks/ 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # VERSION defines the project version for the bundle. 2 | # Update this value when you upgrade the version of your project. 3 | # To re-generate a bundle for another specific version without changing the standard setup, you can: 4 | # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) 5 | # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) 6 | VERSION ?= 0.0.1 7 | 8 | # CHANNELS define the bundle channels used in the bundle. 9 | # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") 10 | # To re-generate a bundle for other specific channels without changing the standard setup, you can: 11 | # - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) 12 | # - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") 13 | ifneq ($(origin CHANNELS), undefined) 14 | BUNDLE_CHANNELS := --channels=$(CHANNELS) 15 | endif 16 | 17 | # DEFAULT_CHANNEL defines the default channel used in the bundle. 18 | # Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") 19 | # To re-generate a bundle for any other default channel without changing the default setup, you can: 20 | # - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) 21 | # - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") 22 | ifneq ($(origin DEFAULT_CHANNEL), undefined) 23 | BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) 24 | endif 25 | BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) 26 | 27 | # IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images. 28 | # This variable is used to construct full image tags for bundle and catalog images. 29 | # 30 | # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both 31 | # rhpds.com/gitea-operator-bundle:$VERSION and rhpds.com/gitea-operator-catalog:$VERSION. 32 | IMAGE_TAG_BASE ?= rhpds.com/gitea-operator 33 | 34 | # BUNDLE_IMG defines the image:tag used for the bundle. 35 | # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) 36 | BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) 37 | 38 | # BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command 39 | BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) 40 | 41 | # USE_IMAGE_DIGESTS defines if images are resolved via tags or digests 42 | # You can enable this value if you would like to use SHA Based Digests 43 | # To enable set flag to true 44 | USE_IMAGE_DIGESTS ?= false 45 | ifeq ($(USE_IMAGE_DIGESTS), true) 46 | BUNDLE_GEN_FLAGS += --use-image-digests 47 | endif 48 | 49 | # Set the Operator SDK version to use. By default, what is installed on the system is used. 50 | # This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. 51 | OPERATOR_SDK_VERSION ?= v1.39.2 52 | 53 | # Image URL to use all building/pushing image targets 54 | IMG ?= controller:latest 55 | 56 | .PHONY: all 57 | all: docker-build 58 | 59 | ##@ General 60 | 61 | # The help target prints out all targets with their descriptions organized 62 | # beneath their categories. The categories are represented by '##@' and the 63 | # target descriptions by '##'. The awk commands is responsible for reading the 64 | # entire set of makefiles included in this invocation, looking for lines of the 65 | # file as xyz: ## something, and then pretty-format the target and help. Then, 66 | # if there's a line with ##@ something, that gets pretty-printed as a category. 67 | # More info on the usage of ANSI control characters for terminal formatting: 68 | # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters 69 | # More info on the awk command: 70 | # http://linuxcommand.org/lc3_adv_awk.php 71 | 72 | .PHONY: help 73 | help: ## Display this help. 74 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 75 | 76 | ##@ Build 77 | 78 | .PHONY: run 79 | ANSIBLE_ROLES_PATH?="$(shell pwd)/roles" 80 | run: ansible-operator ## Run against the configured Kubernetes cluster in ~/.kube/config 81 | $(ANSIBLE_OPERATOR) run 82 | 83 | .PHONY: docker-build 84 | docker-build: ## Build docker image with the manager. 85 | docker build -t ${IMG} . 86 | 87 | .PHONY: docker-push 88 | docker-push: ## Push docker image with the manager. 89 | docker push ${IMG} 90 | 91 | # PLATFORMS defines the target platforms for the manager image be build to provide support to multiple 92 | # architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: 93 | # - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ 94 | # - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ 95 | # - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> than the export will fail) 96 | # To properly provided solutions that supports more than one platform you should use this option. 97 | PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le 98 | .PHONY: docker-buildx 99 | docker-buildx: ## Build and push docker image for the manager for cross-platform support 100 | - docker buildx create --name project-v3-builder 101 | docker buildx use project-v3-builder 102 | - docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile . 103 | - docker buildx rm project-v3-builder 104 | 105 | ##@ Deployment 106 | 107 | .PHONY: install 108 | install: kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. 109 | $(KUSTOMIZE) build config/crd | kubectl apply -f - 110 | 111 | .PHONY: uninstall 112 | uninstall: kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. 113 | $(KUSTOMIZE) build config/crd | kubectl delete -f - 114 | 115 | .PHONY: deploy 116 | deploy: kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. 117 | cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} 118 | $(KUSTOMIZE) build config/default | kubectl apply -f - 119 | 120 | .PHONY: undeploy 121 | undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. 122 | $(KUSTOMIZE) build config/default | kubectl delete -f - 123 | 124 | OS := $(shell uname -s | tr '[:upper:]' '[:lower:]') 125 | ARCH := $(shell uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/') 126 | 127 | .PHONY: kustomize 128 | KUSTOMIZE = $(shell pwd)/bin/kustomize 129 | kustomize: ## Download kustomize locally if necessary. 130 | ifeq (,$(wildcard $(KUSTOMIZE))) 131 | ifeq (,$(shell which kustomize 2>/dev/null)) 132 | @{ \ 133 | set -e ;\ 134 | mkdir -p $(dir $(KUSTOMIZE)) ;\ 135 | curl -sSLo - https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v5.4.3/kustomize_v5.4.3_$(OS)_$(ARCH).tar.gz | \ 136 | tar xzf - -C bin/ ;\ 137 | } 138 | else 139 | KUSTOMIZE = $(shell which kustomize) 140 | endif 141 | endif 142 | 143 | .PHONY: ansible-operator 144 | ANSIBLE_OPERATOR = $(shell pwd)/bin/ansible-operator 145 | ansible-operator: ## Download ansible-operator locally if necessary, preferring the $(pwd)/bin path over global if both exist. 146 | ifeq (,$(wildcard $(ANSIBLE_OPERATOR))) 147 | ifeq (,$(shell which ansible-operator 2>/dev/null)) 148 | @{ \ 149 | set -e ;\ 150 | mkdir -p $(dir $(ANSIBLE_OPERATOR)) ;\ 151 | curl -sSLo $(ANSIBLE_OPERATOR) https://github.com/operator-framework/ansible-operator-plugins/releases/download/v1.37.2/ansible-operator_$(OS)_$(ARCH) ;\ 152 | chmod +x $(ANSIBLE_OPERATOR) ;\ 153 | } 154 | else 155 | ANSIBLE_OPERATOR = $(shell which ansible-operator) 156 | endif 157 | endif 158 | 159 | .PHONY: operator-sdk 160 | OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk 161 | operator-sdk: ## Download operator-sdk locally if necessary. 162 | ifeq (,$(wildcard $(OPERATOR_SDK))) 163 | ifeq (, $(shell which operator-sdk 2>/dev/null)) 164 | @{ \ 165 | set -e ;\ 166 | mkdir -p $(dir $(OPERATOR_SDK)) ;\ 167 | curl -sSLo $(OPERATOR_SDK) https://github.com/operator-framework/operator-sdk/releases/download/$(OPERATOR_SDK_VERSION)/operator-sdk_$(OS)_$(ARCH) ;\ 168 | chmod +x $(OPERATOR_SDK) ;\ 169 | } 170 | else 171 | OPERATOR_SDK = $(shell which operator-sdk) 172 | endif 173 | endif 174 | 175 | .PHONY: bundle 176 | bundle: kustomize operator-sdk ## Generate bundle manifests and metadata, then validate generated files. 177 | $(OPERATOR_SDK) generate kustomize manifests -q 178 | cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) 179 | $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle $(BUNDLE_GEN_FLAGS) 180 | $(OPERATOR_SDK) bundle validate ./bundle 181 | 182 | .PHONY: bundle-build 183 | bundle-build: ## Build the bundle image. 184 | docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . 185 | 186 | .PHONY: bundle-push 187 | bundle-push: ## Push the bundle image. 188 | $(MAKE) docker-push IMG=$(BUNDLE_IMG) 189 | 190 | .PHONY: opm 191 | OPM = $(LOCALBIN)/opm 192 | opm: ## Download opm locally if necessary. 193 | ifeq (,$(wildcard $(OPM))) 194 | ifeq (,$(shell which opm 2>/dev/null)) 195 | @{ \ 196 | set -e ;\ 197 | mkdir -p $(dir $(OPM)) ;\ 198 | curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$(OS)-$(ARCH)-opm ;\ 199 | chmod +x $(OPM) ;\ 200 | } 201 | else 202 | OPM = $(shell which opm) 203 | endif 204 | endif 205 | 206 | # A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). 207 | # These images MUST exist in a registry and be pull-able. 208 | BUNDLE_IMGS ?= $(BUNDLE_IMG) 209 | 210 | # The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). 211 | CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) 212 | 213 | # Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. 214 | ifneq ($(origin CATALOG_BASE_IMG), undefined) 215 | FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) 216 | endif 217 | 218 | # Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. 219 | # This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: 220 | # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator 221 | .PHONY: catalog-build 222 | catalog-build: opm ## Build a catalog image. 223 | $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) 224 | 225 | # Push the catalog image. 226 | .PHONY: catalog-push 227 | catalog-push: ## Push a catalog image. 228 | $(MAKE) docker-push IMG=$(CATALOG_IMG) 229 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | # Code generated by tool. DO NOT EDIT. 2 | # This file is used to track the info used to scaffold your project 3 | # and allow the plugins properly work. 4 | # More info: https://book.kubebuilder.io/reference/project-config.html 5 | domain: rhpds.com 6 | layout: 7 | - ansible.sdk.operatorframework.io/v1 8 | plugins: 9 | manifests.sdk.operatorframework.io/v2: {} 10 | scorecard.sdk.operatorframework.io/v2: {} 11 | projectName: gitea-operator 12 | resources: 13 | - api: 14 | crdVersion: v1 15 | namespaced: true 16 | domain: rhpds.com 17 | group: pfe 18 | kind: Gitea 19 | version: v1 20 | version: "3" 21 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Gitea Operator 2 | 3 | This repository holds the source code for the Red Hat Portfolio Marketing Gitea Operator. 4 | The operator can be recreated by following the steps in https://github.com/redhat-cop/openshift-lab-origin/blob/master/OpenShift4/Building_Ansible_Operator_for_Gitea.adoc. 5 | 6 | == Installing the Operator 7 | 8 | The V2 operator can now be installed into its own namespace `gitea-operator` instead of just `openshift-operators`. 9 | 10 | . You can create the operator by simply applying the YAML files from the Github repository 11 | + 12 | ---- 13 | oc apply -k https://github.com/rhpds/gitea-operator/OLMDeploy 14 | ---- 15 | 16 | . This installs the operator cluster wide - which means you (or your users) can create instances of `Gitea` in any project on the cluster. See below for examples of how to create Gitea instances. 17 | 18 | == Creating Gitea instances 19 | 20 | . Create a new project for your Gitea instance: 21 | + 22 | ---- 23 | oc new-project gitea 24 | ---- 25 | 26 | . All following examples can be combined. For example you can have a Gitea installation with self-managed PostgreSQL database, with e-mail integration, selecting specific storage classes for the PVCs, an Admin user created and self-provisioning turned off. 27 | 28 | === Integrated Setup 29 | 30 | The Gitea Operator can set up a PostgreSQL database alongside the Gitea Pod. This is the default deployment. 31 | 32 | To deploy a simple Gitea instance: 33 | 34 | ---- 35 | --- 36 | apiVersion: pfe.rhpds.com/v1 37 | kind: Gitea 38 | metadata: 39 | name: simple-gitea 40 | spec: 41 | giteaImageTag: 1.20.0 42 | giteaVolumeSize: 4Gi 43 | giteaSsl: true 44 | postgresqlVolumeSize: 4Gi 45 | ---- 46 | 47 | Note that you can specify specific properties for `postgresqlServiceName`, `postgresqlDatabaseName`, `postgresqlUser` and `postgresqlPassword`. If you do however you *must* also specify the relevant properties for Gitea to connect to the database and they must match the PostgreSQL properties: `giteaPostgresqlServiceName`, `giteaPostgresqlDatabaseName`, `giteaPostgresqlUser` and `giteaPostgresqlPassword` 48 | 49 | To deploy a simple Gitea instance using OpenShift Container Storage (if the storage class `ocs-storagecluster-ceph-rbd` is not the default storage class): 50 | 51 | ---- 52 | --- 53 | apiVersion: pfe.rhpds.com/v1 54 | kind: Gitea 55 | metadata: 56 | name: simple-gitea-ocs 57 | spec: 58 | giteaImageTag: latest 59 | giteaVolumeSize: 4Gi 60 | giteaVolumeStorageClass: ocs-storagecluster-ceph-rbd 61 | giteaSsl: true 62 | postgresqlVolumeSize: 4Gi 63 | postgresqlVolumeStorageClass: ocs-storagecluster-ceph-rbd 64 | ---- 65 | 66 | === Create Admin User 67 | 68 | The Operator can create an Admin User in the Gitea instance. If an Admin User ID is specified you can specify a password for the admin user as well. If no password is specified a password is generated with a certain length. The length of the generated password can be set via the password length variable. 69 | 70 | If an Admin user has been created then you can set the variable `giteaDisableRegistration: false`. This will disable user self-registration - only the admin user will be able to create other users on Gitea. 71 | 72 | The operator will report the status of the admin user creation as well as the user password in the fields `.status.adminSetupComplete: true` and `.status.adminPassword`. 73 | 74 | ---- 75 | --- 76 | apiVersion: pfe.rhpds.com/v1 77 | kind: Gitea 78 | metadata: 79 | name: gitea-with-admin 80 | spec: 81 | giteaSsl: true 82 | giteaAdminUser: opentlc-mgr 83 | giteaAdminEmail: opentlc-mgr@redhat.com 84 | giteaAdminPassword: "" 85 | giteaAdminPasswordLength: 32 86 | ---- 87 | 88 | === Create Admin user with password from secret 89 | 90 | The operator can use a password from the admin user that has been pre-created in a secret. The secret *must* be in the same namespace as the gitea instance - and the key for the admin password *must* be `adminPassword`. 91 | 92 | . Create a secret with the password: 93 | + 94 | ---- 95 | oc create secret generic gitea-admin-password --from-literal=adminPassword=myPassw0rd 96 | ---- 97 | 98 | . Tell the operator to use the password from the secret: 99 | + 100 | ---- 101 | --- 102 | apiVersion: pfe.rhpds.com/v1 103 | kind: Gitea 104 | metadata: 105 | name: gitea-with-admin 106 | spec: 107 | giteaSsl: true 108 | giteaAdminUser: opentlc-mgr 109 | giteaAdminEmail: opentlc-mgr@redhat.com 110 | giteaAdminPasswordSecretName: gitea-admin-password 111 | ---- 112 | 113 | It may be useful to hide the admin password from the `status` section of the Gitea custom resource. In that case set the variable `giteaAdminPasswordExpose: false`. For compatibility reasons the default for that variable is `true` which means that whatever password has been set for the admin user will be visible in `status.adminPassword`. 114 | 115 | === Create regular users 116 | 117 | The Operator can also create regular users in the Gitea instance. It is a requirement that an admin user is also being created. 118 | 119 | The operator can create a single user - or a number of users based on a username template. 120 | 121 | You can specify a common password for all regular users. If no password is specified a password is generated with a certain length. The length of the generated password can be set via the password length variable. 122 | 123 | If users have been created then you can set the variable `giteaDisableRegistration: false`. This will disable user self-registration - only the admin user will be able to create additional users on Gitea. 124 | 125 | The operator will report the status of the user creation as well as the user password in the fields `.status.userSetupComplete: true` and `.status.userPassword`. 126 | 127 | Create a single user, `lab-user` with a generated password of length 16: 128 | 129 | ---- 130 | --- 131 | apiVersion: pfe.rhpds.com/v1 132 | kind: Gitea 133 | metadata: 134 | name: gitea-with-admin 135 | spec: 136 | giteaSsl: true 137 | giteaAdminUser: opentlc-mgr 138 | giteaAdminPassword: "" 139 | giteaAdminPasswordLength: 32 140 | giteaAdminEmail: opentlc-mgr@redhat.com 141 | giteaCreateUsers: true 142 | giteaGenerateUserFormat: lab-user 143 | giteaUserNumber: 1 144 | giteaUserPasswordLength: 16 145 | ---- 146 | 147 | Create a three users, `student1`, `student2` and `student3` with password `openshift`: 148 | 149 | ---- 150 | --- 151 | apiVersion: pfe.rhpds.com/v1 152 | kind: Gitea 153 | metadata: 154 | name: gitea-with-users 155 | spec: 156 | giteaSsl: true 157 | 158 | giteaAdminUser: opentlc-mgr 159 | giteaAdminPassword: "" 160 | giteaAdminPasswordLength: 32 161 | giteaAdminEmail: opentlc-mgr@redhat.com 162 | 163 | giteaCreateUsers: true 164 | giteaGenerateUserFormat: "student%d" 165 | giteaUserNumber: 3 166 | giteaUserPassword: openshift 167 | ---- 168 | 169 | === Create users with password from secret 170 | 171 | The operator can use a password for the created users that has been pre-created in a secret. The secret *must* be in the same namespace as the gitea instance - and the key for the user password *must* be `userPassword`. 172 | 173 | . Create a secret with the password: 174 | + 175 | ---- 176 | oc create secret generic gitea-user-password --from-literal=userPassword=myPassw0rd 177 | ---- 178 | 179 | . Tell the operator to use the password from the secret (this example also generates a new 16 character password for the admin user): 180 | + 181 | ---- 182 | --- 183 | apiVersion: pfe.rhpds.com/v1 184 | kind: Gitea 185 | metadata: 186 | name: gitea-with-users 187 | spec: 188 | giteaSsl: true 189 | 190 | giteaAdminUser: opentlc-mgr 191 | giteaAdminEmail: opentlc-mgr@redhat.com 192 | giteaAdminPassword: "" 193 | giteaAdminPasswordLength: 16 194 | 195 | giteaCreateUsers: true 196 | giteaGenerateUserFormat: "student%d" 197 | giteaUserNumber: 3 198 | giteaUserPasswordSecretName: gitea-user-password 199 | ---- 200 | 201 | It may be useful to hide the user password from the `status` section of the Gitea custom resource. In that case set the variable `giteaUserPasswordExpose: false`. For compatibility reasons the default for that variable is `true` which means that whatever password has been set for the users will be visible in `status.userPassword`. 202 | 203 | === Migrating repositories for created users 204 | 205 | If users are being created it is also possible to seed all users with repositories from another github (compatible) source. Every user will get the same repositories in their account. You can specify the source URL of the repository, the name of the migrated repository in Gitea and if the migrated repository should be a private repository or not. 206 | 207 | If the migration was successful the operator sets the field `.status.repoMigrationComplete: true`. 208 | 209 | Create 2 users `lab-user-1` and `lab-user-2` and migrate two repositories from GitHub to Gitea for each user: 210 | 211 | ---- 212 | --- 213 | apiVersion: pfe.rhpds.com/v1 214 | kind: Gitea 215 | metadata: 216 | name: gitea-with-repositories 217 | spec: 218 | giteaSsl: true 219 | 220 | giteaAdminUser: opentlc-mgr 221 | giteaAdminPassword: "" 222 | giteaAdminPasswordLength: 32 223 | giteaAdminEmail: opentlc-mgr@redhat.com 224 | 225 | giteaCreateUsers: true 226 | giteaGenerateUserFormat: "lab-user-%d" 227 | giteaUserNumber: 2 228 | giteaUserPassword: openshift 229 | 230 | giteaMigrateRepositories: true 231 | giteaRepositoriesList: 232 | - repo: https://github.com/repository1.git 233 | name: repository1 234 | private: false 235 | - repo: https://github.com/repository2.git 236 | name: another-repository 237 | private: true 238 | ---- 239 | 240 | === Set up e-mail Service 241 | 242 | See https://docs.gitea.io/en-us/email-setup for more information on how to set up e-mail services with Gitea. 243 | 244 | . Set the variable `giteaMailerEnabled: true`. If this is set to true then the other `giteaMailer*` variables need to be set as well. If the e-mail account you are using uses two-factor authentication (for example GMail) you may need to create an app-specific password to be used. 245 | + 246 | Once e-mail is enabled you can use the variables `giteaRegisterEmailConfirm` and `giteaEnableNotifyMail` to turn on e-mail verification and notification. 247 | + 248 | .Example for GMail 249 | ---- 250 | --- 251 | apiVersion: pfe.rhpds.com/v1 252 | kind: Gitea 253 | metadata: 254 | name: gitea-with-email 255 | spec: 256 | giteaMailerEnabled: true 257 | giteaMailerFrom: gmail-user@gmail.com 258 | giteaMailerProtocol: smtps 259 | giteaMailerHost: smtp.gmail.com 260 | giteaMailerPort: 465 261 | giteaMailerUser: gmail-user@gmail.com 262 | giteaMailerPassword: gmail-user-app-specific-password 263 | giteaMailerHeloHostname: example.com 264 | giteaRegisterEmailConfirm: true 265 | giteaEnableNotifyMail: true 266 | ---- 267 | 268 | === Unmanaged PostgreSQL database 269 | 270 | If you want to manage your PostgreSQL database separately from the Gitea pod then you can deploy it first. You can either use the OpenShift template or you can use a PostgreSQL operator to manage your database. 271 | 272 | . Create a PostgreSQL database from the OpenShift template: 273 | + 274 | ---- 275 | oc new-app postgresql-persistent \ 276 | --param DATABASE_SERVICE_NAME=postgresql-gitea \ 277 | --param POSTGRESQL_USER=gitea_user \ 278 | --param POSTGRESQL_PASSWORD=gitea_pass \ 279 | --param POSTGRESQL_DATABASE=gitea \ 280 | --param VOLUME_CAPACITY=10Gi 281 | ---- 282 | + 283 | [NOTE] 284 | You can not specify a storage class when using the OpenShift template. If you need a specific storage class make that storage class the default storage class before creating the database. You can switch back to another default storage class once the persistent volume has been created. 285 | 286 | . Create a Gitea instance. When using a self-managed database you must set `postgresqlSetup: false` and specify connection information to the database. 287 | 288 | ---- 289 | --- 290 | apiVersion: pfe.rhpds.com/v1 291 | kind: Gitea 292 | metadata: 293 | name: gitea-unmanaged-db 294 | spec: 295 | postgresqlSetup: false 296 | giteaPostgresqlServiceName: postgresql-gitea 297 | giteaPostgresqlDatabaseName: gitea 298 | giteaPostgresqlUser: gitea_user 299 | giteaPostgresqlPassword: gitea_pass 300 | giteaVolumeSize: 10Gi 301 | ---- 302 | 303 | === Provide app.ini in ConfigMap 304 | 305 | A rather advanced use case is to create the `app.ini` configuration for Gitea yourself rather than having the operator create it. 306 | 307 | Usually the operator generates the `app.ini` configuration file based on the content of the variables in the Gitea custom resource. However not every single configuration option for Gitea is exposed as Gitea variables. Maybe because they are rather uncommon or because they only got added to a Gitea version that got released after the operator was built. 308 | 309 | One use case could be enabling `ssh` cloning capability for the Gitea repository. 310 | 311 | Another use case could be setting up OAuth2 authorization for the repository. 312 | 313 | The suggested approach is this: 314 | 315 | * Set up a Gitea instance with all variables that are available. 316 | * Wait until the Gitea instance is running 317 | * Extract (save) the ConfigMap that has been created (usually in `-config`) 318 | + 319 | ---- 320 | oc get configmap gitea-config -n gitea -o yaml >$HOME/configmap.yaml 321 | ---- 322 | 323 | * Edit the file `app.ini` in the configmap as desired (the file name always *must* be `app.ini`). 324 | * Delete the Gitea config. 325 | 326 | Now you can re-create the configuration from scratch. You can have the Gitea operator create the PostgreSQL database - but of course you must specify a user name and password for the PostgreSQL database that matches what you will provide in your `app.ini` config map. 327 | 328 | . Create the project 329 | . Determine what the `hostname` of your Gitea installation will be. This hostname *must* match in the configmap's `app.ini` file and in the parameter `giteaHostname` (which *must* be provided in case an external configmap is provided) 330 | . Create the ConfigMap (from file or otherwise) making sure the URLs match the desired hostname and the database connectivity matches your PostgreSQL database (either pre-setup of setup by the operator) 331 | + 332 | .Example 333 | ---- 334 | [...] 335 | [database] 336 | DB_TYPE = postgres 337 | HOST = postgresql-gitea:5432 338 | NAME = giteadb 339 | USER = gitea_user 340 | PASSWD = gitea_pass 341 | SSL_MODE = disable 342 | 343 | [...] 344 | 345 | [server] 346 | ROOT_URL = https://gitea-gitea.apps.cluster-hyper.hyper.dev.redhatworkshops.io 347 | SSH_DOMAIN = gitea-gitea.apps.cluster-hyper.hyper.dev.redhatworkshops.io 348 | DOMAIN = gitea-gitea.apps.cluster-hyper.hyper.dev.redhatworkshops.io 349 | 350 | [...] 351 | ---- 352 | . Create Gitea making sure the `giteaHostname` is part of the `spec`. 353 | 354 | .Example 355 | ---- 356 | --- 357 | apiVersion: pfe.rhpds.com/v1 358 | kind: Gitea 359 | metadata: 360 | name: gitea-ext-config 361 | spec: 362 | postgresqlSetup: true 363 | giteaPostgresqlServiceName: postgresql-gitea 364 | giteaPostgresqlDatabaseName: giteadb 365 | giteaPostgresqlUser: gitea_user 366 | giteaPostgresqlPassword: gitea_pass 367 | 368 | giteaImageTag: 1.20.0 369 | giteaVolumeSize: 4Gi 370 | giteaAdminUser: wkadmin 371 | giteaAdminPassword: "" 372 | giteaAdminUserPasswordLength: 16 373 | 374 | giteaConfigMapName: custom-gitea-config 375 | giteaSsl: true 376 | giteaHostname: gitea.apps.cluster-hyper.hyper.dev.redhatworkshops.io 377 | ---- 378 | 379 | == API Reference: 380 | 381 | Below is a list and description of all possible parameters that can be set for the `Gitea` custom resource. 382 | 383 | ---- 384 | postgresqlSetup: 385 | description: 'Set up a PostgreSQL database alongside the Gitea instance. Default is true. If set to false the values for giteaPostgresqlServiceName, giteaPostgresqlDatabaseName, giteaPostgresqlUser and giteaPostgresqlPassword need to be specified to connect to an existing PostgreSQL database. If set to true no values need to be specified for database name, database service, database user and database service.' 386 | type: boolean 387 | postgresqlServiceName: 388 | description: Name of the PostgreSQL database service. Default is 'postgresql-' followed by the name of the Gitea resource. 389 | type: string 390 | postgresqlDatabaseName: 391 | description: Name of the PostgreSQL Database to be created. Default is 'giteadb'. 392 | type: string 393 | postgresqlUser: 394 | description: Username to be created in the PostgreSQL database. Default is 'giteauser'. 395 | type: string 396 | postgresqlPassword: 397 | description: Password to be used for the PostgreSQL database user. Default is 'giteapassword'. 398 | type: string 399 | 400 | postgresqlVolumeSize: 401 | description: Size of the persistent volume claim for the PostgreSQL database. Default 402 | is '4Gi'. 403 | type: string 404 | postgresqlVolumeStorageClass: 405 | description: Storage Class to be used for the PostgreSQL persistent volume claim. Default is empty - which will create a PVC using the currently available default storage class on the cluster. 406 | type: string 407 | 408 | postgresqlImage: 409 | description: Container image for the PostgreSQL database. Default is 'registry.redhat.io/rhel8/postgresql-12'. 410 | type: string 411 | postgresqlImageTag: 412 | description: Image tag for the PostgreSQL container image. Default is 'latest'. 413 | type: string 414 | postgresqlImagePullPolicy: 415 | description: Pull policy for the PostgreSQL container image. Default is 'IfNotPresent'. 416 | type: string 417 | 418 | postgresqlMemoryRequest: 419 | description: Memory request for the PostgreSQL database. Default is '512Mi'. 420 | type: string 421 | postgresqlMemoryLimit: 422 | description: Memory limit for the PostgreSQL database. Default is '512Mi'. 423 | type: string 424 | postgresqlCpuRequest: 425 | description: CPU request for the PostgreSQL database. Default is '200m'. 426 | type: object 427 | additionalProperties: 428 | anyOf: 429 | - type: integer 430 | - type: string 431 | pattern: "^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$" 432 | x-kubernetes-int-or-string: true 433 | postgresqlCpuLimit: 434 | description: CPU limit for the PostgreSQL database. Default is '500m'. 435 | type: object 436 | additionalProperties: 437 | anyOf: 438 | - type: integer 439 | - type: string 440 | pattern: "^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$" 441 | x-kubernetes-int-or-string: true 442 | 443 | giteaServiceName: 444 | description: Name of the Gitea Service to be deployed. Defaults to the name of the Gitea custom resource. 445 | type: string 446 | giteaSsl: 447 | description: Create an HTTPS terminated route for Gitea. Default is 'false' 448 | type: boolean 449 | giteaHostname: 450 | description: Specify the hostname for the Gitea Route. Default is ''. Make sure the route is reachable from outside the cluster. 451 | type: string 452 | 453 | giteaVolumeSize: 454 | description: Size of the persistent volume claim for Gitea. Default is '4Gi'. 455 | type: string 456 | giteaVolumeStorageClass: 457 | description: Storage Class to be used for the Gitea persistent volume claim. Default is empty - which will create a PVC using the currently available default storage class on the cluster. 458 | type: string 459 | 460 | giteaImage: 461 | description: Container image for Gitea. Default is 'quay.io/rhpds/gitea'. 462 | type: string 463 | giteaImageTag: 464 | description: Image tag for the Gitea container image. Default is 'latest'. 465 | type: string 466 | giteaImagePullPolicy: 467 | description: Pull policy for the Gitea container image. Default is 'IfNotPresent'. 468 | type: string 469 | 470 | giteaMemoryRequest: 471 | description: Memory request for Gitea. Default is '1Gi'. 472 | type: string 473 | giteaMemoryLimit: 474 | description: Memory limit for Gitea. Default is '1Gi'. 475 | type: string 476 | giteaCpuRequest: 477 | description: CPU request for Gitea. Default is '200m'. 478 | type: object 479 | additionalProperties: 480 | anyOf: 481 | - type: integer 482 | - type: string 483 | pattern: "^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$" 484 | x-kubernetes-int-or-string: true 485 | giteaCpuLimit: 486 | description: CPU limit for Gitea. Default is '500m'. 487 | type: object 488 | additionalProperties: 489 | anyOf: 490 | - type: integer 491 | - type: string 492 | pattern: "^(\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\\\+|-)?(([0-9]+(\\\\.[0-9]*)?)|(\\\\.[0-9]+))))?$" 493 | x-kubernetes-int-or-string: true 494 | 495 | giteaPostgresqlServiceName: 496 | description: 'Name of the PostgreSQL service. Only required when PostgreSQL is not set up by the operator. Default: postgresql- followed by the Gitea resource name.' 497 | type: string 498 | giteaPostgresqlDatabaseName: 499 | description: 'Name of the PostgreSQL database. Only required when PostgreSQL is not set up by the operator. Default: giteadb' 500 | type: string 501 | giteaPostgresqlUser: 502 | description: 'Name of the PostgreSQL user. Only required when PostgreSQL is not set up by the operator. Default: giteauser' 503 | type: string 504 | giteaPostgresqlPassword: 505 | description: 'PostgreSQL password. Only required when PostgreSQL is not set up by the operator. Default: giteapassword' 506 | type: string 507 | 508 | giteaConfigMapName: 509 | description: Name of a config map in the same namespace as the Gitea custom resource. The config map must contain one file called app.ini to configure Gitea. If this variable is set then giteaHostname must also be set. giteaSsl should be set but will default to false. 510 | type: string 511 | 512 | giteaAdminUser: 513 | description: 'User ID for the Admin User to be created. If not specified no admin user will be created. Note that if giteaDisableRegistration is set to false and no admin user will be created you will not be able to create any users for Gitea. Default: ""' 514 | type: string 515 | giteaAdminPassword: 516 | description: 'Password for the Gitea admin user. If not specified or empty a random password will be created with length of giteaAdminPasswordLength random ASCII characters. Default: ""' 517 | type: string 518 | giteaAdminPasswordLength: 519 | description: 'If a giteaAdminUser is provided but no giteaAdminPassowrd is provided a random ASCII password with the length specified will be created. Default: 16' 520 | type: integer 521 | giteaAdminPasswordSecretName: 522 | description: Name of a secret containing the Gitea admin user's password in secret key adminPassword. If this variable is set it takes precedence over all other ways to specify/generate an admin password. 523 | type: string 524 | giteaAdminPasswordExpose: 525 | description: Expose the final Gitea admin password in the custom resource status section. Default is true for compatibility. Set to false to not show the password in the status section. 526 | type: boolean 527 | giteaAdminEmail: 528 | description: 'e-mail address for the Gitea Admin User. Default: "notset@notset.org"' 529 | type: string 530 | 531 | giteaCreateUsers: 532 | description: 'Create users in Gitea. Only possible if an admin user is also being created. Default: false' 533 | type: boolean 534 | giteaUserNumber: 535 | description: 'Number of users to create in Gitea. If 1 then only one user will be created with the username from giteaGenerateUserFormat. If more than one then users will be created according to the format in giteaGenerateUserFormat. Default: 2' 536 | type: integer 537 | giteaGenerateUserFormat: 538 | description: 'Format for user names to be created. This will be taken literally if only one user is to be created (e.g. lab-user). If more than one user is to be created the format needs to include a "%d" to set the user number. Default: "user%d"' 539 | type: string 540 | giteaUserPassword: 541 | description: 'Password for all created Gitea users. If not specified or empty a random password will be created with length of giteaUserPasswordLength random ASCII characters. Default: ""' 542 | type: string 543 | giteaUserPasswordLength: 544 | description: 'If a giteaCreateUsers is set but no giteaUserPassowrd is provided a random ASCII password with the length specified will be created. Default: 16' 545 | type: integer 546 | giteaUserEmailDomain: 547 | description: 'e-mail domain for the created Gitea users. Default: "example.com"' 548 | type: string 549 | giteaUserPasswordSecretName: 550 | description: Name of a secret containing the Gitea user common password in secret key userPassword. If this variable is set it takes precedence over all other ways to specify/generate a user password. 551 | type: string 552 | giteaUserPasswordExpose: 553 | description: Expose the common Gitea user password in the custom resource status section. Default is true for compatibility. Set to false to not show the password in the status section. 554 | type: boolean 555 | 556 | giteaMigrateRepositories: 557 | description: 'For created users migrate repositories from another location, e.g. GitHub. Default: false' 558 | type: boolean 559 | giteaRepositoriesList: 560 | description: 'List of repositories to be created. Each repository is an array of "repo: " and "private: true | false". Default: []' 561 | type: array 562 | items: 563 | type: object 564 | properties: 565 | repo: 566 | description: 'Source repository URL to clone.' 567 | type: string 568 | name: 569 | description: 'Name of the repository in Gitea.' 570 | type: string 571 | private: 572 | description: 'Create private repository in Gitea.' 573 | type: boolean 574 | 575 | giteaHttpPort: 576 | description: 'Port for Gitea to listen on. Default: 3000' 577 | type: integer 578 | giteaSshPort: 579 | description: 'Port for Gitea to start an SSH server on. Default: 2022' 580 | type: integer 581 | giteaDisableSsh: 582 | description: 'Disable SSH for Gitea. Default: true' 583 | type: boolean 584 | giteaStartSshServer: 585 | description: 'Start SSH Server in the Gitea container. Default: false' 586 | type: boolean 587 | giteaStartLfsServer: 588 | description: 'Start LFS Server in the Gitea container. Default: false' 589 | type: boolean 590 | giteaDisableRegistration: 591 | description: 'Disable user self-registration. If this flag is set an Admin User should be specified to be created. Otherwise no users can be created at all. Default: false' 592 | type: boolean 593 | giteaEnableCaptcha: 594 | description: 'Display Captcha when users are registering a new account. No effect if giteaDisableRegistration is set to false. Default: false' 595 | type: boolean 596 | giteaAllowCreateOrganization: 597 | description: 'Allow users to create organizations in Gitea. Default: true' 598 | type: boolean 599 | giteaAllowLocalNetworkMigration: 600 | description: 'Allow migration of repositories hosted on local network IPs as defined by RFC 1918, RFC 1122, RFC 4632 and RFC 4291. Default: false' 601 | type: boolean 602 | 603 | giteaWebhookAllowedHostList: 604 | description: List of hosts that a web hook is allowed to call. See https://docs.gitea.com/next/administration/config-cheat-sheet#webhook-webhook for more details. Default is 'external,private'. 605 | type: string 606 | giteaWebhookSkipTlsVerify: 607 | description: Set to 'true' to skip validation of the webhook target URL certificate. Default is false. 608 | type: boolean 609 | 610 | giteaMailerEnabled: 611 | description: 'Enable e-mail integration for Gitea. If set to true the other giteaMailer* properties need to be provided. See https://docs.gitea.io/en-us/email-setup/ for example values. Default: false' 612 | type: boolean 613 | giteaMailerFrom: 614 | description: 'E-mail integration. FROM e-mail address to be used. Default: ""' 615 | type: string 616 | giteaMailerProtocol: 617 | description: 'Protocol of e-mail provider to be used. Default: smtps' 618 | type: string 619 | giteaMailerHost: 620 | description: 'Hostname of the e-mail server to be used. Default: ""' 621 | type: string 622 | giteaMailerPort: 623 | description: 'Port of the e-mail server to be used. Default: ""' 624 | type: integer 625 | giteaMailerUser: 626 | description: 'User ID on the e-mail server to use. Frequently the same as the value for giteaMailerFrom. Default: ""' 627 | type: string 628 | giteaMailerPassword: 629 | description: 'Password for the User ID on the e-mail server to be used. May need to be an app-specific password if two-factor authentication is enabled on the e-mail server. Default: ""' 630 | type: string 631 | giteaMailerHeloHostname: 632 | description: 'Helo Hostname for the e-mail server. Not required for all e-mail providers. Default: ""' 633 | type: string 634 | 635 | giteaRegisterEmailConfirm: 636 | description: 'Send e-mail confirmation to users when self-registering. Users must click a link to validate their e-mail address before the account gets created. Requires the mailer to be configured correctly. Default: false' 637 | type: boolean 638 | giteaEnableNotifyMail: 639 | description: 'Send e-mail notifications to users for various tasks in Gitea. Requires the mailer to be configured correctly. Default: false' 640 | type: boolean 641 | ---- 642 | -------------------------------------------------------------------------------- /bundle.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | # Core bundle labels. 4 | LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 5 | LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ 6 | LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ 7 | LABEL operators.operatorframework.io.bundle.package.v1=gitea-operator 8 | LABEL operators.operatorframework.io.bundle.channels.v1=stable 9 | LABEL operators.operatorframework.io.bundle.channel.default.v1=stable 10 | LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.39.2 11 | LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 12 | LABEL operators.operatorframework.io.metrics.project_layout=ansible.sdk.operatorframework.io/v1 13 | 14 | # Labels for testing. 15 | LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 16 | LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ 17 | 18 | # Copy files to locations specified by labels. 19 | COPY bundle/manifests /manifests/ 20 | COPY bundle/metadata /metadata/ 21 | COPY bundle/tests/scorecard /tests/scorecard/ 22 | -------------------------------------------------------------------------------- /bundle/manifests/gitea-operator-controller-manager-metrics-service_v1_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: gitea-operator 8 | control-plane: controller-manager 9 | name: gitea-operator-controller-manager-metrics-service 10 | spec: 11 | ports: 12 | - name: https 13 | port: 8443 14 | protocol: TCP 15 | targetPort: 8443 16 | selector: 17 | control-plane: controller-manager 18 | status: 19 | loadBalancer: {} 20 | -------------------------------------------------------------------------------- /bundle/manifests/gitea-operator-gitea-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: gitea-operator 8 | name: gitea-operator-gitea-editor-role 9 | rules: 10 | - apiGroups: 11 | - pfe.rhpds.com 12 | resources: 13 | - gitea 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - pfe.rhpds.com 24 | resources: 25 | - gitea/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/gitea-operator-gitea-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: gitea-operator 8 | name: gitea-operator-gitea-viewer-role 9 | rules: 10 | - apiGroups: 11 | - pfe.rhpds.com 12 | resources: 13 | - gitea 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - pfe.rhpds.com 20 | resources: 21 | - gitea/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/gitea-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | name: gitea-operator-metrics-reader 6 | rules: 7 | - nonResourceURLs: 8 | - /metrics 9 | verbs: 10 | - get 11 | -------------------------------------------------------------------------------- /bundle/manifests/gitea-operator.clusterserviceversion.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: ClusterServiceVersion 3 | metadata: 4 | annotations: 5 | alm-examples: |- 6 | [ 7 | { 8 | "apiVersion": "pfe.rhpds.com/v1", 9 | "kind": "Gitea", 10 | "metadata": { 11 | "labels": { 12 | "app.kubernetes.io/managed-by": "kustomize", 13 | "app.kubernetes.io/name": "gitea-operator" 14 | }, 15 | "name": "repository" 16 | }, 17 | "spec": { 18 | "giteaImagePullPolicy": "Always", 19 | "giteaImageTag": "1.23.7", 20 | "giteaSsl": true, 21 | "giteaVolumeSize": "4Gi", 22 | "postgresqlVolumeSize": "4Gi" 23 | } 24 | } 25 | ] 26 | capabilities: Basic Install 27 | createdAt: "2025-05-15T10:27:43Z" 28 | operators.operatorframework.io/builder: operator-sdk-v1.39.2 29 | operators.operatorframework.io/project_layout: ansible.sdk.operatorframework.io/v1 30 | name: gitea-operator.v2.1.0 31 | namespace: placeholder 32 | spec: 33 | apiservicedefinitions: {} 34 | customresourcedefinitions: 35 | owned: 36 | - kind: Gitea 37 | name: gitea.pfe.rhpds.com 38 | version: v1 39 | description: Gitea Operator - provided by Red Hat Demo Platform, see https://github.com/rhpds/gitea-operator 40 | for documentation. 41 | displayName: Gitea Operator 42 | icon: 43 | - base64data: PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJtYWluX291dGxpbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIKCSB5PSIwcHgiIHZpZXdCb3g9IjAgMCA2NDAgNjQwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA2NDAgNjQwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPHBhdGggaWQ9InRlYWJhZyIgc3R5bGU9ImZpbGw6I0ZGRkZGRiIgZD0iTTM5NS45LDQ4NC4ybC0xMjYuOS02MWMtMTIuNS02LTE3LjktMjEuMi0xMS44LTMzLjhsNjEtMTI2LjljNi0xMi41LDIxLjItMTcuOSwzMy44LTExLjgKCQljMTcuMiw4LjMsMjcuMSwxMywyNy4xLDEzbC0wLjEtMTA5LjJsMTYuNy0wLjFsMC4xLDExNy4xYzAsMCw1Ny40LDI0LjIsODMuMSw0MC4xYzMuNywyLjMsMTAuMiw2LjgsMTIuOSwxNC40CgkJYzIuMSw2LjEsMiwxMy4xLTEsMTkuM2wtNjEsMTI2LjlDNDIzLjYsNDg0LjksNDA4LjQsNDkwLjMsMzk1LjksNDg0LjJ6Ii8+Cgk8Zz4KCQk8Zz4KCQkJPHBhdGggc3R5bGU9ImZpbGw6IzYwOTkyNiIgZD0iTTYyMi43LDE0OS44Yy00LjEtNC4xLTkuNi00LTkuNi00cy0xMTcuMiw2LjYtMTc3LjksOGMtMTMuMywwLjMtMjYuNSwwLjYtMzkuNiwwLjdjMCwzOS4xLDAsNzguMiwwLDExNy4yCgkJCQljLTUuNS0yLjYtMTEuMS01LjMtMTYuNi03LjljMC0zNi40LTAuMS0xMDkuMi0wLjEtMTA5LjJjLTI5LDAuNC04OS4yLTIuMi04OS4yLTIuMnMtMTQxLjQtNy4xLTE1Ni44LTguNQoJCQkJYy05LjgtMC42LTIyLjUtMi4xLTM5LDEuNWMtOC43LDEuOC0zMy41LDcuNC01My44LDI2LjlDLTQuOSwyMTIuNCw2LjYsMjc2LjIsOCwyODUuOGMxLjcsMTEuNyw2LjksNDQuMiwzMS43LDcyLjUKCQkJCWM0NS44LDU2LjEsMTQ0LjQsNTQuOCwxNDQuNCw1NC44czEyLjEsMjguOSwzMC42LDU1LjVjMjUsMzMuMSw1MC43LDU4LjksNzUuNyw2MmM2MywwLDE4OC45LTAuMSwxODguOS0wLjFzMTIsMC4xLDI4LjMtMTAuMwoJCQkJYzE0LTguNSwyNi41LTIzLjQsMjYuNS0yMy40czEyLjktMTMuOCwzMC45LTQ1LjNjNS41LTkuNywxMC4xLTE5LjEsMTQuMS0yOGMwLDAsNTUuMi0xMTcuMSw1NS4yLTIzMS4xCgkJCQlDNjMzLjIsMTU3LjksNjI0LjcsMTUxLjgsNjIyLjcsMTQ5Ljh6IE0xMjUuNiwzNTMuOWMtMjUuOS04LjUtMzYuOS0xOC43LTM2LjktMTguN1M2OS42LDMyMS44LDYwLDI5NS40CgkJCQljLTE2LjUtNDQuMi0xLjQtNzEuMi0xLjQtNzEuMnM4LjQtMjIuNSwzOC41LTMwYzEzLjgtMy43LDMxLTMuMSwzMS0zLjFzNy4xLDU5LjQsMTUuNyw5NC4yYzcuMiwyOS4yLDI0LjgsNzcuNywyNC44LDc3LjcKCQkJCVMxNDIuNSwzNTkuOSwxMjUuNiwzNTMuOXogTTQyNS45LDQ2MS41YzAsMC02LjEsMTQuNS0xOS42LDE1LjRjLTUuOCwwLjQtMTAuMy0xLjItMTAuMy0xLjJzLTAuMy0wLjEtNS4zLTIuMWwtMTEyLjktNTUKCQkJCWMwLDAtMTAuOS01LjctMTIuOC0xNS42Yy0yLjItOC4xLDIuNy0xOC4xLDIuNy0xOC4xTDMyMiwyNzNjMCwwLDQuOC05LjcsMTIuMi0xM2MwLjYtMC4zLDIuMy0xLDQuNS0xLjVjOC4xLTIuMSwxOCwyLjgsMTgsMi44CgkJCQlsMTEwLjcsNTMuN2MwLDAsMTIuNiw1LjcsMTUuMywxNi4yYzEuOSw3LjQtMC41LDE0LTEuOCwxNy4yQzQ3NC42LDM2My44LDQyNS45LDQ2MS41LDQyNS45LDQ2MS41eiIvPgoJCQk8cGF0aCBzdHlsZT0iZmlsbDojNjA5OTI2IiBkPSJNMzI2LjgsMzgwLjFjLTguMiwwLjEtMTUuNCw1LjgtMTcuMywxMy44Yy0xLjksOCwyLDE2LjMsOS4xLDIwYzcuNyw0LDE3LjUsMS44LDIyLjctNS40CgkJCQljNS4xLTcuMSw0LjMtMTYuOS0xLjgtMjMuMWwyNC00OS4xYzEuNSwwLjEsMy43LDAuMiw2LjItMC41YzQuMS0wLjksNy4xLTMuNiw3LjEtMy42YzQuMiwxLjgsOC42LDMuOCwxMy4yLDYuMQoJCQkJYzQuOCwyLjQsOS4zLDQuOSwxMy40LDcuM2MwLjksMC41LDEuOCwxLjEsMi44LDEuOWMxLjYsMS4zLDMuNCwzLjEsNC43LDUuNWMxLjksNS41LTEuOSwxNC45LTEuOSwxNC45CgkJCQljLTIuMyw3LjYtMTguNCw0MC42LTE4LjQsNDAuNmMtOC4xLTAuMi0xNS4zLDUtMTcuNywxMi41Yy0yLjYsOC4xLDEuMSwxNy4zLDguOSwyMS4zYzcuOCw0LDE3LjQsMS43LDIyLjUtNS4zCgkJCQljNS02LjgsNC42LTE2LjMtMS4xLTIyLjZjMS45LTMuNywzLjctNy40LDUuNi0xMS4zYzUtMTAuNCwxMy41LTMwLjQsMTMuNS0zMC40YzAuOS0xLjcsNS43LTEwLjMsMi43LTIxLjMKCQkJCWMtMi41LTExLjQtMTIuNi0xNi43LTEyLjYtMTYuN2MtMTIuMi03LjktMjkuMi0xNS4yLTI5LjItMTUuMnMwLTQuMS0xLjEtNy4xYy0xLjEtMy4xLTIuOC01LjEtMy45LTYuM2M0LjctOS43LDkuNC0xOS4zLDE0LjEtMjkKCQkJCWMtNC4xLTItOC4xLTQtMTIuMi02LjFjLTQuOCw5LjgtOS43LDE5LjctMTQuNSwyOS41Yy02LjctMC4xLTEyLjksMy41LTE2LjEsOS40Yy0zLjQsNi4zLTIuNywxNC4xLDEuOSwxOS44CgkJCQlDMzQzLjIsMzQ2LjUsMzM1LDM2My4zLDMyNi44LDM4MC4xeiIvPgoJCTwvZz4KCTwvZz4KPC9nPgo8L3N2Zz4K 44 | mediatype: image/svg+xml 45 | install: 46 | spec: 47 | clusterPermissions: 48 | - rules: 49 | - apiGroups: 50 | - "" 51 | resources: 52 | - secrets 53 | - pods 54 | - pods/exec 55 | - pods/log 56 | verbs: 57 | - create 58 | - delete 59 | - get 60 | - list 61 | - patch 62 | - update 63 | - watch 64 | - apiGroups: 65 | - apps 66 | resources: 67 | - deployments 68 | - daemonsets 69 | - replicasets 70 | - statefulsets 71 | verbs: 72 | - create 73 | - delete 74 | - get 75 | - list 76 | - patch 77 | - update 78 | - watch 79 | - apiGroups: 80 | - pfe.rhpds.com 81 | resources: 82 | - gitea 83 | - gitea/status 84 | - gitea/finalizers 85 | verbs: 86 | - create 87 | - delete 88 | - get 89 | - list 90 | - patch 91 | - update 92 | - watch 93 | - apiGroups: 94 | - "" 95 | resources: 96 | - serviceaccounts 97 | - persistentvolumeclaims 98 | - configmaps 99 | - services 100 | verbs: 101 | - create 102 | - delete 103 | - get 104 | - list 105 | - patch 106 | - update 107 | - watch 108 | - apiGroups: 109 | - route.openshift.io 110 | resources: 111 | - routes 112 | - routes/custom-host 113 | verbs: 114 | - create 115 | - delete 116 | - get 117 | - list 118 | - patch 119 | - update 120 | - watch 121 | - apiGroups: 122 | - authentication.k8s.io 123 | resources: 124 | - tokenreviews 125 | verbs: 126 | - create 127 | - apiGroups: 128 | - authorization.k8s.io 129 | resources: 130 | - subjectaccessreviews 131 | verbs: 132 | - create 133 | serviceAccountName: gitea-operator-controller-manager 134 | deployments: 135 | - label: 136 | app.kubernetes.io/managed-by: kustomize 137 | app.kubernetes.io/name: gitea-operator 138 | control-plane: controller-manager 139 | name: gitea-operator-controller-manager 140 | spec: 141 | replicas: 1 142 | selector: 143 | matchLabels: 144 | control-plane: controller-manager 145 | strategy: {} 146 | template: 147 | metadata: 148 | annotations: 149 | kubectl.kubernetes.io/default-container: manager 150 | labels: 151 | control-plane: controller-manager 152 | spec: 153 | containers: 154 | - args: 155 | - --metrics-require-rbac 156 | - --metrics-secure 157 | - --metrics-bind-address=:8443 158 | - --leader-elect 159 | - --leader-election-id=gitea-operator 160 | - --health-probe-bind-address=:6789 161 | env: 162 | - name: ANSIBLE_GATHERING 163 | value: explicit 164 | image: quay.io/rhpds/gitea-operator:v2.1.0 165 | livenessProbe: 166 | httpGet: 167 | path: /healthz 168 | port: 6789 169 | initialDelaySeconds: 15 170 | periodSeconds: 20 171 | name: manager 172 | readinessProbe: 173 | httpGet: 174 | path: /readyz 175 | port: 6789 176 | initialDelaySeconds: 5 177 | periodSeconds: 10 178 | resources: 179 | limits: 180 | cpu: "1" 181 | memory: 2Gi 182 | requests: 183 | cpu: 200m 184 | memory: 1Gi 185 | securityContext: 186 | allowPrivilegeEscalation: false 187 | capabilities: 188 | drop: 189 | - ALL 190 | securityContext: 191 | runAsNonRoot: true 192 | serviceAccountName: gitea-operator-controller-manager 193 | terminationGracePeriodSeconds: 10 194 | permissions: 195 | - rules: 196 | - apiGroups: 197 | - "" 198 | resources: 199 | - configmaps 200 | verbs: 201 | - get 202 | - list 203 | - watch 204 | - create 205 | - update 206 | - patch 207 | - delete 208 | - apiGroups: 209 | - coordination.k8s.io 210 | resources: 211 | - leases 212 | verbs: 213 | - get 214 | - list 215 | - watch 216 | - create 217 | - update 218 | - patch 219 | - delete 220 | - apiGroups: 221 | - "" 222 | resources: 223 | - events 224 | verbs: 225 | - create 226 | - patch 227 | serviceAccountName: gitea-operator-controller-manager 228 | strategy: deployment 229 | installModes: 230 | - supported: false 231 | type: OwnNamespace 232 | - supported: false 233 | type: SingleNamespace 234 | - supported: false 235 | type: MultiNamespace 236 | - supported: true 237 | type: AllNamespaces 238 | keywords: 239 | - gitea 240 | - repository 241 | links: 242 | - name: Gitea Operator 243 | url: https://gitea-operator.domain 244 | maintainers: 245 | - email: wkulhane@redhat.com 246 | name: Wolfgang Kulhanek 247 | maturity: stable 248 | provider: 249 | name: Red Hat Portfolio Technical Marketing and Platforms 250 | replaces: gitea-operator.v1.1.0 251 | version: 2.1.0 252 | -------------------------------------------------------------------------------- /bundle/manifests/pfe.rhpds.com_gitea.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | creationTimestamp: null 5 | name: gitea.pfe.rhpds.com 6 | spec: 7 | group: pfe.rhpds.com 8 | names: 9 | kind: Gitea 10 | listKind: GiteaList 11 | plural: gitea 12 | singular: gitea 13 | scope: Namespaced 14 | versions: 15 | - name: v1 16 | schema: 17 | openAPIV3Schema: 18 | description: Gitea is the Schema for the giteas API 19 | properties: 20 | apiVersion: 21 | description: 'APIVersion defines the versioned schema of this representation 22 | of an object. Servers should convert recognized schemas to the latest 23 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 24 | type: string 25 | kind: 26 | description: 'Kind is a string value representing the REST resource this 27 | object represents. Servers may infer this from the endpoint the client 28 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | description: Spec defines the desired state of Gitea 34 | properties: 35 | giteaAdminEmail: 36 | description: e-mail address for the Gitea Admin User. Default is 'notset@notset.org' 37 | type: string 38 | giteaAdminPassword: 39 | description: Password for the Gitea admin user. If not specified or 40 | empty a random password will be created with length of giteaAdminPasswordLength 41 | random ASCII characters. Default is '' 42 | type: string 43 | giteaAdminPasswordLength: 44 | description: If a giteaAdminUser is provided but no giteaAdminPassowrd 45 | is provided a random ASCII password with the length specified will 46 | be created. Default is 16 47 | type: integer 48 | giteaAdminPasswordSecretName: 49 | description: Name of a secret containing the Gitea admin user's password 50 | in secret key adminPassword. If this variable is set it takes precedence 51 | over all other ways to specify/generate an admin password. 52 | type: string 53 | giteaAdminUser: 54 | description: User ID for the Admin User to be created. If not specified 55 | no admin user will be created. Note that if giteaDisableRegistration 56 | is set to false and no admin user will be created you will not be 57 | able to create any users for Gitea. Default is '' 58 | type: string 59 | giteaAllowCreateOrganization: 60 | description: Allow users to create organizations in Gitea. Default 61 | is true. 62 | type: boolean 63 | giteaAllowLocalNetworkMigration: 64 | description: 'Allow migration of repositories hosted on local network 65 | IPs as defined by RFC 1918, RFC 1122, RFC 4632 and RFC 4291. Default: 66 | false' 67 | type: boolean 68 | giteaConfigMapName: 69 | description: Name of a config map in the same namespace as the Gitea 70 | custom resource. The config map must contain one file called app.ini 71 | to configure Gitea. If this variable is set then giteaHostname must 72 | also be set. giteaSsl should be set but will default to false. 73 | type: string 74 | giteaCpuLimit: 75 | anyOf: 76 | - type: integer 77 | - type: string 78 | description: CPU limit for Gitea. Default is '500m'. 79 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 80 | x-kubernetes-int-or-string: true 81 | giteaCpuRequest: 82 | anyOf: 83 | - type: integer 84 | - type: string 85 | description: CPU request for Gitea. Default is '200m'. 86 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 87 | x-kubernetes-int-or-string: true 88 | giteaCreateUsers: 89 | description: Create users in Gitea. Only possible if an admin user 90 | is also being created. Default is false 91 | type: boolean 92 | giteaDisableRegistration: 93 | description: Disable user self-registration. If this flag is set an 94 | Admin User should be specified to be created. Otherwise no users 95 | can be created at all. Default is false. 96 | type: boolean 97 | giteaDisableSsh: 98 | description: Disable SSH for Gitea. Default is true. 99 | type: boolean 100 | giteaEnableCaptcha: 101 | description: Display Captcha when users are registering a new account. 102 | No effect if giteaDisableRegistration is set to false. Default is 103 | false. 104 | type: boolean 105 | giteaEnableNotifyMail: 106 | description: Send e-mail notifications to users for various tasks 107 | in Gitea. Requires the mailer to be configured correctly. Default 108 | is false. 109 | type: boolean 110 | giteaGenerateUserFormat: 111 | description: Format for user names to be created. This will be taken 112 | literally if only one user is to be created (e.g. lab-user). If 113 | more than one user is to be created the format needs to include 114 | a '%d' to set the user number. Default is 'user%d' 115 | type: string 116 | giteaHostname: 117 | description: Specify the hostname for the Gitea Route. Default is 118 | ''. Make sure the route is reachable from outside the cluster. 119 | type: string 120 | giteaHttpPort: 121 | description: Port for Gitea to listen on. Default is 3000. 122 | type: integer 123 | giteaImage: 124 | description: Container image for Gitea. Default is 'quay.io/rhpds/gitea'. 125 | type: string 126 | giteaImagePullPolicy: 127 | description: Pull policy for the Gitea container image. Default is 128 | 'IfNotPresent'. 129 | type: string 130 | giteaImageTag: 131 | description: Image tag for the Gitea container image. Default is 'latest'. 132 | type: string 133 | giteaMailerEnabled: 134 | description: Enable e-mail integration for Gitea. If set to true the 135 | other giteaMailer* properties need to be provided. See https://docs.gitea.io/en-us/email-setup/ 136 | for example values. Default is false. 137 | type: boolean 138 | giteaMailerFrom: 139 | description: E-mail integration. FROM e-mail address to be used. Default 140 | is "". 141 | type: string 142 | giteaMailerHeloHostname: 143 | description: Helo Hostname for the e-mail server. Not required for 144 | all e-mail providers. Default is "". 145 | type: string 146 | giteaMailerHost: 147 | description: Hostname of the e-mail server to be used. Default is 148 | "". 149 | type: string 150 | giteaMailerPassword: 151 | description: Password for the User ID on the e-mail server to be used. 152 | May need to be an app-specific password if two-factor authentication 153 | is enabled on the e-mail server. Default is "". 154 | type: string 155 | giteaMailerTls: 156 | description: Use TLS encryption when connecting to the mailer host. 157 | Default is true. 158 | type: boolean 159 | giteaMailerType: 160 | description: Type of e-mail provider to be used. Default is smtp. 161 | type: string 162 | giteaMailerUser: 163 | description: User ID on the e-mail server to use. Frequently the same 164 | as the value for giteaMailerFrom. Default is "". 165 | type: string 166 | giteaMemoryLimit: 167 | description: Memory limit for Gitea. Default is '1Gi'. 168 | type: string 169 | giteaMemoryRequest: 170 | description: Memory request for Gitea. Default is '1Gi'. 171 | type: string 172 | giteaMigrateRepositories: 173 | description: For created users migrate repositories from another location, 174 | e.g. GitHub. Default is false. 175 | type: boolean 176 | giteaPostgresqlDatabaseName: 177 | description: Name of the PostgreSQL database. Only required when PostgreSQL 178 | is not set up by the operator. Default is 'giteadb' 179 | type: string 180 | giteaPostgresqlPassword: 181 | description: PostgreSQL password. Only required when PostgreSQL is 182 | not set up by the operator. Default is 'giteapassword' 183 | type: string 184 | giteaPostgresqlServiceName: 185 | description: Name of the PostgreSQL service. Only required when PostgreSQL 186 | is not set up by the operator. Default is 'postgresql-' followed 187 | by the Gitea resource name. 188 | type: string 189 | giteaPostgresqlUser: 190 | description: Name of the PostgreSQL user. Only required when PostgreSQL 191 | is not set up by the operator. Default is 'giteauser' 192 | type: string 193 | giteaRegisterEmailConfirm: 194 | description: Send e-mail confirmation to users when self-registering. 195 | Users must click a link to validate their e-mail address before 196 | the account gets created. Requires the mailer to be configured correctly. 197 | Default is false. 198 | type: boolean 199 | giteaRepositoriesList: 200 | description: List of repositories to be migrated from another location. 201 | Each repository is an array of repo, name and private. Default is 202 | []. 203 | items: 204 | properties: 205 | name: 206 | description: Name of the migrated repository in Gitea. 207 | type: string 208 | private: 209 | description: Create private repository in Gitea. 210 | type: boolean 211 | repo: 212 | description: Source repository URL to migrate. 213 | type: string 214 | type: object 215 | type: array 216 | giteaServiceName: 217 | description: Name of the Gitea Service to be deployed. Defaults to 218 | the name of the Gitea custom resource. 219 | type: string 220 | giteaSshPort: 221 | description: Port for Gitea to start an SSH server on. Default is 222 | 2022 223 | type: integer 224 | giteaSsl: 225 | description: Create an HTTPS terminated route for Gitea. Default is 226 | 'false' 227 | type: boolean 228 | giteaStartLfsServer: 229 | description: 'Start LFS Server in the Gitea container. Default: false' 230 | type: boolean 231 | giteaStartSshServer: 232 | description: Start SSH Server in the Gitea container. Default is false. 233 | type: boolean 234 | giteaUserEmailDomain: 235 | description: e-mail domain for the created Gitea users. Default is 236 | "example.com" 237 | type: string 238 | giteaUserNumber: 239 | description: Number of users to create in Gitea. If 1 then only one 240 | user will be created with the username from giteaGenerateUserFormat. 241 | If more than one then users will be created according to the format 242 | in giteaGenerateUserFormat. Default is 2 243 | type: integer 244 | giteaUserPassword: 245 | description: Password for all created Gitea users. If not specified 246 | or empty a random password will be created with length of giteaUserPasswordLength 247 | random ASCII characters. Default is "" 248 | type: string 249 | giteaUserPasswordLength: 250 | description: If a giteaCreateUsers is set but no giteaUserPassowrd 251 | is provided a random ASCII password with the length specified will 252 | be created. Default is 16 253 | type: integer 254 | giteaUserPasswordSecretName: 255 | description: Name of a secret containing the Gitea user common password 256 | in secret key userPassword. If this variable is set it takes precedence 257 | over all other ways to specify/generate a user password. 258 | type: string 259 | giteaVolumeSize: 260 | description: Size of the persistent volume claim for Gitea. Default 261 | is '4Gi'. 262 | type: string 263 | giteaVolumeStorageClass: 264 | description: Storage Class to be used for the Gitea persistent volume 265 | claim. Default is empty - which will create a PVC using the currently 266 | available default storage class on the cluster. 267 | type: string 268 | giteaWebhookAllowedHostList: 269 | description: List of hosts that a web hook is allowed to call. See 270 | https://docs.gitea.com/next/administration/config-cheat-sheet#webhook-webhook 271 | for more details. Default is 'external,private'. 272 | type: string 273 | giteaWebhookSkipTlsVerify: 274 | description: Set to 'true' to skip validation of the webhook target 275 | URL certificate. Default is false. 276 | type: boolean 277 | postgresqlCpuLimit: 278 | anyOf: 279 | - type: integer 280 | - type: string 281 | description: CPU limit for the PostgreSQL database. Default is '500m'. 282 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 283 | x-kubernetes-int-or-string: true 284 | postgresqlCpuRequest: 285 | anyOf: 286 | - type: integer 287 | - type: string 288 | description: CPU request for the PostgreSQL database. Default is '200m'. 289 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 290 | x-kubernetes-int-or-string: true 291 | postgresqlDatabaseName: 292 | description: Name of the PostgreSQL Database to be created. Default 293 | is 'giteadb'. 294 | type: string 295 | postgresqlImage: 296 | description: Container image for the PostgreSQL database. Default 297 | is 'registry.redhat.io/rhel8/postgresql-12'. 298 | type: string 299 | postgresqlImagePullPolicy: 300 | description: Pull policy for the PostgreSQL container image. Default 301 | is 'IfNotPresent'. 302 | type: string 303 | postgresqlImageTag: 304 | description: Image tag for the PostgreSQL container image. Default 305 | is 'latest'. 306 | type: string 307 | postgresqlMemoryLimit: 308 | description: Memory limit for the PostgreSQL database. Default is 309 | '512Mi'. 310 | type: string 311 | postgresqlMemoryRequest: 312 | description: Memory request for the PostgreSQL database. Default is 313 | '512Mi'. 314 | type: string 315 | postgresqlPassword: 316 | description: Password to be used for the PostgreSQL database user. 317 | Default is 'giteapassword'. 318 | type: string 319 | postgresqlServiceName: 320 | description: Name of the PostgreSQL database service. Default is 'postgresql-' 321 | followed by the name of the Gitea resource. 322 | type: string 323 | postgresqlSetup: 324 | description: Set up a PostgreSQL database alongside the Gitea instance. 325 | Default is true. If set to false the values for giteaPostgresqlServiceName, 326 | giteaPostgresqlDatabaseName, giteaPostgresqlUser and giteaPostgresqlPassword 327 | need to be specified to connect to an existing PostgreSQL database. 328 | If set to true no values need to be specified for database name, 329 | database service, database user and database service. 330 | type: boolean 331 | postgresqlUser: 332 | description: Username to be created in the PostgreSQL database. Default 333 | is 'giteauser'. 334 | type: string 335 | postgresqlVolumeSize: 336 | description: Size of the persistent volume claim for the PostgreSQL 337 | database. Default is '4Gi'. 338 | type: string 339 | postgresqlVolumeStorageClass: 340 | description: Storage Class to be used for the PostgreSQL persistent 341 | volume claim. Default is empty - which will create a PVC using the 342 | currently available default storage class on the cluster. 343 | type: string 344 | type: object 345 | x-kubernetes-preserve-unknown-fields: true 346 | status: 347 | description: Status defines the observed state of Gitea 348 | type: object 349 | x-kubernetes-preserve-unknown-fields: true 350 | type: object 351 | served: true 352 | storage: true 353 | subresources: 354 | status: {} 355 | status: 356 | acceptedNames: 357 | kind: "" 358 | plural: "" 359 | conditions: null 360 | storedVersions: null 361 | -------------------------------------------------------------------------------- /bundle/metadata/annotations.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | # Core bundle annotations. 3 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1 4 | operators.operatorframework.io.bundle.manifests.v1: manifests/ 5 | operators.operatorframework.io.bundle.metadata.v1: metadata/ 6 | operators.operatorframework.io.bundle.package.v1: gitea-operator 7 | operators.operatorframework.io.bundle.channels.v1: stable 8 | operators.operatorframework.io.bundle.channel.default.v1: stable 9 | operators.operatorframework.io.metrics.builder: operator-sdk-v1.39.2 10 | operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 11 | operators.operatorframework.io.metrics.project_layout: ansible.sdk.operatorframework.io/v1 12 | 13 | # Annotations for testing. 14 | operators.operatorframework.io.test.mediatype.v1: scorecard+v1 15 | operators.operatorframework.io.test.config.v1: tests/scorecard/ 16 | -------------------------------------------------------------------------------- /bundle/tests/scorecard/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: 8 | - entrypoint: 9 | - scorecard-test 10 | - basic-check-spec 11 | image: quay.io/operator-framework/scorecard-test:v1.39.2 12 | labels: 13 | suite: basic 14 | test: basic-check-spec-test 15 | storage: 16 | spec: 17 | mountPath: {} 18 | - entrypoint: 19 | - scorecard-test 20 | - olm-bundle-validation 21 | image: quay.io/operator-framework/scorecard-test:v1.39.2 22 | labels: 23 | suite: olm 24 | test: olm-bundle-validation-test 25 | storage: 26 | spec: 27 | mountPath: {} 28 | - entrypoint: 29 | - scorecard-test 30 | - olm-crds-have-validation 31 | image: quay.io/operator-framework/scorecard-test:v1.39.2 32 | labels: 33 | suite: olm 34 | test: olm-crds-have-validation-test 35 | storage: 36 | spec: 37 | mountPath: {} 38 | - entrypoint: 39 | - scorecard-test 40 | - olm-crds-have-resources 41 | image: quay.io/operator-framework/scorecard-test:v1.39.2 42 | labels: 43 | suite: olm 44 | test: olm-crds-have-resources-test 45 | storage: 46 | spec: 47 | mountPath: {} 48 | - entrypoint: 49 | - scorecard-test 50 | - olm-spec-descriptors 51 | image: quay.io/operator-framework/scorecard-test:v1.39.2 52 | labels: 53 | suite: olm 54 | test: olm-spec-descriptors-test 55 | storage: 56 | spec: 57 | mountPath: {} 58 | - entrypoint: 59 | - scorecard-test 60 | - olm-status-descriptors 61 | image: quay.io/operator-framework/scorecard-test:v1.39.2 62 | labels: 63 | suite: olm 64 | test: olm-status-descriptors-test 65 | storage: 66 | spec: 67 | mountPath: {} 68 | storage: 69 | spec: 70 | mountPath: {} 71 | -------------------------------------------------------------------------------- /catalog_source.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: operators.coreos.com/v1alpha1 4 | kind: CatalogSource 5 | metadata: 6 | name: redhat-rhpds-gitea 7 | namespace: openshift-marketplace 8 | spec: 9 | sourceType: grpc 10 | image: quay.io/rhpds/gitea-catalog:v2.1.0 11 | displayName: Red Hat Demo Platform (Gitea) 12 | publisher: Red Hat Demo Platform 13 | 14 | -------------------------------------------------------------------------------- /config/crd/bases/pfe.rhpds.com_gitea.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: gitea.pfe.rhpds.com 6 | spec: 7 | group: pfe.rhpds.com 8 | names: 9 | kind: Gitea 10 | listKind: GiteaList 11 | plural: gitea 12 | singular: gitea 13 | scope: Namespaced 14 | versions: 15 | - name: v1 16 | schema: 17 | openAPIV3Schema: 18 | description: Gitea is the Schema for the gitea API 19 | properties: 20 | apiVersion: 21 | description: 'APIVersion defines the versioned schema of this representation 22 | of an object. Servers should convert recognized schemas to the latest 23 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 24 | type: string 25 | kind: 26 | description: 'Kind is a string value representing the REST resource this 27 | object represents. Servers may infer this from the endpoint the client 28 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | description: Spec defines the desired state of Gitea 34 | type: object 35 | x-kubernetes-preserve-unknown-fields: true 36 | status: 37 | description: Status defines the observed state of Gitea 38 | type: object 39 | x-kubernetes-preserve-unknown-fields: true 40 | type: object 41 | served: true 42 | storage: true 43 | subresources: 44 | status: {} 45 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/pfe.rhpds.com_gitea.yaml 6 | # +kubebuilder:scaffold:crdkustomizeresource 7 | 8 | patches: 9 | - path: ./patches/crd_openapi.yaml 10 | target: 11 | group: apiextensions.k8s.io 12 | version: v1 13 | kind: CustomResourceDefinition 14 | name: gitea.pfe.rhpds.com 15 | -------------------------------------------------------------------------------- /config/crd/patches/crd_openapi.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: gitea.pfe.rhpds.com 6 | spec: 7 | versions: 8 | - name: v1 9 | served: true 10 | storage: true 11 | subresources: 12 | status: {} 13 | schema: 14 | openAPIV3Schema: 15 | description: Gitea is the Schema for the giteas API 16 | type: object 17 | properties: 18 | apiVersion: 19 | description: 'APIVersion defines the versioned schema of this representation 20 | of an object. Servers should convert recognized schemas to the latest 21 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 22 | type: string 23 | kind: 24 | description: 'Kind is a string value representing the REST resource this 25 | object represents. Servers may infer this from the endpoint the client 26 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 27 | type: string 28 | metadata: 29 | type: object 30 | status: 31 | description: Status defines the observed state of Gitea 32 | type: object 33 | x-kubernetes-preserve-unknown-fields: true 34 | spec: 35 | description: Spec defines the desired state of Gitea 36 | type: object 37 | x-kubernetes-preserve-unknown-fields: true 38 | properties: 39 | 40 | postgresqlSetup: 41 | description: 'Set up a PostgreSQL database alongside the Gitea instance. Default is true. 42 | If set to false the values for giteaPostgresqlServiceName, giteaPostgresqlDatabaseName, giteaPostgresqlUser and giteaPostgresqlPassword need to be specified to connect to an existing PostgreSQL database. 43 | If set to true no values need to be specified for database name, database service, database user and database service.' 44 | type: boolean 45 | postgresqlServiceName: 46 | description: Name of the PostgreSQL database service. Default is 'postgresql-' followed by the name of the Gitea resource. 47 | type: string 48 | postgresqlDatabaseName: 49 | description: Name of the PostgreSQL Database to be created. Default is 'giteadb'. 50 | type: string 51 | postgresqlUser: 52 | description: Username to be created in the PostgreSQL database. Default is 'giteauser'. 53 | type: string 54 | postgresqlPassword: 55 | description: Password to be used for the PostgreSQL database user. Default is 'giteapassword'. 56 | type: string 57 | postgresqlVolumeSize: 58 | description: Size of the persistent volume claim for the PostgreSQL database. Default is '4Gi'. 59 | type: string 60 | postgresqlVolumeStorageClass: 61 | description: Storage Class to be used for the PostgreSQL persistent volume claim. Default is empty - which will create a PVC using the currently available default storage class on the cluster. 62 | type: string 63 | postgresqlImage: 64 | description: Container image for the PostgreSQL database. Default is 'registry.redhat.io/rhel8/postgresql-12'. 65 | type: string 66 | postgresqlImageTag: 67 | description: Image tag for the PostgreSQL container image. Default is 'latest'. 68 | type: string 69 | postgresqlImagePullPolicy: 70 | description: Pull policy for the PostgreSQL container image. Default is 'IfNotPresent'. 71 | type: string 72 | postgresqlMemoryRequest: 73 | description: Memory request for the PostgreSQL database. Default is '512Mi'. 74 | type: string 75 | postgresqlMemoryLimit: 76 | description: Memory limit for the PostgreSQL database. Default is '512Mi'. 77 | type: string 78 | postgresqlCpuRequest: 79 | description: CPU request for the PostgreSQL database. Default is '200m'. 80 | anyOf: 81 | - type: integer 82 | - type: string 83 | pattern: "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$" 84 | x-kubernetes-int-or-string: true 85 | postgresqlCpuLimit: 86 | description: CPU limit for the PostgreSQL database. Default is '500m'. 87 | anyOf: 88 | - type: integer 89 | - type: string 90 | pattern: "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$" 91 | x-kubernetes-int-or-string: true 92 | 93 | giteaServiceName: 94 | description: Name of the Gitea Service to be deployed. Defaults to the name of the Gitea custom resource. 95 | type: string 96 | giteaSsl: 97 | description: Create an HTTPS terminated route for Gitea. Default is 'false' 98 | type: boolean 99 | giteaHostname: 100 | description: Specify the hostname for the Gitea Route. Default is ''. Make sure the route is reachable from outside the cluster. 101 | type: string 102 | giteaVolumeSize: 103 | description: Size of the persistent volume claim for Gitea. Default is '4Gi'. 104 | type: string 105 | giteaVolumeStorageClass: 106 | description: Storage Class to be used for the Gitea persistent volume claim. Default is empty - which will create a PVC using the currently available default storage class on the cluster. 107 | type: string 108 | giteaImage: 109 | description: Container image for Gitea. Default is 'quay.io/rhpds/gitea'. 110 | type: string 111 | giteaImageTag: 112 | description: Image tag for the Gitea container image. Default is 'latest'. 113 | type: string 114 | giteaImagePullPolicy: 115 | description: Pull policy for the Gitea container image. Default is 'IfNotPresent'. 116 | type: string 117 | giteaMemoryRequest: 118 | description: Memory request for Gitea. Default is '1Gi'. 119 | type: string 120 | giteaMemoryLimit: 121 | description: Memory limit for Gitea. Default is '1Gi'. 122 | type: string 123 | giteaCpuRequest: 124 | description: CPU request for Gitea. Default is '200m'. 125 | anyOf: 126 | - type: integer 127 | - type: string 128 | pattern: "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$" 129 | x-kubernetes-int-or-string: true 130 | giteaCpuLimit: 131 | description: CPU limit for Gitea. Default is '500m'. 132 | anyOf: 133 | - type: integer 134 | - type: string 135 | pattern: "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$" 136 | x-kubernetes-int-or-string: true 137 | 138 | giteaPostgresqlServiceName: 139 | description: Name of the PostgreSQL service. Only required when PostgreSQL is not set up by the operator. Default is 'postgresql-' followed by the Gitea resource name. 140 | type: string 141 | giteaPostgresqlDatabaseName: 142 | description: Name of the PostgreSQL database. Only required when PostgreSQL is not set up by the operator. Default is 'giteadb' 143 | type: string 144 | giteaPostgresqlUser: 145 | description: Name of the PostgreSQL user. Only required when PostgreSQL is not set up by the operator. Default is 'giteauser' 146 | type: string 147 | giteaPostgresqlPassword: 148 | description: PostgreSQL password. Only required when PostgreSQL is not set up by the operator. Default is 'giteapassword' 149 | type: string 150 | 151 | giteaConfigMapName: 152 | description: Name of a config map in the same namespace as the Gitea custom resource. The config map must contain one file called app.ini to configure Gitea. If this variable is set then giteaHostname must also be set. giteaSsl should be set but will default to false. 153 | type: string 154 | 155 | giteaAdminUser: 156 | description: User ID for the Admin User to be created. If not specified no admin user will be created. Note that if giteaDisableRegistration is set to false and no admin user will be created you will not be able to create any users for Gitea. Default is '' 157 | type: string 158 | giteaAdminPassword: 159 | description: Password for the Gitea admin user. If not specified or empty a random password will be created with length of giteaAdminPasswordLength random ASCII characters. Default is '' 160 | type: string 161 | giteaAdminPasswordLength: 162 | description: If a giteaAdminUser is provided but no giteaAdminPassowrd is provided a random ASCII password with the length specified will be created. Default is 16 163 | type: integer 164 | giteaAdminPasswordSecretName: 165 | description: Name of a secret containing the Gitea admin user's password in secret key adminPassword. If this variable is set it takes precedence over all other ways to specify/generate an admin password. 166 | type: string 167 | giteaAdminEmail: 168 | description: e-mail address for the Gitea Admin User. Default is 'notset@notset.org' 169 | type: string 170 | 171 | giteaCreateUsers: 172 | description: Create users in Gitea. Only possible if an admin user is also being created. Default is false 173 | type: boolean 174 | giteaUserNumber: 175 | description: Number of users to create in Gitea. If 1 then only one user will be created with the username from giteaGenerateUserFormat. If more than one then users will be created according to the format in giteaGenerateUserFormat. Default is 2 176 | type: integer 177 | giteaGenerateUserFormat: 178 | description: Format for user names to be created. This will be taken literally if only one user is to be created (e.g. lab-user). If more than one user is to be created the format needs to include a '%d' to set the user number. Default is 'user%d' 179 | type: string 180 | giteaUserPassword: 181 | description: Password for all created Gitea users. If not specified or empty a random password will be created with length of giteaUserPasswordLength random ASCII characters. Default is "" 182 | type: string 183 | giteaUserPasswordLength: 184 | description: If a giteaCreateUsers is set but no giteaUserPassowrd is provided a random ASCII password with the length specified will be created. Default is 16 185 | type: integer 186 | giteaUserEmailDomain: 187 | description: e-mail domain for the created Gitea users. Default is "example.com" 188 | type: string 189 | giteaUserPasswordSecretName: 190 | description: Name of a secret containing the Gitea user common password in secret key userPassword. If this variable is set it takes precedence over all other ways to specify/generate a user password. 191 | type: string 192 | 193 | giteaMigrateRepositories: 194 | description: For created users migrate repositories from another location, e.g. GitHub. Default is false. 195 | type: boolean 196 | giteaRepositoriesList: 197 | description: List of repositories to be migrated from another location. Each repository is an array of repo, name and private. Default is []. 198 | type: array 199 | items: 200 | type: object 201 | properties: 202 | repo: 203 | description: Source repository URL to migrate. 204 | type: string 205 | name: 206 | description: Name of the migrated repository in Gitea. 207 | type: string 208 | private: 209 | description: Create private repository in Gitea. 210 | type: boolean 211 | 212 | giteaHttpPort: 213 | description: Port for Gitea to listen on. Default is 3000. 214 | type: integer 215 | giteaSshPort: 216 | description: Port for Gitea to start an SSH server on. Default is 2022 217 | type: integer 218 | giteaDisableSsh: 219 | description: Disable SSH for Gitea. Default is true. 220 | type: boolean 221 | giteaStartSshServer: 222 | description: Start SSH Server in the Gitea container. Default is false. 223 | type: boolean 224 | giteaStartLfsServer: 225 | description: 'Start LFS Server in the Gitea container. Default: false' 226 | type: boolean 227 | giteaDisableRegistration: 228 | description: Disable user self-registration. If this flag is set an Admin User should be specified to be created. Otherwise no users can be created at all. Default is false. 229 | type: boolean 230 | giteaEnableCaptcha: 231 | description: Display Captcha when users are registering a new account. No effect if giteaDisableRegistration is set to false. Default is false. 232 | type: boolean 233 | giteaAllowCreateOrganization: 234 | description: Allow users to create organizations in Gitea. Default is true. 235 | type: boolean 236 | giteaAllowLocalNetworkMigration: 237 | description: 'Allow migration of repositories hosted on local network IPs as defined by RFC 1918, RFC 1122, RFC 4632 and RFC 4291. Default: false' 238 | type: boolean 239 | 240 | giteaWebhookAllowedHostList: 241 | description: List of hosts that a web hook is allowed to call. See https://docs.gitea.com/next/administration/config-cheat-sheet#webhook-webhook for more details. Default is 'external,private'. 242 | type: string 243 | giteaWebhookSkipTlsVerify: 244 | description: Set to 'true' to skip validation of the webhook target URL certificate. Default is false. 245 | type: boolean 246 | 247 | giteaMailerEnabled: 248 | description: Enable e-mail integration for Gitea. If set to true the other giteaMailer* properties need to be provided. See https://docs.gitea.io/en-us/email-setup/ for example values. Default is false. 249 | type: boolean 250 | giteaMailerFrom: 251 | description: E-mail integration. FROM e-mail address to be used. Default is "". 252 | type: string 253 | giteaMailerType: 254 | description: Type of e-mail provider to be used. Default is smtp. 255 | type: string 256 | giteaMailerHost: 257 | description: Hostname of the e-mail server to be used. Default is "". 258 | type: string 259 | giteaMailerTls: 260 | description: Use TLS encryption when connecting to the mailer host. Default is true. 261 | type: boolean 262 | giteaMailerUser: 263 | description: User ID on the e-mail server to use. Frequently the same as the value for giteaMailerFrom. Default is "". 264 | type: string 265 | giteaMailerPassword: 266 | description: Password for the User ID on the e-mail server to be used. May need to be an app-specific password if two-factor authentication is enabled on the e-mail server. Default is "". 267 | type: string 268 | giteaMailerHeloHostname: 269 | description: Helo Hostname for the e-mail server. Not required for all e-mail providers. Default is "". 270 | type: string 271 | 272 | giteaRegisterEmailConfirm: 273 | description: Send e-mail confirmation to users when self-registering. Users must click a link to validate their e-mail address before the account gets created. Requires the mailer to be configured correctly. Default is false. 274 | type: boolean 275 | giteaEnableNotifyMail: 276 | description: Send e-mail notifications to users for various tasks in Gitea. Requires the mailer to be configured correctly. Default is false. 277 | type: boolean 278 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: gitea-operator 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: gitea-operator- 10 | 11 | # Labels to add to all resources and selectors. 12 | #labels: 13 | #- includeSelectors: true 14 | # pairs: 15 | # someName: someValue 16 | 17 | resources: 18 | - ../crd 19 | - ../rbac 20 | - ../manager 21 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 22 | #- ../prometheus 23 | # [METRICS] Expose the controller manager metrics service. 24 | - metrics_service.yaml 25 | # [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. 26 | # Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. 27 | # Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will 28 | # be able to communicate with the Webhook Server. 29 | #- ../network-policy 30 | 31 | # Uncomment the patches line if you enable Metrics, and/or are using webhooks and cert-manager 32 | patches: 33 | # [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. 34 | # More info: https://book.kubebuilder.io/reference/metrics 35 | - path: manager_metrics_patch.yaml 36 | target: 37 | kind: Deployment 38 | -------------------------------------------------------------------------------- /config/default/manager_metrics_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch adds the args to allow exposing the metrics endpoint using HTTPS 2 | - op: add 3 | path: /spec/template/spec/containers/0/args/0 4 | value: --metrics-bind-address=:8443 5 | # This patch adds the args to allow securing the metrics endpoint 6 | - op: add 7 | path: /spec/template/spec/containers/0/args/0 8 | value: --metrics-secure 9 | # This patch adds the args to allow RBAC-based authn/authz the metrics endpoint 10 | - op: add 11 | path: /spec/template/spec/containers/0/args/0 12 | value: --metrics-require-rbac 13 | -------------------------------------------------------------------------------- /config/default/metrics_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | app.kubernetes.io/name: gitea-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: controller-manager-metrics-service 9 | namespace: system 10 | spec: 11 | ports: 12 | - name: https 13 | port: 8443 14 | protocol: TCP 15 | targetPort: 8443 16 | selector: 17 | control-plane: controller-manager 18 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | images: 6 | - name: controller 7 | newName: quay.io/rhpds/gitea-operator 8 | newTag: v2.1.0 9 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | app.kubernetes.io/name: gitea-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: system 9 | --- 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | metadata: 13 | name: controller-manager 14 | namespace: system 15 | labels: 16 | control-plane: controller-manager 17 | app.kubernetes.io/name: gitea-operator 18 | app.kubernetes.io/managed-by: kustomize 19 | spec: 20 | selector: 21 | matchLabels: 22 | control-plane: controller-manager 23 | replicas: 1 24 | template: 25 | metadata: 26 | annotations: 27 | kubectl.kubernetes.io/default-container: manager 28 | labels: 29 | control-plane: controller-manager 30 | spec: 31 | # TODO(user): Uncomment the following code to configure the nodeAffinity expression 32 | # according to the platforms which are supported by your solution. 33 | # It is considered best practice to support multiple architectures. You can 34 | # build your manager image using the makefile target docker-buildx. 35 | # affinity: 36 | # nodeAffinity: 37 | # requiredDuringSchedulingIgnoredDuringExecution: 38 | # nodeSelectorTerms: 39 | # - matchExpressions: 40 | # - key: kubernetes.io/arch 41 | # operator: In 42 | # values: 43 | # - amd64 44 | # - arm64 45 | # - ppc64le 46 | # - s390x 47 | # - key: kubernetes.io/os 48 | # operator: In 49 | # values: 50 | # - linux 51 | securityContext: 52 | runAsNonRoot: true 53 | # TODO(user): For common cases that do not require escalating privileges 54 | # it is recommended to ensure that all your Pods/Containers are restrictive. 55 | # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted 56 | # Please uncomment the following code if your project does NOT have to work on old Kubernetes 57 | # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). 58 | # seccompProfile: 59 | # type: RuntimeDefault 60 | containers: 61 | - args: 62 | - --leader-elect 63 | - --leader-election-id=gitea-operator 64 | - --health-probe-bind-address=:6789 65 | image: controller:latest 66 | name: manager 67 | env: 68 | - name: ANSIBLE_GATHERING 69 | value: explicit 70 | securityContext: 71 | allowPrivilegeEscalation: false 72 | capabilities: 73 | drop: 74 | - "ALL" 75 | livenessProbe: 76 | httpGet: 77 | path: /healthz 78 | port: 6789 79 | initialDelaySeconds: 15 80 | periodSeconds: 20 81 | readinessProbe: 82 | httpGet: 83 | path: /readyz 84 | port: 6789 85 | initialDelaySeconds: 5 86 | periodSeconds: 10 87 | # TODO(user): Configure the resources accordingly based on the project requirements. 88 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 89 | resources: 90 | limits: 91 | cpu: 1 92 | memory: 2Gi 93 | requests: 94 | cpu: 200m 95 | memory: 1Gi 96 | serviceAccountName: controller-manager 97 | terminationGracePeriodSeconds: 10 98 | -------------------------------------------------------------------------------- /config/manifests/bases/gitea-operator.clusterserviceversion.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: ClusterServiceVersion 3 | metadata: 4 | annotations: 5 | alm-examples: '[]' 6 | capabilities: Basic Install 7 | name: gitea-operator.v0.0.0 8 | namespace: placeholder 9 | spec: 10 | apiservicedefinitions: {} 11 | customresourcedefinitions: {} 12 | description: Gitea Operator - provided by Red Hat Demo Platform, see https://github.com/rhpds/gitea-operator 13 | for documentation. 14 | displayName: Gitea Operator 15 | icon: 16 | - base64data: "" 17 | mediatype: "" 18 | install: 19 | spec: 20 | deployments: null 21 | strategy: "" 22 | installModes: 23 | - supported: false 24 | type: OwnNamespace 25 | - supported: false 26 | type: SingleNamespace 27 | - supported: false 28 | type: MultiNamespace 29 | - supported: true 30 | type: AllNamespaces 31 | keywords: 32 | - gitea 33 | - repository 34 | links: 35 | - name: Gitea Operator 36 | url: https://gitea-operator.domain 37 | maintainers: 38 | - email: wkulhane@redhat.com 39 | name: Wolfgang Kulhanek 40 | maturity: alpha 41 | provider: 42 | name: Red Hat Portfolio Technical Marketing and Platforms 43 | version: 0.0.0 44 | -------------------------------------------------------------------------------- /config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - bases/gitea-operator.clusterserviceversion.yaml 5 | - ../default 6 | - ../samples 7 | - ../scorecard 8 | 9 | patches: 10 | - path: ./patches/csv.yaml 11 | target: 12 | group: operators.coreos.com 13 | version: v1alpha1 14 | kind: ClusterServiceVersion 15 | name: gitea-operator.v0.0.0 16 | namespace: placeholder 17 | 18 | -------------------------------------------------------------------------------- /config/manifests/patches/csv.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: operators.coreos.com/v1alpha1 4 | kind: ClusterServiceVersion 5 | metadata: 6 | name: gitea-operator.v0.0.0 7 | namespace: placeholder 8 | spec: 9 | replaces: gitea-operator.v1.1.0 10 | maturity: stable 11 | icon: 12 | - base64data: PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJtYWluX291dGxpbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIKCSB5PSIwcHgiIHZpZXdCb3g9IjAgMCA2NDAgNjQwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA2NDAgNjQwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPHBhdGggaWQ9InRlYWJhZyIgc3R5bGU9ImZpbGw6I0ZGRkZGRiIgZD0iTTM5NS45LDQ4NC4ybC0xMjYuOS02MWMtMTIuNS02LTE3LjktMjEuMi0xMS44LTMzLjhsNjEtMTI2LjljNi0xMi41LDIxLjItMTcuOSwzMy44LTExLjgKCQljMTcuMiw4LjMsMjcuMSwxMywyNy4xLDEzbC0wLjEtMTA5LjJsMTYuNy0wLjFsMC4xLDExNy4xYzAsMCw1Ny40LDI0LjIsODMuMSw0MC4xYzMuNywyLjMsMTAuMiw2LjgsMTIuOSwxNC40CgkJYzIuMSw2LjEsMiwxMy4xLTEsMTkuM2wtNjEsMTI2LjlDNDIzLjYsNDg0LjksNDA4LjQsNDkwLjMsMzk1LjksNDg0LjJ6Ii8+Cgk8Zz4KCQk8Zz4KCQkJPHBhdGggc3R5bGU9ImZpbGw6IzYwOTkyNiIgZD0iTTYyMi43LDE0OS44Yy00LjEtNC4xLTkuNi00LTkuNi00cy0xMTcuMiw2LjYtMTc3LjksOGMtMTMuMywwLjMtMjYuNSwwLjYtMzkuNiwwLjdjMCwzOS4xLDAsNzguMiwwLDExNy4yCgkJCQljLTUuNS0yLjYtMTEuMS01LjMtMTYuNi03LjljMC0zNi40LTAuMS0xMDkuMi0wLjEtMTA5LjJjLTI5LDAuNC04OS4yLTIuMi04OS4yLTIuMnMtMTQxLjQtNy4xLTE1Ni44LTguNQoJCQkJYy05LjgtMC42LTIyLjUtMi4xLTM5LDEuNWMtOC43LDEuOC0zMy41LDcuNC01My44LDI2LjlDLTQuOSwyMTIuNCw2LjYsMjc2LjIsOCwyODUuOGMxLjcsMTEuNyw2LjksNDQuMiwzMS43LDcyLjUKCQkJCWM0NS44LDU2LjEsMTQ0LjQsNTQuOCwxNDQuNCw1NC44czEyLjEsMjguOSwzMC42LDU1LjVjMjUsMzMuMSw1MC43LDU4LjksNzUuNyw2MmM2MywwLDE4OC45LTAuMSwxODguOS0wLjFzMTIsMC4xLDI4LjMtMTAuMwoJCQkJYzE0LTguNSwyNi41LTIzLjQsMjYuNS0yMy40czEyLjktMTMuOCwzMC45LTQ1LjNjNS41LTkuNywxMC4xLTE5LjEsMTQuMS0yOGMwLDAsNTUuMi0xMTcuMSw1NS4yLTIzMS4xCgkJCQlDNjMzLjIsMTU3LjksNjI0LjcsMTUxLjgsNjIyLjcsMTQ5Ljh6IE0xMjUuNiwzNTMuOWMtMjUuOS04LjUtMzYuOS0xOC43LTM2LjktMTguN1M2OS42LDMyMS44LDYwLDI5NS40CgkJCQljLTE2LjUtNDQuMi0xLjQtNzEuMi0xLjQtNzEuMnM4LjQtMjIuNSwzOC41LTMwYzEzLjgtMy43LDMxLTMuMSwzMS0zLjFzNy4xLDU5LjQsMTUuNyw5NC4yYzcuMiwyOS4yLDI0LjgsNzcuNywyNC44LDc3LjcKCQkJCVMxNDIuNSwzNTkuOSwxMjUuNiwzNTMuOXogTTQyNS45LDQ2MS41YzAsMC02LjEsMTQuNS0xOS42LDE1LjRjLTUuOCwwLjQtMTAuMy0xLjItMTAuMy0xLjJzLTAuMy0wLjEtNS4zLTIuMWwtMTEyLjktNTUKCQkJCWMwLDAtMTAuOS01LjctMTIuOC0xNS42Yy0yLjItOC4xLDIuNy0xOC4xLDIuNy0xOC4xTDMyMiwyNzNjMCwwLDQuOC05LjcsMTIuMi0xM2MwLjYtMC4zLDIuMy0xLDQuNS0xLjVjOC4xLTIuMSwxOCwyLjgsMTgsMi44CgkJCQlsMTEwLjcsNTMuN2MwLDAsMTIuNiw1LjcsMTUuMywxNi4yYzEuOSw3LjQtMC41LDE0LTEuOCwxNy4yQzQ3NC42LDM2My44LDQyNS45LDQ2MS41LDQyNS45LDQ2MS41eiIvPgoJCQk8cGF0aCBzdHlsZT0iZmlsbDojNjA5OTI2IiBkPSJNMzI2LjgsMzgwLjFjLTguMiwwLjEtMTUuNCw1LjgtMTcuMywxMy44Yy0xLjksOCwyLDE2LjMsOS4xLDIwYzcuNyw0LDE3LjUsMS44LDIyLjctNS40CgkJCQljNS4xLTcuMSw0LjMtMTYuOS0xLjgtMjMuMWwyNC00OS4xYzEuNSwwLjEsMy43LDAuMiw2LjItMC41YzQuMS0wLjksNy4xLTMuNiw3LjEtMy42YzQuMiwxLjgsOC42LDMuOCwxMy4yLDYuMQoJCQkJYzQuOCwyLjQsOS4zLDQuOSwxMy40LDcuM2MwLjksMC41LDEuOCwxLjEsMi44LDEuOWMxLjYsMS4zLDMuNCwzLjEsNC43LDUuNWMxLjksNS41LTEuOSwxNC45LTEuOSwxNC45CgkJCQljLTIuMyw3LjYtMTguNCw0MC42LTE4LjQsNDAuNmMtOC4xLTAuMi0xNS4zLDUtMTcuNywxMi41Yy0yLjYsOC4xLDEuMSwxNy4zLDguOSwyMS4zYzcuOCw0LDE3LjQsMS43LDIyLjUtNS4zCgkJCQljNS02LjgsNC42LTE2LjMtMS4xLTIyLjZjMS45LTMuNywzLjctNy40LDUuNi0xMS4zYzUtMTAuNCwxMy41LTMwLjQsMTMuNS0zMC40YzAuOS0xLjcsNS43LTEwLjMsMi43LTIxLjMKCQkJCWMtMi41LTExLjQtMTIuNi0xNi43LTEyLjYtMTYuN2MtMTIuMi03LjktMjkuMi0xNS4yLTI5LjItMTUuMnMwLTQuMS0xLjEtNy4xYy0xLjEtMy4xLTIuOC01LjEtMy45LTYuM2M0LjctOS43LDkuNC0xOS4zLDE0LjEtMjkKCQkJCWMtNC4xLTItOC4xLTQtMTIuMi02LjFjLTQuOCw5LjgtOS43LDE5LjctMTQuNSwyOS41Yy02LjctMC4xLTEyLjksMy41LTE2LjEsOS40Yy0zLjQsNi4zLTIuNywxNC4xLDEuOSwxOS44CgkJCQlDMzQzLjIsMzQ2LjUsMzM1LDM2My4zLDMyNi44LDM4MC4xeiIvPgoJCTwvZz4KCTwvZz4KPC9nPgo8L3N2Zz4K 13 | mediatype: image/svg+xml 14 | 15 | -------------------------------------------------------------------------------- /config/network-policy/allow-metrics-traffic.yaml: -------------------------------------------------------------------------------- 1 | # This NetworkPolicy allows ingress traffic 2 | # with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those 3 | # namespaces are able to gathering data from the metrics endpoint. 4 | apiVersion: networking.k8s.io/v1 5 | kind: NetworkPolicy 6 | metadata: 7 | labels: 8 | app.kubernetes.io/name: gitea-operator 9 | app.kubernetes.io/managed-by: kustomize 10 | name: allow-metrics-traffic 11 | namespace: system 12 | spec: 13 | podSelector: 14 | matchLabels: 15 | control-plane: controller-manager 16 | policyTypes: 17 | - Ingress 18 | ingress: 19 | # This allows ingress traffic from any namespace with the label metrics: enabled 20 | - from: 21 | - namespaceSelector: 22 | matchLabels: 23 | metrics: enabled # Only from namespaces with this label 24 | ports: 25 | - port: 8443 26 | protocol: TCP 27 | -------------------------------------------------------------------------------- /config/network-policy/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - allow-metrics-traffic.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | # Prometheus Monitor Service (Metrics) 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | labels: 6 | control-plane: controller-manager 7 | app.kubernetes.io/name: gitea-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | name: controller-manager-metrics-monitor 10 | namespace: system 11 | spec: 12 | endpoints: 13 | - path: /metrics 14 | port: https # Ensure this is the name of the port that exposes HTTPS metrics 15 | scheme: https 16 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 17 | tlsConfig: 18 | # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables 19 | # certificate verification. This poses a significant security risk by making the system vulnerable to 20 | # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between 21 | # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, 22 | # compromising the integrity and confidentiality of the information. 23 | # Please use the following options for secure configurations: 24 | # caFile: /etc/metrics-certs/ca.crt 25 | # certFile: /etc/metrics-certs/tls.crt 26 | # keyFile: /etc/metrics-certs/tls.key 27 | insecureSkipVerify: true 28 | selector: 29 | matchLabels: 30 | control-plane: controller-manager 31 | -------------------------------------------------------------------------------- /config/rbac/gitea_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit gitea. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: gitea-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: gitea-editor-role 9 | rules: 10 | - apiGroups: 11 | - pfe.rhpds.com 12 | resources: 13 | - gitea 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - pfe.rhpds.com 24 | resources: 25 | - gitea/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/gitea_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view gitea. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: gitea-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: gitea-viewer-role 9 | rules: 10 | - apiGroups: 11 | - pfe.rhpds.com 12 | resources: 13 | - gitea 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - pfe.rhpds.com 20 | resources: 21 | - gitea/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # The following RBAC configurations are used to protect 13 | # the metrics endpoint with authn/authz. These configurations 14 | # ensure that only authorized users and service accounts 15 | # can access the metrics endpoint. Comment the following 16 | # permissions if you want to disable this protection. 17 | # More info: https://book.kubebuilder.io/reference/metrics.html 18 | - metrics_auth_role.yaml 19 | - metrics_auth_role_binding.yaml 20 | - metrics_reader_role.yaml 21 | # For each CRD, "Editor" and "Viewer" roles are scaffolded by 22 | # default, aiding admins in cluster management. Those roles are 23 | # not used by the Project itself. You can comment the following lines 24 | # if you do not want those helpers be installed with your Project. 25 | - gitea_editor_role.yaml 26 | - gitea_viewer_role.yaml 27 | 28 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: gitea-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: leader-election-role 9 | rules: 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - configmaps 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - create 19 | - update 20 | - patch 21 | - delete 22 | - apiGroups: 23 | - coordination.k8s.io 24 | resources: 25 | - leases 26 | verbs: 27 | - get 28 | - list 29 | - watch 30 | - create 31 | - update 32 | - patch 33 | - delete 34 | - apiGroups: 35 | - "" 36 | resources: 37 | - events 38 | verbs: 39 | - create 40 | - patch 41 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: gitea-operator 6 | app.kubernetes.io/managed-by: kustomize 7 | name: leader-election-rolebinding 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: Role 11 | name: leader-election-role 12 | subjects: 13 | - kind: ServiceAccount 14 | name: controller-manager 15 | namespace: system 16 | -------------------------------------------------------------------------------- /config/rbac/metrics_auth_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-auth-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/rbac/metrics_auth_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: metrics-auth-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: metrics-auth-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/metrics_reader_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | ## 8 | ## Base operator rules 9 | ## 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - secrets 14 | - pods 15 | - pods/exec 16 | - pods/log 17 | verbs: 18 | - create 19 | - delete 20 | - get 21 | - list 22 | - patch 23 | - update 24 | - watch 25 | - apiGroups: 26 | - apps 27 | resources: 28 | - deployments 29 | - daemonsets 30 | - replicasets 31 | - statefulsets 32 | verbs: 33 | - create 34 | - delete 35 | - get 36 | - list 37 | - patch 38 | - update 39 | - watch 40 | ## 41 | ## Rules for pfe.rhpds.com/v1, Kind: Gitea 42 | ## 43 | - apiGroups: 44 | - pfe.rhpds.com 45 | resources: 46 | - gitea 47 | - gitea/status 48 | - gitea/finalizers 49 | verbs: 50 | - create 51 | - delete 52 | - get 53 | - list 54 | - patch 55 | - update 56 | - watch 57 | # +kubebuilder:scaffold:rules 58 | - apiGroups: 59 | - "" 60 | resources: 61 | - serviceaccounts 62 | - persistentvolumeclaims 63 | - configmaps 64 | - services 65 | verbs: 66 | - create 67 | - delete 68 | - get 69 | - list 70 | - patch 71 | - update 72 | - watch 73 | - apiGroups: 74 | - route.openshift.io 75 | resources: 76 | - routes 77 | - routes/custom-host 78 | verbs: 79 | - create 80 | - delete 81 | - get 82 | - list 83 | - patch 84 | - update 85 | - watch 86 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: gitea-operator 6 | app.kubernetes.io/managed-by: kustomize 7 | name: manager-rolebinding 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: ClusterRole 11 | name: manager-role 12 | subjects: 13 | - kind: ServiceAccount 14 | name: controller-manager 15 | namespace: system 16 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: gitea-operator 6 | app.kubernetes.io/managed-by: kustomize 7 | name: controller-manager 8 | namespace: system 9 | -------------------------------------------------------------------------------- /config/samples/gitea-server.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: pfe.rhpds.com/v1 3 | kind: Gitea 4 | metadata: 5 | name: repository 6 | labels: 7 | app.kubernetes.io/name: gitea-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | spec: 10 | giteaImageTag: 1.23.7 11 | giteaImagePullPolicy: Always 12 | postgresqlVolumeSize: 4Gi 13 | giteaVolumeSize: 4Gi 14 | giteaSsl: True 15 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples of your project ## 2 | resources: 3 | - gitea-server.yaml 4 | # +kubebuilder:scaffold:manifestskustomizesamples 5 | -------------------------------------------------------------------------------- /config/samples/pfe_v1_gitea.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: pfe.rhpds.com/v1 4 | kind: Gitea 5 | metadata: 6 | name: repository 7 | spec: 8 | postgresqlVolumeSize: 4Gi 9 | giteaVolumeSize: 4Gi 10 | giteaSsl: True 11 | 12 | -------------------------------------------------------------------------------- /config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: [] 8 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | patches: 6 | - path: patches/basic.config.yaml 7 | target: 8 | group: scorecard.operatorframework.io 9 | kind: Configuration 10 | name: config 11 | version: v1alpha3 12 | - path: patches/olm.config.yaml 13 | target: 14 | group: scorecard.operatorframework.io 15 | kind: Configuration 16 | name: config 17 | version: v1alpha3 18 | # +kubebuilder:scaffold:patches 19 | -------------------------------------------------------------------------------- /config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - basic-check-spec 7 | image: quay.io/operator-framework/scorecard-test:v1.39.2 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:v1.39.2 8 | labels: 9 | suite: olm 10 | test: olm-bundle-validation-test 11 | - op: add 12 | path: /stages/0/tests/- 13 | value: 14 | entrypoint: 15 | - scorecard-test 16 | - olm-crds-have-validation 17 | image: quay.io/operator-framework/scorecard-test:v1.39.2 18 | labels: 19 | suite: olm 20 | test: olm-crds-have-validation-test 21 | - op: add 22 | path: /stages/0/tests/- 23 | value: 24 | entrypoint: 25 | - scorecard-test 26 | - olm-crds-have-resources 27 | image: quay.io/operator-framework/scorecard-test:v1.39.2 28 | labels: 29 | suite: olm 30 | test: olm-crds-have-resources-test 31 | - op: add 32 | path: /stages/0/tests/- 33 | value: 34 | entrypoint: 35 | - scorecard-test 36 | - olm-spec-descriptors 37 | image: quay.io/operator-framework/scorecard-test:v1.39.2 38 | labels: 39 | suite: olm 40 | test: olm-spec-descriptors-test 41 | - op: add 42 | path: /stages/0/tests/- 43 | value: 44 | entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:v1.39.2 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /config/testing/debug_logs_patch.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | env: 13 | - name: ANSIBLE_DEBUG_LOGS 14 | value: "TRUE" 15 | -------------------------------------------------------------------------------- /config/testing/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: osdk-test 3 | 4 | namePrefix: osdk- 5 | 6 | # Labels to add to all resources and selectors. 7 | #commonLabels: 8 | # someName: someValue 9 | 10 | patches: 11 | - path: manager_image.yaml 12 | - path: debug_logs_patch.yaml 13 | - path: ../default/manager_metrics_patch.yaml 14 | target: 15 | kind: Deployment 16 | 17 | apiVersion: kustomize.config.k8s.io/v1beta1 18 | kind: Kustomization 19 | resources: 20 | - ../crd 21 | - ../rbac 22 | - ../manager 23 | images: 24 | - name: testing 25 | newName: testing-operator 26 | -------------------------------------------------------------------------------- /config/testing/manager_image.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | image: testing 13 | -------------------------------------------------------------------------------- /config/testing/pull_policy/Always.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | imagePullPolicy: Always 13 | -------------------------------------------------------------------------------- /config/testing/pull_policy/IfNotPresent.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | imagePullPolicy: IfNotPresent 13 | -------------------------------------------------------------------------------- /config/testing/pull_policy/Never.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | imagePullPolicy: Never 13 | -------------------------------------------------------------------------------- /gitea-base64.svg: -------------------------------------------------------------------------------- 1 | PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJtYWluX291dGxpbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIKCSB5PSIwcHgiIHZpZXdCb3g9IjAgMCA2NDAgNjQwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA2NDAgNjQwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPHBhdGggaWQ9InRlYWJhZyIgc3R5bGU9ImZpbGw6I0ZGRkZGRiIgZD0iTTM5NS45LDQ4NC4ybC0xMjYuOS02MWMtMTIuNS02LTE3LjktMjEuMi0xMS44LTMzLjhsNjEtMTI2LjljNi0xMi41LDIxLjItMTcuOSwzMy44LTExLjgKCQljMTcuMiw4LjMsMjcuMSwxMywyNy4xLDEzbC0wLjEtMTA5LjJsMTYuNy0wLjFsMC4xLDExNy4xYzAsMCw1Ny40LDI0LjIsODMuMSw0MC4xYzMuNywyLjMsMTAuMiw2LjgsMTIuOSwxNC40CgkJYzIuMSw2LjEsMiwxMy4xLTEsMTkuM2wtNjEsMTI2LjlDNDIzLjYsNDg0LjksNDA4LjQsNDkwLjMsMzk1LjksNDg0LjJ6Ii8+Cgk8Zz4KCQk8Zz4KCQkJPHBhdGggc3R5bGU9ImZpbGw6IzYwOTkyNiIgZD0iTTYyMi43LDE0OS44Yy00LjEtNC4xLTkuNi00LTkuNi00cy0xMTcuMiw2LjYtMTc3LjksOGMtMTMuMywwLjMtMjYuNSwwLjYtMzkuNiwwLjdjMCwzOS4xLDAsNzguMiwwLDExNy4yCgkJCQljLTUuNS0yLjYtMTEuMS01LjMtMTYuNi03LjljMC0zNi40LTAuMS0xMDkuMi0wLjEtMTA5LjJjLTI5LDAuNC04OS4yLTIuMi04OS4yLTIuMnMtMTQxLjQtNy4xLTE1Ni44LTguNQoJCQkJYy05LjgtMC42LTIyLjUtMi4xLTM5LDEuNWMtOC43LDEuOC0zMy41LDcuNC01My44LDI2LjlDLTQuOSwyMTIuNCw2LjYsMjc2LjIsOCwyODUuOGMxLjcsMTEuNyw2LjksNDQuMiwzMS43LDcyLjUKCQkJCWM0NS44LDU2LjEsMTQ0LjQsNTQuOCwxNDQuNCw1NC44czEyLjEsMjguOSwzMC42LDU1LjVjMjUsMzMuMSw1MC43LDU4LjksNzUuNyw2MmM2MywwLDE4OC45LTAuMSwxODguOS0wLjFzMTIsMC4xLDI4LjMtMTAuMwoJCQkJYzE0LTguNSwyNi41LTIzLjQsMjYuNS0yMy40czEyLjktMTMuOCwzMC45LTQ1LjNjNS41LTkuNywxMC4xLTE5LjEsMTQuMS0yOGMwLDAsNTUuMi0xMTcuMSw1NS4yLTIzMS4xCgkJCQlDNjMzLjIsMTU3LjksNjI0LjcsMTUxLjgsNjIyLjcsMTQ5Ljh6IE0xMjUuNiwzNTMuOWMtMjUuOS04LjUtMzYuOS0xOC43LTM2LjktMTguN1M2OS42LDMyMS44LDYwLDI5NS40CgkJCQljLTE2LjUtNDQuMi0xLjQtNzEuMi0xLjQtNzEuMnM4LjQtMjIuNSwzOC41LTMwYzEzLjgtMy43LDMxLTMuMSwzMS0zLjFzNy4xLDU5LjQsMTUuNyw5NC4yYzcuMiwyOS4yLDI0LjgsNzcuNywyNC44LDc3LjcKCQkJCVMxNDIuNSwzNTkuOSwxMjUuNiwzNTMuOXogTTQyNS45LDQ2MS41YzAsMC02LjEsMTQuNS0xOS42LDE1LjRjLTUuOCwwLjQtMTAuMy0xLjItMTAuMy0xLjJzLTAuMy0wLjEtNS4zLTIuMWwtMTEyLjktNTUKCQkJCWMwLDAtMTAuOS01LjctMTIuOC0xNS42Yy0yLjItOC4xLDIuNy0xOC4xLDIuNy0xOC4xTDMyMiwyNzNjMCwwLDQuOC05LjcsMTIuMi0xM2MwLjYtMC4zLDIuMy0xLDQuNS0xLjVjOC4xLTIuMSwxOCwyLjgsMTgsMi44CgkJCQlsMTEwLjcsNTMuN2MwLDAsMTIuNiw1LjcsMTUuMywxNi4yYzEuOSw3LjQtMC41LDE0LTEuOCwxNy4yQzQ3NC42LDM2My44LDQyNS45LDQ2MS41LDQyNS45LDQ2MS41eiIvPgoJCQk8cGF0aCBzdHlsZT0iZmlsbDojNjA5OTI2IiBkPSJNMzI2LjgsMzgwLjFjLTguMiwwLjEtMTUuNCw1LjgtMTcuMywxMy44Yy0xLjksOCwyLDE2LjMsOS4xLDIwYzcuNyw0LDE3LjUsMS44LDIyLjctNS40CgkJCQljNS4xLTcuMSw0LjMtMTYuOS0xLjgtMjMuMWwyNC00OS4xYzEuNSwwLjEsMy43LDAuMiw2LjItMC41YzQuMS0wLjksNy4xLTMuNiw3LjEtMy42YzQuMiwxLjgsOC42LDMuOCwxMy4yLDYuMQoJCQkJYzQuOCwyLjQsOS4zLDQuOSwxMy40LDcuM2MwLjksMC41LDEuOCwxLjEsMi44LDEuOWMxLjYsMS4zLDMuNCwzLjEsNC43LDUuNWMxLjksNS41LTEuOSwxNC45LTEuOSwxNC45CgkJCQljLTIuMyw3LjYtMTguNCw0MC42LTE4LjQsNDAuNmMtOC4xLTAuMi0xNS4zLDUtMTcuNywxMi41Yy0yLjYsOC4xLDEuMSwxNy4zLDguOSwyMS4zYzcuOCw0LDE3LjQsMS43LDIyLjUtNS4zCgkJCQljNS02LjgsNC42LTE2LjMtMS4xLTIyLjZjMS45LTMuNywzLjctNy40LDUuNi0xMS4zYzUtMTAuNCwxMy41LTMwLjQsMTMuNS0zMC40YzAuOS0xLjcsNS43LTEwLjMsMi43LTIxLjMKCQkJCWMtMi41LTExLjQtMTIuNi0xNi43LTEyLjYtMTYuN2MtMTIuMi03LjktMjkuMi0xNS4yLTI5LjItMTUuMnMwLTQuMS0xLjEtNy4xYy0xLjEtMy4xLTIuOC01LjEtMy45LTYuM2M0LjctOS43LDkuNC0xOS4zLDE0LjEtMjkKCQkJCWMtNC4xLTItOC4xLTQtMTIuMi02LjFjLTQuOCw5LjgtOS43LDE5LjctMTQuNSwyOS41Yy02LjctMC4xLTEyLjksMy41LTE2LjEsOS40Yy0zLjQsNi4zLTIuNywxNC4xLDEuOSwxOS44CgkJCQlDMzQzLjIsMzQ2LjUsMzM1LDM2My4zLDMyNi44LDM4MC4xeiIvPgoJCTwvZz4KCTwvZz4KPC9nPgo8L3N2Zz4K -------------------------------------------------------------------------------- /gitea-catalog.Dockerfile: -------------------------------------------------------------------------------- 1 | # The builder image is expected to contain 2 | # /bin/opm (with serve subcommand) 3 | FROM quay.io/operator-framework/opm:latest as builder 4 | 5 | # Copy FBC root into image at /configs and pre-populate serve cache 6 | ADD gitea-catalog /configs 7 | RUN ["/bin/opm", "serve", "/configs", "--cache-dir=/tmp/cache", "--cache-only"] 8 | 9 | FROM quay.io/operator-framework/opm:latest 10 | # The base image is expected to contain 11 | # /bin/opm (with serve subcommand) and /bin/grpc_health_probe 12 | 13 | # Configure the entrypoint and command 14 | ENTRYPOINT ["/bin/opm"] 15 | CMD ["serve", "/configs", "--cache-dir=/tmp/cache"] 16 | 17 | COPY --from=builder /configs /configs 18 | COPY --from=builder /tmp/cache /tmp/cache 19 | 20 | # Set FBC-specific label for the location of the FBC root directory 21 | # in the image 22 | LABEL operators.operatorframework.io.index.configs.v1=/configs 23 | -------------------------------------------------------------------------------- /gitea-catalog/index.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | image: quay.io/rhpds/gitea-operator-bundle:v2.1.0 3 | name: gitea-operator.v2.1.0 4 | package: gitea-operator 5 | properties: 6 | - type: olm.gvk 7 | value: 8 | group: pfe.rhpds.com 9 | kind: Gitea 10 | version: v1 11 | - type: olm.package 12 | value: 13 | packageName: gitea-operator 14 | version: 2.1.0 15 | - type: olm.bundle.object 16 | value: 17 | data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsiY3JlYXRpb25UaW1lc3RhbXAiOm51bGwsIm5hbWUiOiJnaXRlYS5wZmUucmhwZHMuY29tIn0sInNwZWMiOnsiZ3JvdXAiOiJwZmUucmhwZHMuY29tIiwibmFtZXMiOnsia2luZCI6IkdpdGVhIiwibGlzdEtpbmQiOiJHaXRlYUxpc3QiLCJwbHVyYWwiOiJnaXRlYSIsInNpbmd1bGFyIjoiZ2l0ZWEifSwic2NvcGUiOiJOYW1lc3BhY2VkIiwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSIsInNjaGVtYSI6eyJvcGVuQVBJVjNTY2hlbWEiOnsiZGVzY3JpcHRpb24iOiJHaXRlYSBpcyB0aGUgU2NoZW1hIGZvciB0aGUgZ2l0ZWFzIEFQSSIsInByb3BlcnRpZXMiOnsiYXBpVmVyc2lvbiI6eyJkZXNjcmlwdGlvbiI6IkFQSVZlcnNpb24gZGVmaW5lcyB0aGUgdmVyc2lvbmVkIHNjaGVtYSBvZiB0aGlzIHJlcHJlc2VudGF0aW9uIG9mIGFuIG9iamVjdC4gU2VydmVycyBzaG91bGQgY29udmVydCByZWNvZ25pemVkIHNjaGVtYXMgdG8gdGhlIGxhdGVzdCBpbnRlcm5hbCB2YWx1ZSwgYW5kIG1heSByZWplY3QgdW5yZWNvZ25pemVkIHZhbHVlcy4gTW9yZSBpbmZvOiBodHRwczovL2dpdC5rOHMuaW8vY29tbXVuaXR5L2NvbnRyaWJ1dG9ycy9kZXZlbC9zaWctYXJjaGl0ZWN0dXJlL2FwaS1jb252ZW50aW9ucy5tZCNyZXNvdXJjZXMiLCJ0eXBlIjoic3RyaW5nIn0sImtpbmQiOnsiZGVzY3JpcHRpb24iOiJLaW5kIGlzIGEgc3RyaW5nIHZhbHVlIHJlcHJlc2VudGluZyB0aGUgUkVTVCByZXNvdXJjZSB0aGlzIG9iamVjdCByZXByZXNlbnRzLiBTZXJ2ZXJzIG1heSBpbmZlciB0aGlzIGZyb20gdGhlIGVuZHBvaW50IHRoZSBjbGllbnQgc3VibWl0cyByZXF1ZXN0cyB0by4gQ2Fubm90IGJlIHVwZGF0ZWQuIEluIENhbWVsQ2FzZS4gTW9yZSBpbmZvOiBodHRwczovL2dpdC5rOHMuaW8vY29tbXVuaXR5L2NvbnRyaWJ1dG9ycy9kZXZlbC9zaWctYXJjaGl0ZWN0dXJlL2FwaS1jb252ZW50aW9ucy5tZCN0eXBlcy1raW5kcyIsInR5cGUiOiJzdHJpbmcifSwibWV0YWRhdGEiOnsidHlwZSI6Im9iamVjdCJ9LCJzcGVjIjp7ImRlc2NyaXB0aW9uIjoiU3BlYyBkZWZpbmVzIHRoZSBkZXNpcmVkIHN0YXRlIG9mIEdpdGVhIiwicHJvcGVydGllcyI6eyJnaXRlYUFkbWluRW1haWwiOnsiZGVzY3JpcHRpb24iOiJlLW1haWwgYWRkcmVzcyBmb3IgdGhlIEdpdGVhIEFkbWluIFVzZXIuIERlZmF1bHQgaXMgJ25vdHNldEBub3RzZXQub3JnJyIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFBZG1pblBhc3N3b3JkIjp7ImRlc2NyaXB0aW9uIjoiUGFzc3dvcmQgZm9yIHRoZSBHaXRlYSBhZG1pbiB1c2VyLiBJZiBub3Qgc3BlY2lmaWVkIG9yIGVtcHR5IGEgcmFuZG9tIHBhc3N3b3JkIHdpbGwgYmUgY3JlYXRlZCB3aXRoIGxlbmd0aCBvZiBnaXRlYUFkbWluUGFzc3dvcmRMZW5ndGggcmFuZG9tIEFTQ0lJIGNoYXJhY3RlcnMuIERlZmF1bHQgaXMgJyciLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhQWRtaW5QYXNzd29yZExlbmd0aCI6eyJkZXNjcmlwdGlvbiI6IklmIGEgZ2l0ZWFBZG1pblVzZXIgaXMgcHJvdmlkZWQgYnV0IG5vIGdpdGVhQWRtaW5QYXNzb3dyZCBpcyBwcm92aWRlZCBhIHJhbmRvbSBBU0NJSSBwYXNzd29yZCB3aXRoIHRoZSBsZW5ndGggc3BlY2lmaWVkIHdpbGwgYmUgY3JlYXRlZC4gRGVmYXVsdCBpcyAxNiIsInR5cGUiOiJpbnRlZ2VyIn0sImdpdGVhQWRtaW5QYXNzd29yZFNlY3JldE5hbWUiOnsiZGVzY3JpcHRpb24iOiJOYW1lIG9mIGEgc2VjcmV0IGNvbnRhaW5pbmcgdGhlIEdpdGVhIGFkbWluIHVzZXIncyBwYXNzd29yZCBpbiBzZWNyZXQga2V5IGFkbWluUGFzc3dvcmQuIElmIHRoaXMgdmFyaWFibGUgaXMgc2V0IGl0IHRha2VzIHByZWNlZGVuY2Ugb3ZlciBhbGwgb3RoZXIgd2F5cyB0byBzcGVjaWZ5L2dlbmVyYXRlIGFuIGFkbWluIHBhc3N3b3JkLiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFBZG1pblVzZXIiOnsiZGVzY3JpcHRpb24iOiJVc2VyIElEIGZvciB0aGUgQWRtaW4gVXNlciB0byBiZSBjcmVhdGVkLiBJZiBub3Qgc3BlY2lmaWVkIG5vIGFkbWluIHVzZXIgd2lsbCBiZSBjcmVhdGVkLiBOb3RlIHRoYXQgaWYgZ2l0ZWFEaXNhYmxlUmVnaXN0cmF0aW9uIGlzIHNldCB0byBmYWxzZSBhbmQgbm8gYWRtaW4gdXNlciB3aWxsIGJlIGNyZWF0ZWQgeW91IHdpbGwgbm90IGJlIGFibGUgdG8gY3JlYXRlIGFueSB1c2VycyBmb3IgR2l0ZWEuIERlZmF1bHQgaXMgJyciLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhQWxsb3dDcmVhdGVPcmdhbml6YXRpb24iOnsiZGVzY3JpcHRpb24iOiJBbGxvdyB1c2VycyB0byBjcmVhdGUgb3JnYW5pemF0aW9ucyBpbiBHaXRlYS4gRGVmYXVsdCBpcyB0cnVlLiIsInR5cGUiOiJib29sZWFuIn0sImdpdGVhQWxsb3dMb2NhbE5ldHdvcmtNaWdyYXRpb24iOnsiZGVzY3JpcHRpb24iOiJBbGxvdyBtaWdyYXRpb24gb2YgcmVwb3NpdG9yaWVzIGhvc3RlZCBvbiBsb2NhbCBuZXR3b3JrIElQcyBhcyBkZWZpbmVkIGJ5IFJGQyAxOTE4LCBSRkMgMTEyMiwgUkZDIDQ2MzIgYW5kIFJGQyA0MjkxLiBEZWZhdWx0OiBmYWxzZSIsInR5cGUiOiJib29sZWFuIn0sImdpdGVhQ29uZmlnTWFwTmFtZSI6eyJkZXNjcmlwdGlvbiI6Ik5hbWUgb2YgYSBjb25maWcgbWFwIGluIHRoZSBzYW1lIG5hbWVzcGFjZSBhcyB0aGUgR2l0ZWEgY3VzdG9tIHJlc291cmNlLiBUaGUgY29uZmlnIG1hcCBtdXN0IGNvbnRhaW4gb25lIGZpbGUgY2FsbGVkIGFwcC5pbmkgdG8gY29uZmlndXJlIEdpdGVhLiBJZiB0aGlzIHZhcmlhYmxlIGlzIHNldCB0aGVuIGdpdGVhSG9zdG5hbWUgbXVzdCBhbHNvIGJlIHNldC4gZ2l0ZWFTc2wgc2hvdWxkIGJlIHNldCBidXQgd2lsbCBkZWZhdWx0IHRvIGZhbHNlLiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFDcHVMaW1pdCI6eyJhbnlPZiI6W3sidHlwZSI6ImludGVnZXIifSx7InR5cGUiOiJzdHJpbmcifV0sImRlc2NyaXB0aW9uIjoiQ1BVIGxpbWl0IGZvciBHaXRlYS4gRGVmYXVsdCBpcyAnNTAwbScuIiwicGF0dGVybiI6Il4oXFwrfC0pPygoWzAtOV0rKFxcLlswLTldKik/KXwoXFwuWzAtOV0rKSkoKFtLTUdUUEVdaSl8W251bWtNR1RQRV18KFtlRV0oXFwrfC0pPygoWzAtOV0rKFxcLlswLTldKik/KXwoXFwuWzAtOV0rKSkpKT8kIiwieC1rdWJlcm5ldGVzLWludC1vci1zdHJpbmciOnRydWV9LCJnaXRlYUNwdVJlcXVlc3QiOnsiYW55T2YiOlt7InR5cGUiOiJpbnRlZ2VyIn0seyJ0eXBlIjoic3RyaW5nIn1dLCJkZXNjcmlwdGlvbiI6IkNQVSByZXF1ZXN0IGZvciBHaXRlYS4gRGVmYXVsdCBpcyAnMjAwbScuIiwicGF0dGVybiI6Il4oXFwrfC0pPygoWzAtOV0rKFxcLlswLTldKik/KXwoXFwuWzAtOV0rKSkoKFtLTUdUUEVdaSl8W251bWtNR1RQRV18KFtlRV0oXFwrfC0pPygoWzAtOV0rKFxcLlswLTldKik/KXwoXFwuWzAtOV0rKSkpKT8kIiwieC1rdWJlcm5ldGVzLWludC1vci1zdHJpbmciOnRydWV9LCJnaXRlYUNyZWF0ZVVzZXJzIjp7ImRlc2NyaXB0aW9uIjoiQ3JlYXRlIHVzZXJzIGluIEdpdGVhLiBPbmx5IHBvc3NpYmxlIGlmIGFuIGFkbWluIHVzZXIgaXMgYWxzbyBiZWluZyBjcmVhdGVkLiBEZWZhdWx0IGlzIGZhbHNlIiwidHlwZSI6ImJvb2xlYW4ifSwiZ2l0ZWFEaXNhYmxlUmVnaXN0cmF0aW9uIjp7ImRlc2NyaXB0aW9uIjoiRGlzYWJsZSB1c2VyIHNlbGYtcmVnaXN0cmF0aW9uLiBJZiB0aGlzIGZsYWcgaXMgc2V0IGFuIEFkbWluIFVzZXIgc2hvdWxkIGJlIHNwZWNpZmllZCB0byBiZSBjcmVhdGVkLiBPdGhlcndpc2Ugbm8gdXNlcnMgY2FuIGJlIGNyZWF0ZWQgYXQgYWxsLiBEZWZhdWx0IGlzIGZhbHNlLiIsInR5cGUiOiJib29sZWFuIn0sImdpdGVhRGlzYWJsZVNzaCI6eyJkZXNjcmlwdGlvbiI6IkRpc2FibGUgU1NIIGZvciBHaXRlYS4gRGVmYXVsdCBpcyB0cnVlLiIsInR5cGUiOiJib29sZWFuIn0sImdpdGVhRW5hYmxlQ2FwdGNoYSI6eyJkZXNjcmlwdGlvbiI6IkRpc3BsYXkgQ2FwdGNoYSB3aGVuIHVzZXJzIGFyZSByZWdpc3RlcmluZyBhIG5ldyBhY2NvdW50LiBObyBlZmZlY3QgaWYgZ2l0ZWFEaXNhYmxlUmVnaXN0cmF0aW9uIGlzIHNldCB0byBmYWxzZS4gRGVmYXVsdCBpcyBmYWxzZS4iLCJ0eXBlIjoiYm9vbGVhbiJ9LCJnaXRlYUVuYWJsZU5vdGlmeU1haWwiOnsiZGVzY3JpcHRpb24iOiJTZW5kIGUtbWFpbCBub3RpZmljYXRpb25zIHRvIHVzZXJzIGZvciB2YXJpb3VzIHRhc2tzIGluIEdpdGVhLiBSZXF1aXJlcyB0aGUgbWFpbGVyIHRvIGJlIGNvbmZpZ3VyZWQgY29ycmVjdGx5LiBEZWZhdWx0IGlzIGZhbHNlLiIsInR5cGUiOiJib29sZWFuIn0sImdpdGVhR2VuZXJhdGVVc2VyRm9ybWF0Ijp7ImRlc2NyaXB0aW9uIjoiRm9ybWF0IGZvciB1c2VyIG5hbWVzIHRvIGJlIGNyZWF0ZWQuIFRoaXMgd2lsbCBiZSB0YWtlbiBsaXRlcmFsbHkgaWYgb25seSBvbmUgdXNlciBpcyB0byBiZSBjcmVhdGVkIChlLmcuIGxhYi11c2VyKS4gSWYgbW9yZSB0aGFuIG9uZSB1c2VyIGlzIHRvIGJlIGNyZWF0ZWQgdGhlIGZvcm1hdCBuZWVkcyB0byBpbmNsdWRlIGEgJyVkJyB0byBzZXQgdGhlIHVzZXIgbnVtYmVyLiBEZWZhdWx0IGlzICd1c2VyJWQnIiwidHlwZSI6InN0cmluZyJ9LCJnaXRlYUhvc3RuYW1lIjp7ImRlc2NyaXB0aW9uIjoiU3BlY2lmeSB0aGUgaG9zdG5hbWUgZm9yIHRoZSBHaXRlYSBSb3V0ZS4gRGVmYXVsdCBpcyAnJy4gTWFrZSBzdXJlIHRoZSByb3V0ZSBpcyByZWFjaGFibGUgZnJvbSBvdXRzaWRlIHRoZSBjbHVzdGVyLiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFIdHRwUG9ydCI6eyJkZXNjcmlwdGlvbiI6IlBvcnQgZm9yIEdpdGVhIHRvIGxpc3RlbiBvbi4gRGVmYXVsdCBpcyAzMDAwLiIsInR5cGUiOiJpbnRlZ2VyIn0sImdpdGVhSW1hZ2UiOnsiZGVzY3JpcHRpb24iOiJDb250YWluZXIgaW1hZ2UgZm9yIEdpdGVhLiBEZWZhdWx0IGlzICdxdWF5LmlvL3JocGRzL2dpdGVhJy4iLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhSW1hZ2VQdWxsUG9saWN5Ijp7ImRlc2NyaXB0aW9uIjoiUHVsbCBwb2xpY3kgZm9yIHRoZSBHaXRlYSBjb250YWluZXIgaW1hZ2UuIERlZmF1bHQgaXMgJ0lmTm90UHJlc2VudCcuIiwidHlwZSI6InN0cmluZyJ9LCJnaXRlYUltYWdlVGFnIjp7ImRlc2NyaXB0aW9uIjoiSW1hZ2UgdGFnIGZvciB0aGUgR2l0ZWEgY29udGFpbmVyIGltYWdlLiBEZWZhdWx0IGlzICdsYXRlc3QnLiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFNYWlsZXJFbmFibGVkIjp7ImRlc2NyaXB0aW9uIjoiRW5hYmxlIGUtbWFpbCBpbnRlZ3JhdGlvbiBmb3IgR2l0ZWEuIElmIHNldCB0byB0cnVlIHRoZSBvdGhlciBnaXRlYU1haWxlciogcHJvcGVydGllcyBuZWVkIHRvIGJlIHByb3ZpZGVkLiBTZWUgaHR0cHM6Ly9kb2NzLmdpdGVhLmlvL2VuLXVzL2VtYWlsLXNldHVwLyBmb3IgZXhhbXBsZSB2YWx1ZXMuIERlZmF1bHQgaXMgZmFsc2UuIiwidHlwZSI6ImJvb2xlYW4ifSwiZ2l0ZWFNYWlsZXJGcm9tIjp7ImRlc2NyaXB0aW9uIjoiRS1tYWlsIGludGVncmF0aW9uLiBGUk9NIGUtbWFpbCBhZGRyZXNzIHRvIGJlIHVzZWQuIERlZmF1bHQgaXMgXCJcIi4iLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhTWFpbGVySGVsb0hvc3RuYW1lIjp7ImRlc2NyaXB0aW9uIjoiSGVsbyBIb3N0bmFtZSBmb3IgdGhlIGUtbWFpbCBzZXJ2ZXIuIE5vdCByZXF1aXJlZCBmb3IgYWxsIGUtbWFpbCBwcm92aWRlcnMuIERlZmF1bHQgaXMgXCJcIi4iLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhTWFpbGVySG9zdCI6eyJkZXNjcmlwdGlvbiI6Ikhvc3RuYW1lIG9mIHRoZSBlLW1haWwgc2VydmVyIHRvIGJlIHVzZWQuIERlZmF1bHQgaXMgXCJcIi4iLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhTWFpbGVyUGFzc3dvcmQiOnsiZGVzY3JpcHRpb24iOiJQYXNzd29yZCBmb3IgdGhlIFVzZXIgSUQgb24gdGhlIGUtbWFpbCBzZXJ2ZXIgdG8gYmUgdXNlZC4gTWF5IG5lZWQgdG8gYmUgYW4gYXBwLXNwZWNpZmljIHBhc3N3b3JkIGlmIHR3by1mYWN0b3IgYXV0aGVudGljYXRpb24gaXMgZW5hYmxlZCBvbiB0aGUgZS1tYWlsIHNlcnZlci4gRGVmYXVsdCBpcyBcIlwiLiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFNYWlsZXJUbHMiOnsiZGVzY3JpcHRpb24iOiJVc2UgVExTIGVuY3J5cHRpb24gd2hlbiBjb25uZWN0aW5nIHRvIHRoZSBtYWlsZXIgaG9zdC4gRGVmYXVsdCBpcyB0cnVlLiIsInR5cGUiOiJib29sZWFuIn0sImdpdGVhTWFpbGVyVHlwZSI6eyJkZXNjcmlwdGlvbiI6IlR5cGUgb2YgZS1tYWlsIHByb3ZpZGVyIHRvIGJlIHVzZWQuIERlZmF1bHQgaXMgc210cC4iLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhTWFpbGVyVXNlciI6eyJkZXNjcmlwdGlvbiI6IlVzZXIgSUQgb24gdGhlIGUtbWFpbCBzZXJ2ZXIgdG8gdXNlLiBGcmVxdWVudGx5IHRoZSBzYW1lIGFzIHRoZSB2YWx1ZSBmb3IgZ2l0ZWFNYWlsZXJGcm9tLiBEZWZhdWx0IGlzIFwiXCIuIiwidHlwZSI6InN0cmluZyJ9LCJnaXRlYU1lbW9yeUxpbWl0Ijp7ImRlc2NyaXB0aW9uIjoiTWVtb3J5IGxpbWl0IGZvciBHaXRlYS4gRGVmYXVsdCBpcyAnMUdpJy4iLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhTWVtb3J5UmVxdWVzdCI6eyJkZXNjcmlwdGlvbiI6Ik1lbW9yeSByZXF1ZXN0IGZvciBHaXRlYS4gRGVmYXVsdCBpcyAnMUdpJy4iLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhTWlncmF0ZVJlcG9zaXRvcmllcyI6eyJkZXNjcmlwdGlvbiI6IkZvciBjcmVhdGVkIHVzZXJzIG1pZ3JhdGUgcmVwb3NpdG9yaWVzIGZyb20gYW5vdGhlciBsb2NhdGlvbiwgZS5nLiBHaXRIdWIuIERlZmF1bHQgaXMgZmFsc2UuIiwidHlwZSI6ImJvb2xlYW4ifSwiZ2l0ZWFQb3N0Z3Jlc3FsRGF0YWJhc2VOYW1lIjp7ImRlc2NyaXB0aW9uIjoiTmFtZSBvZiB0aGUgUG9zdGdyZVNRTCBkYXRhYmFzZS4gT25seSByZXF1aXJlZCB3aGVuIFBvc3RncmVTUUwgaXMgbm90IHNldCB1cCBieSB0aGUgb3BlcmF0b3IuIERlZmF1bHQgaXMgJ2dpdGVhZGInIiwidHlwZSI6InN0cmluZyJ9LCJnaXRlYVBvc3RncmVzcWxQYXNzd29yZCI6eyJkZXNjcmlwdGlvbiI6IlBvc3RncmVTUUwgcGFzc3dvcmQuIE9ubHkgcmVxdWlyZWQgd2hlbiBQb3N0Z3JlU1FMIGlzIG5vdCBzZXQgdXAgYnkgdGhlIG9wZXJhdG9yLiBEZWZhdWx0IGlzICdnaXRlYXBhc3N3b3JkJyIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFQb3N0Z3Jlc3FsU2VydmljZU5hbWUiOnsiZGVzY3JpcHRpb24iOiJOYW1lIG9mIHRoZSBQb3N0Z3JlU1FMIHNlcnZpY2UuIE9ubHkgcmVxdWlyZWQgd2hlbiBQb3N0Z3JlU1FMIGlzIG5vdCBzZXQgdXAgYnkgdGhlIG9wZXJhdG9yLiBEZWZhdWx0IGlzICdwb3N0Z3Jlc3FsLScgZm9sbG93ZWQgYnkgdGhlIEdpdGVhIHJlc291cmNlIG5hbWUuIiwidHlwZSI6InN0cmluZyJ9LCJnaXRlYVBvc3RncmVzcWxVc2VyIjp7ImRlc2NyaXB0aW9uIjoiTmFtZSBvZiB0aGUgUG9zdGdyZVNRTCB1c2VyLiBPbmx5IHJlcXVpcmVkIHdoZW4gUG9zdGdyZVNRTCBpcyBub3Qgc2V0IHVwIGJ5IHRoZSBvcGVyYXRvci4gRGVmYXVsdCBpcyAnZ2l0ZWF1c2VyJyIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFSZWdpc3RlckVtYWlsQ29uZmlybSI6eyJkZXNjcmlwdGlvbiI6IlNlbmQgZS1tYWlsIGNvbmZpcm1hdGlvbiB0byB1c2VycyB3aGVuIHNlbGYtcmVnaXN0ZXJpbmcuIFVzZXJzIG11c3QgY2xpY2sgYSBsaW5rIHRvIHZhbGlkYXRlIHRoZWlyIGUtbWFpbCBhZGRyZXNzIGJlZm9yZSB0aGUgYWNjb3VudCBnZXRzIGNyZWF0ZWQuIFJlcXVpcmVzIHRoZSBtYWlsZXIgdG8gYmUgY29uZmlndXJlZCBjb3JyZWN0bHkuIERlZmF1bHQgaXMgZmFsc2UuIiwidHlwZSI6ImJvb2xlYW4ifSwiZ2l0ZWFSZXBvc2l0b3JpZXNMaXN0Ijp7ImRlc2NyaXB0aW9uIjoiTGlzdCBvZiByZXBvc2l0b3JpZXMgdG8gYmUgbWlncmF0ZWQgZnJvbSBhbm90aGVyIGxvY2F0aW9uLiBFYWNoIHJlcG9zaXRvcnkgaXMgYW4gYXJyYXkgb2YgcmVwbywgbmFtZSBhbmQgcHJpdmF0ZS4gRGVmYXVsdCBpcyBbXS4iLCJpdGVtcyI6eyJwcm9wZXJ0aWVzIjp7Im5hbWUiOnsiZGVzY3JpcHRpb24iOiJOYW1lIG9mIHRoZSBtaWdyYXRlZCByZXBvc2l0b3J5IGluIEdpdGVhLiIsInR5cGUiOiJzdHJpbmcifSwicHJpdmF0ZSI6eyJkZXNjcmlwdGlvbiI6IkNyZWF0ZSBwcml2YXRlIHJlcG9zaXRvcnkgaW4gR2l0ZWEuIiwidHlwZSI6ImJvb2xlYW4ifSwicmVwbyI6eyJkZXNjcmlwdGlvbiI6IlNvdXJjZSByZXBvc2l0b3J5IFVSTCB0byBtaWdyYXRlLiIsInR5cGUiOiJzdHJpbmcifX0sInR5cGUiOiJvYmplY3QifSwidHlwZSI6ImFycmF5In0sImdpdGVhU2VydmljZU5hbWUiOnsiZGVzY3JpcHRpb24iOiJOYW1lIG9mIHRoZSBHaXRlYSBTZXJ2aWNlIHRvIGJlIGRlcGxveWVkLiBEZWZhdWx0cyB0byB0aGUgbmFtZSBvZiB0aGUgR2l0ZWEgY3VzdG9tIHJlc291cmNlLiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFTc2hQb3J0Ijp7ImRlc2NyaXB0aW9uIjoiUG9ydCBmb3IgR2l0ZWEgdG8gc3RhcnQgYW4gU1NIIHNlcnZlciBvbi4gRGVmYXVsdCBpcyAyMDIyIiwidHlwZSI6ImludGVnZXIifSwiZ2l0ZWFTc2wiOnsiZGVzY3JpcHRpb24iOiJDcmVhdGUgYW4gSFRUUFMgdGVybWluYXRlZCByb3V0ZSBmb3IgR2l0ZWEuIERlZmF1bHQgaXMgJ2ZhbHNlJyIsInR5cGUiOiJib29sZWFuIn0sImdpdGVhU3RhcnRMZnNTZXJ2ZXIiOnsiZGVzY3JpcHRpb24iOiJTdGFydCBMRlMgU2VydmVyIGluIHRoZSBHaXRlYSBjb250YWluZXIuIERlZmF1bHQ6IGZhbHNlIiwidHlwZSI6ImJvb2xlYW4ifSwiZ2l0ZWFTdGFydFNzaFNlcnZlciI6eyJkZXNjcmlwdGlvbiI6IlN0YXJ0IFNTSCBTZXJ2ZXIgaW4gdGhlIEdpdGVhIGNvbnRhaW5lci4gRGVmYXVsdCBpcyBmYWxzZS4iLCJ0eXBlIjoiYm9vbGVhbiJ9LCJnaXRlYVVzZXJFbWFpbERvbWFpbiI6eyJkZXNjcmlwdGlvbiI6ImUtbWFpbCBkb21haW4gZm9yIHRoZSBjcmVhdGVkIEdpdGVhIHVzZXJzLiBEZWZhdWx0IGlzIFwiZXhhbXBsZS5jb21cIiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFVc2VyTnVtYmVyIjp7ImRlc2NyaXB0aW9uIjoiTnVtYmVyIG9mIHVzZXJzIHRvIGNyZWF0ZSBpbiBHaXRlYS4gSWYgMSB0aGVuIG9ubHkgb25lIHVzZXIgd2lsbCBiZSBjcmVhdGVkIHdpdGggdGhlIHVzZXJuYW1lIGZyb20gZ2l0ZWFHZW5lcmF0ZVVzZXJGb3JtYXQuIElmIG1vcmUgdGhhbiBvbmUgdGhlbiB1c2VycyB3aWxsIGJlIGNyZWF0ZWQgYWNjb3JkaW5nIHRvIHRoZSBmb3JtYXQgaW4gZ2l0ZWFHZW5lcmF0ZVVzZXJGb3JtYXQuIERlZmF1bHQgaXMgMiIsInR5cGUiOiJpbnRlZ2VyIn0sImdpdGVhVXNlclBhc3N3b3JkIjp7ImRlc2NyaXB0aW9uIjoiUGFzc3dvcmQgZm9yIGFsbCBjcmVhdGVkIEdpdGVhIHVzZXJzLiBJZiBub3Qgc3BlY2lmaWVkIG9yIGVtcHR5IGEgcmFuZG9tIHBhc3N3b3JkIHdpbGwgYmUgY3JlYXRlZCB3aXRoIGxlbmd0aCBvZiBnaXRlYVVzZXJQYXNzd29yZExlbmd0aCByYW5kb20gQVNDSUkgY2hhcmFjdGVycy4gRGVmYXVsdCBpcyBcIlwiIiwidHlwZSI6InN0cmluZyJ9LCJnaXRlYVVzZXJQYXNzd29yZExlbmd0aCI6eyJkZXNjcmlwdGlvbiI6IklmIGEgZ2l0ZWFDcmVhdGVVc2VycyBpcyBzZXQgYnV0IG5vIGdpdGVhVXNlclBhc3Nvd3JkIGlzIHByb3ZpZGVkIGEgcmFuZG9tIEFTQ0lJIHBhc3N3b3JkIHdpdGggdGhlIGxlbmd0aCBzcGVjaWZpZWQgd2lsbCBiZSBjcmVhdGVkLiBEZWZhdWx0IGlzIDE2IiwidHlwZSI6ImludGVnZXIifSwiZ2l0ZWFVc2VyUGFzc3dvcmRTZWNyZXROYW1lIjp7ImRlc2NyaXB0aW9uIjoiTmFtZSBvZiBhIHNlY3JldCBjb250YWluaW5nIHRoZSBHaXRlYSB1c2VyIGNvbW1vbiBwYXNzd29yZCBpbiBzZWNyZXQga2V5IHVzZXJQYXNzd29yZC4gSWYgdGhpcyB2YXJpYWJsZSBpcyBzZXQgaXQgdGFrZXMgcHJlY2VkZW5jZSBvdmVyIGFsbCBvdGhlciB3YXlzIHRvIHNwZWNpZnkvZ2VuZXJhdGUgYSB1c2VyIHBhc3N3b3JkLiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFWb2x1bWVTaXplIjp7ImRlc2NyaXB0aW9uIjoiU2l6ZSBvZiB0aGUgcGVyc2lzdGVudCB2b2x1bWUgY2xhaW0gZm9yIEdpdGVhLiBEZWZhdWx0IGlzICc0R2knLiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFWb2x1bWVTdG9yYWdlQ2xhc3MiOnsiZGVzY3JpcHRpb24iOiJTdG9yYWdlIENsYXNzIHRvIGJlIHVzZWQgZm9yIHRoZSBHaXRlYSBwZXJzaXN0ZW50IHZvbHVtZSBjbGFpbS4gRGVmYXVsdCBpcyBlbXB0eSAtIHdoaWNoIHdpbGwgY3JlYXRlIGEgUFZDIHVzaW5nIHRoZSBjdXJyZW50bHkgYXZhaWxhYmxlIGRlZmF1bHQgc3RvcmFnZSBjbGFzcyBvbiB0aGUgY2x1c3Rlci4iLCJ0eXBlIjoic3RyaW5nIn0sImdpdGVhV2ViaG9va0FsbG93ZWRIb3N0TGlzdCI6eyJkZXNjcmlwdGlvbiI6Ikxpc3Qgb2YgaG9zdHMgdGhhdCBhIHdlYiBob29rIGlzIGFsbG93ZWQgdG8gY2FsbC4gU2VlIGh0dHBzOi8vZG9jcy5naXRlYS5jb20vbmV4dC9hZG1pbmlzdHJhdGlvbi9jb25maWctY2hlYXQtc2hlZXQjd2ViaG9vay13ZWJob29rIGZvciBtb3JlIGRldGFpbHMuIERlZmF1bHQgaXMgJ2V4dGVybmFsLHByaXZhdGUnLiIsInR5cGUiOiJzdHJpbmcifSwiZ2l0ZWFXZWJob29rU2tpcFRsc1ZlcmlmeSI6eyJkZXNjcmlwdGlvbiI6IlNldCB0byAndHJ1ZScgdG8gc2tpcCB2YWxpZGF0aW9uIG9mIHRoZSB3ZWJob29rIHRhcmdldCBVUkwgY2VydGlmaWNhdGUuIERlZmF1bHQgaXMgZmFsc2UuIiwidHlwZSI6ImJvb2xlYW4ifSwicG9zdGdyZXNxbENwdUxpbWl0Ijp7ImFueU9mIjpbeyJ0eXBlIjoiaW50ZWdlciJ9LHsidHlwZSI6InN0cmluZyJ9XSwiZGVzY3JpcHRpb24iOiJDUFUgbGltaXQgZm9yIHRoZSBQb3N0Z3JlU1FMIGRhdGFiYXNlLiBEZWZhdWx0IGlzICc1MDBtJy4iLCJwYXR0ZXJuIjoiXihcXCt8LSk/KChbMC05XSsoXFwuWzAtOV0qKT8pfChcXC5bMC05XSspKSgoW0tNR1RQRV1pKXxbbnVta01HVFBFXXwoW2VFXShcXCt8LSk/KChbMC05XSsoXFwuWzAtOV0qKT8pfChcXC5bMC05XSspKSkpPyQiLCJ4LWt1YmVybmV0ZXMtaW50LW9yLXN0cmluZyI6dHJ1ZX0sInBvc3RncmVzcWxDcHVSZXF1ZXN0Ijp7ImFueU9mIjpbeyJ0eXBlIjoiaW50ZWdlciJ9LHsidHlwZSI6InN0cmluZyJ9XSwiZGVzY3JpcHRpb24iOiJDUFUgcmVxdWVzdCBmb3IgdGhlIFBvc3RncmVTUUwgZGF0YWJhc2UuIERlZmF1bHQgaXMgJzIwMG0nLiIsInBhdHRlcm4iOiJeKFxcK3wtKT8oKFswLTldKyhcXC5bMC05XSopPyl8KFxcLlswLTldKykpKChbS01HVFBFXWkpfFtudW1rTUdUUEVdfChbZUVdKFxcK3wtKT8oKFswLTldKyhcXC5bMC05XSopPyl8KFxcLlswLTldKykpKSk/JCIsIngta3ViZXJuZXRlcy1pbnQtb3Itc3RyaW5nIjp0cnVlfSwicG9zdGdyZXNxbERhdGFiYXNlTmFtZSI6eyJkZXNjcmlwdGlvbiI6Ik5hbWUgb2YgdGhlIFBvc3RncmVTUUwgRGF0YWJhc2UgdG8gYmUgY3JlYXRlZC4gRGVmYXVsdCBpcyAnZ2l0ZWFkYicuIiwidHlwZSI6InN0cmluZyJ9LCJwb3N0Z3Jlc3FsSW1hZ2UiOnsiZGVzY3JpcHRpb24iOiJDb250YWluZXIgaW1hZ2UgZm9yIHRoZSBQb3N0Z3JlU1FMIGRhdGFiYXNlLiBEZWZhdWx0IGlzICdyZWdpc3RyeS5yZWRoYXQuaW8vcmhlbDgvcG9zdGdyZXNxbC0xMicuIiwidHlwZSI6InN0cmluZyJ9LCJwb3N0Z3Jlc3FsSW1hZ2VQdWxsUG9saWN5Ijp7ImRlc2NyaXB0aW9uIjoiUHVsbCBwb2xpY3kgZm9yIHRoZSBQb3N0Z3JlU1FMIGNvbnRhaW5lciBpbWFnZS4gRGVmYXVsdCBpcyAnSWZOb3RQcmVzZW50Jy4iLCJ0eXBlIjoic3RyaW5nIn0sInBvc3RncmVzcWxJbWFnZVRhZyI6eyJkZXNjcmlwdGlvbiI6IkltYWdlIHRhZyBmb3IgdGhlIFBvc3RncmVTUUwgY29udGFpbmVyIGltYWdlLiBEZWZhdWx0IGlzICdsYXRlc3QnLiIsInR5cGUiOiJzdHJpbmcifSwicG9zdGdyZXNxbE1lbW9yeUxpbWl0Ijp7ImRlc2NyaXB0aW9uIjoiTWVtb3J5IGxpbWl0IGZvciB0aGUgUG9zdGdyZVNRTCBkYXRhYmFzZS4gRGVmYXVsdCBpcyAnNTEyTWknLiIsInR5cGUiOiJzdHJpbmcifSwicG9zdGdyZXNxbE1lbW9yeVJlcXVlc3QiOnsiZGVzY3JpcHRpb24iOiJNZW1vcnkgcmVxdWVzdCBmb3IgdGhlIFBvc3RncmVTUUwgZGF0YWJhc2UuIERlZmF1bHQgaXMgJzUxMk1pJy4iLCJ0eXBlIjoic3RyaW5nIn0sInBvc3RncmVzcWxQYXNzd29yZCI6eyJkZXNjcmlwdGlvbiI6IlBhc3N3b3JkIHRvIGJlIHVzZWQgZm9yIHRoZSBQb3N0Z3JlU1FMIGRhdGFiYXNlIHVzZXIuIERlZmF1bHQgaXMgJ2dpdGVhcGFzc3dvcmQnLiIsInR5cGUiOiJzdHJpbmcifSwicG9zdGdyZXNxbFNlcnZpY2VOYW1lIjp7ImRlc2NyaXB0aW9uIjoiTmFtZSBvZiB0aGUgUG9zdGdyZVNRTCBkYXRhYmFzZSBzZXJ2aWNlLiBEZWZhdWx0IGlzICdwb3N0Z3Jlc3FsLScgZm9sbG93ZWQgYnkgdGhlIG5hbWUgb2YgdGhlIEdpdGVhIHJlc291cmNlLiIsInR5cGUiOiJzdHJpbmcifSwicG9zdGdyZXNxbFNldHVwIjp7ImRlc2NyaXB0aW9uIjoiU2V0IHVwIGEgUG9zdGdyZVNRTCBkYXRhYmFzZSBhbG9uZ3NpZGUgdGhlIEdpdGVhIGluc3RhbmNlLiBEZWZhdWx0IGlzIHRydWUuIElmIHNldCB0byBmYWxzZSB0aGUgdmFsdWVzIGZvciBnaXRlYVBvc3RncmVzcWxTZXJ2aWNlTmFtZSwgZ2l0ZWFQb3N0Z3Jlc3FsRGF0YWJhc2VOYW1lLCBnaXRlYVBvc3RncmVzcWxVc2VyIGFuZCBnaXRlYVBvc3RncmVzcWxQYXNzd29yZCBuZWVkIHRvIGJlIHNwZWNpZmllZCB0byBjb25uZWN0IHRvIGFuIGV4aXN0aW5nIFBvc3RncmVTUUwgZGF0YWJhc2UuIElmIHNldCB0byB0cnVlIG5vIHZhbHVlcyBuZWVkIHRvIGJlIHNwZWNpZmllZCBmb3IgZGF0YWJhc2UgbmFtZSwgZGF0YWJhc2Ugc2VydmljZSwgZGF0YWJhc2UgdXNlciBhbmQgZGF0YWJhc2Ugc2VydmljZS4iLCJ0eXBlIjoiYm9vbGVhbiJ9LCJwb3N0Z3Jlc3FsVXNlciI6eyJkZXNjcmlwdGlvbiI6IlVzZXJuYW1lIHRvIGJlIGNyZWF0ZWQgaW4gdGhlIFBvc3RncmVTUUwgZGF0YWJhc2UuIERlZmF1bHQgaXMgJ2dpdGVhdXNlcicuIiwidHlwZSI6InN0cmluZyJ9LCJwb3N0Z3Jlc3FsVm9sdW1lU2l6ZSI6eyJkZXNjcmlwdGlvbiI6IlNpemUgb2YgdGhlIHBlcnNpc3RlbnQgdm9sdW1lIGNsYWltIGZvciB0aGUgUG9zdGdyZVNRTCBkYXRhYmFzZS4gRGVmYXVsdCBpcyAnNEdpJy4iLCJ0eXBlIjoic3RyaW5nIn0sInBvc3RncmVzcWxWb2x1bWVTdG9yYWdlQ2xhc3MiOnsiZGVzY3JpcHRpb24iOiJTdG9yYWdlIENsYXNzIHRvIGJlIHVzZWQgZm9yIHRoZSBQb3N0Z3JlU1FMIHBlcnNpc3RlbnQgdm9sdW1lIGNsYWltLiBEZWZhdWx0IGlzIGVtcHR5IC0gd2hpY2ggd2lsbCBjcmVhdGUgYSBQVkMgdXNpbmcgdGhlIGN1cnJlbnRseSBhdmFpbGFibGUgZGVmYXVsdCBzdG9yYWdlIGNsYXNzIG9uIHRoZSBjbHVzdGVyLiIsInR5cGUiOiJzdHJpbmcifX0sInR5cGUiOiJvYmplY3QiLCJ4LWt1YmVybmV0ZXMtcHJlc2VydmUtdW5rbm93bi1maWVsZHMiOnRydWV9LCJzdGF0dXMiOnsiZGVzY3JpcHRpb24iOiJTdGF0dXMgZGVmaW5lcyB0aGUgb2JzZXJ2ZWQgc3RhdGUgb2YgR2l0ZWEiLCJ0eXBlIjoib2JqZWN0IiwieC1rdWJlcm5ldGVzLXByZXNlcnZlLXVua25vd24tZmllbGRzIjp0cnVlfX0sInR5cGUiOiJvYmplY3QifX0sInNlcnZlZCI6dHJ1ZSwic3RvcmFnZSI6dHJ1ZSwic3VicmVzb3VyY2VzIjp7InN0YXR1cyI6e319fV19LCJzdGF0dXMiOnsiYWNjZXB0ZWROYW1lcyI6eyJraW5kIjoiIiwicGx1cmFsIjoiIn0sImNvbmRpdGlvbnMiOm51bGwsInN0b3JlZFZlcnNpb25zIjpudWxsfX0= 18 | - type: olm.bundle.object 19 | value: 20 | data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsiYWxtLWV4YW1wbGVzIjoiW1xuICB7XG4gICAgXCJhcGlWZXJzaW9uXCI6IFwicGZlLnJocGRzLmNvbS92MVwiLFxuICAgIFwia2luZFwiOiBcIkdpdGVhXCIsXG4gICAgXCJtZXRhZGF0YVwiOiB7XG4gICAgICBcImxhYmVsc1wiOiB7XG4gICAgICAgIFwiYXBwLmt1YmVybmV0ZXMuaW8vbWFuYWdlZC1ieVwiOiBcImt1c3RvbWl6ZVwiLFxuICAgICAgICBcImFwcC5rdWJlcm5ldGVzLmlvL25hbWVcIjogXCJnaXRlYS1vcGVyYXRvclwiXG4gICAgICB9LFxuICAgICAgXCJuYW1lXCI6IFwicmVwb3NpdG9yeVwiXG4gICAgfSxcbiAgICBcInNwZWNcIjoge1xuICAgICAgXCJnaXRlYUltYWdlUHVsbFBvbGljeVwiOiBcIkFsd2F5c1wiLFxuICAgICAgXCJnaXRlYUltYWdlVGFnXCI6IFwiMS4yMy43XCIsXG4gICAgICBcImdpdGVhU3NsXCI6IHRydWUsXG4gICAgICBcImdpdGVhVm9sdW1lU2l6ZVwiOiBcIjRHaVwiLFxuICAgICAgXCJwb3N0Z3Jlc3FsVm9sdW1lU2l6ZVwiOiBcIjRHaVwiXG4gICAgfVxuICB9XG5dIiwiY2FwYWJpbGl0aWVzIjoiQmFzaWMgSW5zdGFsbCIsImNyZWF0ZWRBdCI6IjIwMjUtMDUtMTVUMTA6Mjc6NDNaIiwib3BlcmF0b3JzLm9wZXJhdG9yZnJhbWV3b3JrLmlvL2J1aWxkZXIiOiJvcGVyYXRvci1zZGstdjEuMzkuMiIsIm9wZXJhdG9ycy5vcGVyYXRvcmZyYW1ld29yay5pby9wcm9qZWN0X2xheW91dCI6ImFuc2libGUuc2RrLm9wZXJhdG9yZnJhbWV3b3JrLmlvL3YxIn0sIm5hbWUiOiJnaXRlYS1vcGVyYXRvci52Mi4xLjAiLCJuYW1lc3BhY2UiOiJwbGFjZWhvbGRlciJ9LCJzcGVjIjp7ImFwaXNlcnZpY2VkZWZpbml0aW9ucyI6e30sImN1c3RvbXJlc291cmNlZGVmaW5pdGlvbnMiOnsib3duZWQiOlt7ImtpbmQiOiJHaXRlYSIsIm5hbWUiOiJnaXRlYS5wZmUucmhwZHMuY29tIiwidmVyc2lvbiI6InYxIn1dfSwiZGVzY3JpcHRpb24iOiJHaXRlYSBPcGVyYXRvciAtIHByb3ZpZGVkIGJ5IFJlZCBIYXQgRGVtbyBQbGF0Zm9ybSwgc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9yaHBkcy9naXRlYS1vcGVyYXRvciBmb3IgZG9jdW1lbnRhdGlvbi4iLCJkaXNwbGF5TmFtZSI6IkdpdGVhIE9wZXJhdG9yIiwiaWNvbiI6W3siYmFzZTY0ZGF0YSI6IlBEOTRiV3dnZG1WeWMybHZiajBpTVM0d0lpQmxibU52WkdsdVp6MGlkWFJtTFRnaVB6NEtQSE4yWnlCMlpYSnphVzl1UFNJeExqRWlJR2xrUFNKdFlXbHVYMjkxZEd4cGJtVWlJSGh0Ykc1elBTSm9kSFJ3T2k4dmQzZDNMbmN6TG05eVp5OHlNREF3TDNOMlp5SWdlRzFzYm5NNmVHeHBibXM5SW1oMGRIQTZMeTkzZDNjdWR6TXViM0puTHpFNU9Ua3ZlR3hwYm1zaUlIZzlJakJ3ZUNJS0NTQjVQU0l3Y0hnaUlIWnBaWGRDYjNnOUlqQWdNQ0EyTkRBZ05qUXdJaUJ6ZEhsc1pUMGlaVzVoWW14bExXSmhZMnRuY205MWJtUTZibVYzSURBZ01DQTJOREFnTmpRd095SWdlRzFzT25Od1lXTmxQU0p3Y21WelpYSjJaU0krQ2p4blBnb0pQSEJoZEdnZ2FXUTlJblJsWVdKaFp5SWdjM1I1YkdVOUltWnBiR3c2STBaR1JrWkdSaUlnWkQwaVRUTTVOUzQ1TERRNE5DNHliQzB4TWpZdU9TMDJNV010TVRJdU5TMDJMVEUzTGprdE1qRXVNaTB4TVM0NExUTXpMamhzTmpFdE1USTJMamxqTmkweE1pNDFMREl4TGpJdE1UY3VPU3d6TXk0NExURXhMamdLQ1Fsak1UY3VNaXc0TGpNc01qY3VNU3d4TXl3eU55NHhMREV6YkMwd0xqRXRNVEE1TGpKc01UWXVOeTB3TGpGc01DNHhMREV4Tnk0eFl6QXNNQ3cxTnk0MExESTBMaklzT0RNdU1TdzBNQzR4WXpNdU55d3lMak1zTVRBdU1pdzJMamdzTVRJdU9Td3hOQzQwQ2drSll6SXVNU3cyTGpFc01pd3hNeTR4TFRFc01Ua3VNMnd0TmpFc01USTJMamxETkRJekxqWXNORGcwTGprc05EQTRMalFzTkRrd0xqTXNNemsxTGprc05EZzBMako2SWk4K0NnazhaejRLQ1FrOFp6NEtDUWtKUEhCaGRHZ2djM1I1YkdVOUltWnBiR3c2SXpZd09Ua3lOaUlnWkQwaVRUWXlNaTQzTERFME9TNDRZeTAwTGpFdE5DNHhMVGt1TmkwMExUa3VOaTAwY3kweE1UY3VNaXcyTGpZdE1UYzNMamtzT0dNdE1UTXVNeXd3TGpNdE1qWXVOU3d3TGpZdE16a3VOaXd3TGpkak1Dd3pPUzR4TERBc056Z3VNaXd3TERFeE55NHlDZ2tKQ1FsakxUVXVOUzB5TGpZdE1URXVNUzAxTGpNdE1UWXVOaTAzTGpsak1DMHpOaTQwTFRBdU1TMHhNRGt1TWkwd0xqRXRNVEE1TGpKakxUSTVMREF1TkMwNE9TNHlMVEl1TWkwNE9TNHlMVEl1TW5NdE1UUXhMalF0Tnk0eExURTFOaTQ0TFRndU5Rb0pDUWtKWXkwNUxqZ3RNQzQyTFRJeUxqVXRNaTR4TFRNNUxERXVOV010T0M0M0xERXVPQzB6TXk0MUxEY3VOQzAxTXk0NExESTJMamxETFRRdU9Td3lNVEl1TkN3MkxqWXNNamMyTGpJc09Dd3lPRFV1T0dNeExqY3NNVEV1Tnl3Mkxqa3NORFF1TWl3ek1TNDNMRGN5TGpVS0NRa0pDV00wTlM0NExEVTJMakVzTVRRMExqUXNOVFF1T0N3eE5EUXVOQ3cxTkM0NGN6RXlMakVzTWpndU9Td3pNQzQyTERVMUxqVmpNalVzTXpNdU1TdzFNQzQzTERVNExqa3NOelV1Tnl3Mk1tTTJNeXd3TERFNE9DNDVMVEF1TVN3eE9EZ3VPUzB3TGpGek1USXNNQzR4TERJNExqTXRNVEF1TXdvSkNRa0pZekUwTFRndU5Td3lOaTQxTFRJekxqUXNNall1TlMweU15NDBjekV5TGprdE1UTXVPQ3d6TUM0NUxUUTFMak5qTlM0MUxUa3VOeXd4TUM0eExURTVMakVzTVRRdU1TMHlPR013TERBc05UVXVNaTB4TVRjdU1TdzFOUzR5TFRJek1TNHhDZ2tKQ1FsRE5qTXpMaklzTVRVM0xqa3NOakkwTGpjc01UVXhMamdzTmpJeUxqY3NNVFE1TGpoNklFMHhNalV1Tml3ek5UTXVPV010TWpVdU9TMDRMalV0TXpZdU9TMHhPQzQzTFRNMkxqa3RNVGd1TjFNMk9TNDJMRE15TVM0NExEWXdMREk1TlM0MENna0pDUWxqTFRFMkxqVXRORFF1TWkweExqUXROekV1TWkweExqUXROekV1TW5NNExqUXRNakl1TlN3ek9DNDFMVE13WXpFekxqZ3RNeTQzTERNeExUTXVNU3d6TVMwekxqRnpOeTR4TERVNUxqUXNNVFV1Tnl3NU5DNHlZemN1TWl3eU9TNHlMREkwTGpnc056Y3VOeXd5TkM0NExEYzNMamNLQ1FrSkNWTXhOREl1TlN3ek5Ua3VPU3d4TWpVdU5pd3pOVE11T1hvZ1RUUXlOUzQ1TERRMk1TNDFZekFzTUMwMkxqRXNNVFF1TlMweE9TNDJMREUxTGpSakxUVXVPQ3d3TGpRdE1UQXVNeTB4TGpJdE1UQXVNeTB4TGpKekxUQXVNeTB3TGpFdE5TNHpMVEl1TVd3dE1URXlMamt0TlRVS0NRa0pDV013TERBdE1UQXVPUzAxTGpjdE1USXVPQzB4TlM0Mll5MHlMakl0T0M0eExESXVOeTB4T0M0eExESXVOeTB4T0M0eFRETXlNaXd5TnpOak1Dd3dMRFF1T0MwNUxqY3NNVEl1TWkweE0yTXdMall0TUM0ekxESXVNeTB4TERRdU5TMHhMalZqT0M0eExUSXVNU3d4T0N3eUxqZ3NNVGdzTWk0NENna0pDUWxzTVRFd0xqY3NOVE11TjJNd0xEQXNNVEl1Tml3MUxqY3NNVFV1TXl3eE5pNHlZekV1T1N3M0xqUXRNQzQxTERFMExURXVPQ3d4Tnk0eVF6UTNOQzQyTERNMk15NDRMRFF5TlM0NUxEUTJNUzQxTERReU5TNDVMRFEyTVM0MWVpSXZQZ29KQ1FrOGNHRjBhQ0J6ZEhsc1pUMGlabWxzYkRvak5qQTVPVEkySWlCa1BTSk5NekkyTGpnc016Z3dMakZqTFRndU1pd3dMakV0TVRVdU5DdzFMamd0TVRjdU15d3hNeTQ0WXkweExqa3NPQ3d5TERFMkxqTXNPUzR4TERJd1l6Y3VOeXcwTERFM0xqVXNNUzQ0TERJeUxqY3ROUzQwQ2drSkNRbGpOUzR4TFRjdU1TdzBMak10TVRZdU9TMHhMamd0TWpNdU1Xd3lOQzAwT1M0eFl6RXVOU3d3TGpFc015NDNMREF1TWl3MkxqSXRNQzQxWXpRdU1TMHdMamtzTnk0eExUTXVOaXczTGpFdE15NDJZelF1TWl3eExqZ3NPQzQyTERNdU9Dd3hNeTR5TERZdU1Rb0pDUWtKWXpRdU9Dd3lMalFzT1M0ekxEUXVPU3d4TXk0MExEY3VNMk13TGprc01DNDFMREV1T0N3eExqRXNNaTQ0TERFdU9XTXhMallzTVM0ekxETXVOQ3d6TGpFc05DNDNMRFV1TldNeExqa3NOUzQxTFRFdU9Td3hOQzQ1TFRFdU9Td3hOQzQ1Q2drSkNRbGpMVEl1TXl3M0xqWXRNVGd1TkN3ME1DNDJMVEU0TGpRc05EQXVObU10T0M0eExUQXVNaTB4TlM0ekxEVXRNVGN1Tnl3eE1pNDFZeTB5TGpZc09DNHhMREV1TVN3eE55NHpMRGd1T1N3eU1TNHpZemN1T0N3MExERTNMalFzTVM0M0xESXlMalV0TlM0ekNna0pDUWxqTlMwMkxqZ3NOQzQyTFRFMkxqTXRNUzR4TFRJeUxqWmpNUzQ1TFRNdU55d3pMamN0Tnk0MExEVXVOaTB4TVM0ell6VXRNVEF1TkN3eE15NDFMVE13TGpRc01UTXVOUzB6TUM0MFl6QXVPUzB4TGpjc05TNDNMVEV3TGpNc01pNDNMVEl4TGpNS0NRa0pDV010TWk0MUxURXhMalF0TVRJdU5pMHhOaTQzTFRFeUxqWXRNVFl1TjJNdE1USXVNaTAzTGprdE1qa3VNaTB4TlM0eUxUSTVMakl0TVRVdU1uTXdMVFF1TVMweExqRXROeTR4WXkweExqRXRNeTR4TFRJdU9DMDFMakV0TXk0NUxUWXVNMk0wTGpjdE9TNDNMRGt1TkMweE9TNHpMREUwTGpFdE1qa0tDUWtKQ1dNdE5DNHhMVEl0T0M0eExUUXRNVEl1TWkwMkxqRmpMVFF1T0N3NUxqZ3RPUzQzTERFNUxqY3RNVFF1TlN3eU9TNDFZeTAyTGpjdE1DNHhMVEV5TGprc015NDFMVEUyTGpFc09TNDBZeTB6TGpRc05pNHpMVEl1Tnl3eE5DNHhMREV1T1N3eE9TNDRDZ2tKQ1FsRE16UXpMaklzTXpRMkxqVXNNek0xTERNMk15NHpMRE15Tmk0NExETTRNQzR4ZWlJdlBnb0pDVHd2Wno0S0NUd3ZaejRLUEM5blBnbzhMM04yWno0SyIsIm1lZGlhdHlwZSI6ImltYWdlL3N2Zyt4bWwifV0sImluc3RhbGwiOnsic3BlYyI6eyJjbHVzdGVyUGVybWlzc2lvbnMiOlt7InJ1bGVzIjpbeyJhcGlHcm91cHMiOlsiIl0sInJlc291cmNlcyI6WyJzZWNyZXRzIiwicG9kcyIsInBvZHMvZXhlYyIsInBvZHMvbG9nIl0sInZlcmJzIjpbImNyZWF0ZSIsImRlbGV0ZSIsImdldCIsImxpc3QiLCJwYXRjaCIsInVwZGF0ZSIsIndhdGNoIl19LHsiYXBpR3JvdXBzIjpbImFwcHMiXSwicmVzb3VyY2VzIjpbImRlcGxveW1lbnRzIiwiZGFlbW9uc2V0cyIsInJlcGxpY2FzZXRzIiwic3RhdGVmdWxzZXRzIl0sInZlcmJzIjpbImNyZWF0ZSIsImRlbGV0ZSIsImdldCIsImxpc3QiLCJwYXRjaCIsInVwZGF0ZSIsIndhdGNoIl19LHsiYXBpR3JvdXBzIjpbInBmZS5yaHBkcy5jb20iXSwicmVzb3VyY2VzIjpbImdpdGVhIiwiZ2l0ZWEvc3RhdHVzIiwiZ2l0ZWEvZmluYWxpemVycyJdLCJ2ZXJicyI6WyJjcmVhdGUiLCJkZWxldGUiLCJnZXQiLCJsaXN0IiwicGF0Y2giLCJ1cGRhdGUiLCJ3YXRjaCJdfSx7ImFwaUdyb3VwcyI6WyIiXSwicmVzb3VyY2VzIjpbInNlcnZpY2VhY2NvdW50cyIsInBlcnNpc3RlbnR2b2x1bWVjbGFpbXMiLCJjb25maWdtYXBzIiwic2VydmljZXMiXSwidmVyYnMiOlsiY3JlYXRlIiwiZGVsZXRlIiwiZ2V0IiwibGlzdCIsInBhdGNoIiwidXBkYXRlIiwid2F0Y2giXX0seyJhcGlHcm91cHMiOlsicm91dGUub3BlbnNoaWZ0LmlvIl0sInJlc291cmNlcyI6WyJyb3V0ZXMiLCJyb3V0ZXMvY3VzdG9tLWhvc3QiXSwidmVyYnMiOlsiY3JlYXRlIiwiZGVsZXRlIiwiZ2V0IiwibGlzdCIsInBhdGNoIiwidXBkYXRlIiwid2F0Y2giXX0seyJhcGlHcm91cHMiOlsiYXV0aGVudGljYXRpb24uazhzLmlvIl0sInJlc291cmNlcyI6WyJ0b2tlbnJldmlld3MiXSwidmVyYnMiOlsiY3JlYXRlIl19LHsiYXBpR3JvdXBzIjpbImF1dGhvcml6YXRpb24uazhzLmlvIl0sInJlc291cmNlcyI6WyJzdWJqZWN0YWNjZXNzcmV2aWV3cyJdLCJ2ZXJicyI6WyJjcmVhdGUiXX1dLCJzZXJ2aWNlQWNjb3VudE5hbWUiOiJnaXRlYS1vcGVyYXRvci1jb250cm9sbGVyLW1hbmFnZXIifV0sImRlcGxveW1lbnRzIjpbeyJsYWJlbCI6eyJhcHAua3ViZXJuZXRlcy5pby9tYW5hZ2VkLWJ5Ijoia3VzdG9taXplIiwiYXBwLmt1YmVybmV0ZXMuaW8vbmFtZSI6ImdpdGVhLW9wZXJhdG9yIiwiY29udHJvbC1wbGFuZSI6ImNvbnRyb2xsZXItbWFuYWdlciJ9LCJuYW1lIjoiZ2l0ZWEtb3BlcmF0b3ItY29udHJvbGxlci1tYW5hZ2VyIiwic3BlYyI6eyJyZXBsaWNhcyI6MSwic2VsZWN0b3IiOnsibWF0Y2hMYWJlbHMiOnsiY29udHJvbC1wbGFuZSI6ImNvbnRyb2xsZXItbWFuYWdlciJ9fSwic3RyYXRlZ3kiOnt9LCJ0ZW1wbGF0ZSI6eyJtZXRhZGF0YSI6eyJhbm5vdGF0aW9ucyI6eyJrdWJlY3RsLmt1YmVybmV0ZXMuaW8vZGVmYXVsdC1jb250YWluZXIiOiJtYW5hZ2VyIn0sImxhYmVscyI6eyJjb250cm9sLXBsYW5lIjoiY29udHJvbGxlci1tYW5hZ2VyIn19LCJzcGVjIjp7ImNvbnRhaW5lcnMiOlt7ImFyZ3MiOlsiLS1tZXRyaWNzLXJlcXVpcmUtcmJhYyIsIi0tbWV0cmljcy1zZWN1cmUiLCItLW1ldHJpY3MtYmluZC1hZGRyZXNzPTo4NDQzIiwiLS1sZWFkZXItZWxlY3QiLCItLWxlYWRlci1lbGVjdGlvbi1pZD1naXRlYS1vcGVyYXRvciIsIi0taGVhbHRoLXByb2JlLWJpbmQtYWRkcmVzcz06Njc4OSJdLCJlbnYiOlt7Im5hbWUiOiJBTlNJQkxFX0dBVEhFUklORyIsInZhbHVlIjoiZXhwbGljaXQifV0sImltYWdlIjoicXVheS5pby9yaHBkcy9naXRlYS1vcGVyYXRvcjp2Mi4xLjAiLCJsaXZlbmVzc1Byb2JlIjp7Imh0dHBHZXQiOnsicGF0aCI6Ii9oZWFsdGh6IiwicG9ydCI6Njc4OX0sImluaXRpYWxEZWxheVNlY29uZHMiOjE1LCJwZXJpb2RTZWNvbmRzIjoyMH0sIm5hbWUiOiJtYW5hZ2VyIiwicmVhZGluZXNzUHJvYmUiOnsiaHR0cEdldCI6eyJwYXRoIjoiL3JlYWR5eiIsInBvcnQiOjY3ODl9LCJpbml0aWFsRGVsYXlTZWNvbmRzIjo1LCJwZXJpb2RTZWNvbmRzIjoxMH0sInJlc291cmNlcyI6eyJsaW1pdHMiOnsiY3B1IjoiMSIsIm1lbW9yeSI6IjJHaSJ9LCJyZXF1ZXN0cyI6eyJjcHUiOiIyMDBtIiwibWVtb3J5IjoiMUdpIn19LCJzZWN1cml0eUNvbnRleHQiOnsiYWxsb3dQcml2aWxlZ2VFc2NhbGF0aW9uIjpmYWxzZSwiY2FwYWJpbGl0aWVzIjp7ImRyb3AiOlsiQUxMIl19fX1dLCJzZWN1cml0eUNvbnRleHQiOnsicnVuQXNOb25Sb290Ijp0cnVlfSwic2VydmljZUFjY291bnROYW1lIjoiZ2l0ZWEtb3BlcmF0b3ItY29udHJvbGxlci1tYW5hZ2VyIiwidGVybWluYXRpb25HcmFjZVBlcmlvZFNlY29uZHMiOjEwfX19fV0sInBlcm1pc3Npb25zIjpbeyJydWxlcyI6W3siYXBpR3JvdXBzIjpbIiJdLCJyZXNvdXJjZXMiOlsiY29uZmlnbWFwcyJdLCJ2ZXJicyI6WyJnZXQiLCJsaXN0Iiwid2F0Y2giLCJjcmVhdGUiLCJ1cGRhdGUiLCJwYXRjaCIsImRlbGV0ZSJdfSx7ImFwaUdyb3VwcyI6WyJjb29yZGluYXRpb24uazhzLmlvIl0sInJlc291cmNlcyI6WyJsZWFzZXMiXSwidmVyYnMiOlsiZ2V0IiwibGlzdCIsIndhdGNoIiwiY3JlYXRlIiwidXBkYXRlIiwicGF0Y2giLCJkZWxldGUiXX0seyJhcGlHcm91cHMiOlsiIl0sInJlc291cmNlcyI6WyJldmVudHMiXSwidmVyYnMiOlsiY3JlYXRlIiwicGF0Y2giXX1dLCJzZXJ2aWNlQWNjb3VudE5hbWUiOiJnaXRlYS1vcGVyYXRvci1jb250cm9sbGVyLW1hbmFnZXIifV19LCJzdHJhdGVneSI6ImRlcGxveW1lbnQifSwiaW5zdGFsbE1vZGVzIjpbeyJzdXBwb3J0ZWQiOmZhbHNlLCJ0eXBlIjoiT3duTmFtZXNwYWNlIn0seyJzdXBwb3J0ZWQiOmZhbHNlLCJ0eXBlIjoiU2luZ2xlTmFtZXNwYWNlIn0seyJzdXBwb3J0ZWQiOmZhbHNlLCJ0eXBlIjoiTXVsdGlOYW1lc3BhY2UifSx7InN1cHBvcnRlZCI6dHJ1ZSwidHlwZSI6IkFsbE5hbWVzcGFjZXMifV0sImtleXdvcmRzIjpbImdpdGVhIiwicmVwb3NpdG9yeSJdLCJsaW5rcyI6W3sibmFtZSI6IkdpdGVhIE9wZXJhdG9yIiwidXJsIjoiaHR0cHM6Ly9naXRlYS1vcGVyYXRvci5kb21haW4ifV0sIm1haW50YWluZXJzIjpbeyJlbWFpbCI6IndrdWxoYW5lQHJlZGhhdC5jb20iLCJuYW1lIjoiV29sZmdhbmcgS3VsaGFuZWsifV0sIm1hdHVyaXR5Ijoic3RhYmxlIiwicHJvdmlkZXIiOnsibmFtZSI6IlJlZCBIYXQgUG9ydGZvbGlvIFRlY2huaWNhbCBNYXJrZXRpbmcgYW5kIFBsYXRmb3JtcyJ9LCJyZXBsYWNlcyI6ImdpdGVhLW9wZXJhdG9yLnYxLjEuMCIsInZlcnNpb24iOiIyLjEuMCJ9fQ== 21 | - type: olm.bundle.object 22 | value: 23 | data: eyJhcGlWZXJzaW9uIjoicmJhYy5hdXRob3JpemF0aW9uLms4cy5pby92MSIsImtpbmQiOiJDbHVzdGVyUm9sZSIsIm1ldGFkYXRhIjp7ImNyZWF0aW9uVGltZXN0YW1wIjpudWxsLCJsYWJlbHMiOnsiYXBwLmt1YmVybmV0ZXMuaW8vbWFuYWdlZC1ieSI6Imt1c3RvbWl6ZSIsImFwcC5rdWJlcm5ldGVzLmlvL25hbWUiOiJnaXRlYS1vcGVyYXRvciJ9LCJuYW1lIjoiZ2l0ZWEtb3BlcmF0b3ItZ2l0ZWEtZWRpdG9yLXJvbGUifSwicnVsZXMiOlt7ImFwaUdyb3VwcyI6WyJwZmUucmhwZHMuY29tIl0sInJlc291cmNlcyI6WyJnaXRlYSJdLCJ2ZXJicyI6WyJjcmVhdGUiLCJkZWxldGUiLCJnZXQiLCJsaXN0IiwicGF0Y2giLCJ1cGRhdGUiLCJ3YXRjaCJdfSx7ImFwaUdyb3VwcyI6WyJwZmUucmhwZHMuY29tIl0sInJlc291cmNlcyI6WyJnaXRlYS9zdGF0dXMiXSwidmVyYnMiOlsiZ2V0Il19XX0= 24 | - type: olm.bundle.object 25 | value: 26 | data: eyJhcGlWZXJzaW9uIjoicmJhYy5hdXRob3JpemF0aW9uLms4cy5pby92MSIsImtpbmQiOiJDbHVzdGVyUm9sZSIsIm1ldGFkYXRhIjp7ImNyZWF0aW9uVGltZXN0YW1wIjpudWxsLCJsYWJlbHMiOnsiYXBwLmt1YmVybmV0ZXMuaW8vbWFuYWdlZC1ieSI6Imt1c3RvbWl6ZSIsImFwcC5rdWJlcm5ldGVzLmlvL25hbWUiOiJnaXRlYS1vcGVyYXRvciJ9LCJuYW1lIjoiZ2l0ZWEtb3BlcmF0b3ItZ2l0ZWEtdmlld2VyLXJvbGUifSwicnVsZXMiOlt7ImFwaUdyb3VwcyI6WyJwZmUucmhwZHMuY29tIl0sInJlc291cmNlcyI6WyJnaXRlYSJdLCJ2ZXJicyI6WyJnZXQiLCJsaXN0Iiwid2F0Y2giXX0seyJhcGlHcm91cHMiOlsicGZlLnJocGRzLmNvbSJdLCJyZXNvdXJjZXMiOlsiZ2l0ZWEvc3RhdHVzIl0sInZlcmJzIjpbImdldCJdfV19 27 | - type: olm.bundle.object 28 | value: 29 | data: eyJhcGlWZXJzaW9uIjoicmJhYy5hdXRob3JpemF0aW9uLms4cy5pby92MSIsImtpbmQiOiJDbHVzdGVyUm9sZSIsIm1ldGFkYXRhIjp7ImNyZWF0aW9uVGltZXN0YW1wIjpudWxsLCJuYW1lIjoiZ2l0ZWEtb3BlcmF0b3ItbWV0cmljcy1yZWFkZXIifSwicnVsZXMiOlt7Im5vblJlc291cmNlVVJMcyI6WyIvbWV0cmljcyJdLCJ2ZXJicyI6WyJnZXQiXX1dfQ== 30 | - type: olm.bundle.object 31 | value: 32 | data: eyJhcGlWZXJzaW9uIjoidjEiLCJraW5kIjoiU2VydmljZSIsIm1ldGFkYXRhIjp7ImNyZWF0aW9uVGltZXN0YW1wIjpudWxsLCJsYWJlbHMiOnsiYXBwLmt1YmVybmV0ZXMuaW8vbWFuYWdlZC1ieSI6Imt1c3RvbWl6ZSIsImFwcC5rdWJlcm5ldGVzLmlvL25hbWUiOiJnaXRlYS1vcGVyYXRvciIsImNvbnRyb2wtcGxhbmUiOiJjb250cm9sbGVyLW1hbmFnZXIifSwibmFtZSI6ImdpdGVhLW9wZXJhdG9yLWNvbnRyb2xsZXItbWFuYWdlci1tZXRyaWNzLXNlcnZpY2UifSwic3BlYyI6eyJwb3J0cyI6W3sibmFtZSI6Imh0dHBzIiwicG9ydCI6ODQ0MywicHJvdG9jb2wiOiJUQ1AiLCJ0YXJnZXRQb3J0Ijo4NDQzfV0sInNlbGVjdG9yIjp7ImNvbnRyb2wtcGxhbmUiOiJjb250cm9sbGVyLW1hbmFnZXIifX0sInN0YXR1cyI6eyJsb2FkQmFsYW5jZXIiOnt9fX0= 33 | relatedImages: 34 | - image: quay.io/rhpds/gitea-operator-bundle:v2.1.0 35 | name: "" 36 | - image: quay.io/rhpds/gitea-operator:v2.1.0 37 | name: "" 38 | schema: olm.bundle 39 | 40 | --- 41 | schema: olm.package 42 | name: gitea-operator 43 | defaultChannel: stable 44 | --- 45 | schema: olm.channel 46 | package: gitea-operator 47 | name: stable 48 | entries: 49 | - name: gitea-operator.v2.1.0 50 | 51 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | collections: 7 | - kubernetes.core 8 | 9 | tasks: 10 | - name: Create Namespace 11 | k8s: 12 | api_version: v1 13 | kind: Namespace 14 | name: '{{ namespace }}' 15 | 16 | - import_tasks: kustomize.yml 17 | vars: 18 | state: present 19 | -------------------------------------------------------------------------------- /molecule/default/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | tasks: [] 7 | -------------------------------------------------------------------------------- /molecule/default/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - kubernetes.core 8 | 9 | tasks: 10 | - import_tasks: kustomize.yml 11 | vars: 12 | state: absent 13 | 14 | - name: Destroy Namespace 15 | k8s: 16 | api_version: v1 17 | kind: Namespace 18 | name: '{{ namespace }}' 19 | state: absent 20 | 21 | - name: Unset pull policy 22 | command: '{{ kustomize }} edit remove patch pull_policy/{{ operator_pull_policy }}.yaml' 23 | args: 24 | chdir: '{{ config_dir }}/testing' 25 | -------------------------------------------------------------------------------- /molecule/default/kustomize.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Build kustomize testing overlay 3 | # load_restrictor must be set to none so we can load patch files from the default overlay 4 | command: '{{ kustomize }} build --load-restrictor LoadRestrictionsNone' 5 | args: 6 | chdir: '{{ config_dir }}/testing' 7 | register: resources 8 | changed_when: false 9 | 10 | - name: Set resources to {{ state }} 11 | k8s: 12 | definition: '{{ item }}' 13 | state: '{{ state }}' 14 | wait: no 15 | loop: '{{ resources.stdout | from_yaml_all | list }}' 16 | 17 | - name: Wait for resources to get to {{ state }} 18 | k8s: 19 | definition: '{{ item }}' 20 | state: '{{ state }}' 21 | wait: yes 22 | loop: '{{ resources.stdout | from_yaml_all | list }}' 23 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: delegated 6 | platforms: 7 | - name: cluster 8 | groups: 9 | - k8s 10 | provisioner: 11 | name: ansible 12 | inventory: 13 | group_vars: 14 | all: 15 | namespace: ${TEST_OPERATOR_NAMESPACE:-osdk-test} 16 | host_vars: 17 | localhost: 18 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 19 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 20 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 21 | operator_image: ${OPERATOR_IMAGE:-""} 22 | operator_pull_policy: ${OPERATOR_PULL_POLICY:-"Always"} 23 | kustomize: ${KUSTOMIZE_PATH:-kustomize} 24 | env: 25 | K8S_AUTH_KUBECONFIG: ${KUBECONFIG:-"~/.kube/config"} 26 | verifier: 27 | name: ansible 28 | -------------------------------------------------------------------------------- /molecule/default/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | 7 | tasks: 8 | - name: Ensure operator image is set 9 | fail: 10 | msg: | 11 | You must specify the OPERATOR_IMAGE environment variable in order to run the 12 | 'default' scenario 13 | when: not operator_image 14 | 15 | - name: Set testing image 16 | command: '{{ kustomize }} edit set image testing={{ operator_image }}' 17 | args: 18 | chdir: '{{ config_dir }}/testing' 19 | 20 | - name: Set pull policy 21 | command: '{{ kustomize }} edit add patch --path pull_policy/{{ operator_pull_policy }}.yaml' 22 | args: 23 | chdir: '{{ config_dir }}/testing' 24 | 25 | - name: Set testing namespace 26 | command: '{{ kustomize }} edit set namespace {{ namespace }}' 27 | args: 28 | chdir: '{{ config_dir }}/testing' 29 | -------------------------------------------------------------------------------- /molecule/default/tasks/gitea_test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create the pfe.rhpds.com/v1.Gitea 3 | k8s: 4 | state: present 5 | namespace: '{{ namespace }}' 6 | definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" 7 | wait: yes 8 | wait_timeout: 300 9 | wait_condition: 10 | type: Successful 11 | status: "True" 12 | vars: 13 | cr_file: 'pfe_v1_gitea.yaml' 14 | 15 | - name: Add assertions here 16 | assert: 17 | that: false 18 | fail_msg: FIXME Add real assertions for your operator 19 | -------------------------------------------------------------------------------- /molecule/default/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Verify 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | collections: 7 | - kubernetes.core 8 | 9 | vars: 10 | ctrl_label: control-plane=controller-manager 11 | 12 | tasks: 13 | - block: 14 | - name: Import all test files from tasks/ 15 | include_tasks: '{{ item }}' 16 | with_fileglob: 17 | - tasks/*_test.yml 18 | rescue: 19 | - name: Retrieve relevant resources 20 | k8s_info: 21 | api_version: '{{ item.api_version }}' 22 | kind: '{{ item.kind }}' 23 | namespace: '{{ namespace }}' 24 | loop: 25 | - api_version: v1 26 | kind: Pod 27 | - api_version: apps/v1 28 | kind: Deployment 29 | - api_version: v1 30 | kind: Secret 31 | - api_version: v1 32 | kind: ConfigMap 33 | register: debug_resources 34 | 35 | - name: Retrieve Pod logs 36 | k8s_log: 37 | name: '{{ item.metadata.name }}' 38 | namespace: '{{ namespace }}' 39 | container: manager 40 | loop: "{{ q('k8s', api_version='v1', kind='Pod', namespace=namespace, label_selector=ctrl_label) }}" 41 | register: debug_logs 42 | 43 | - name: Output gathered resources 44 | debug: 45 | var: debug_resources 46 | 47 | - name: Output gathered logs 48 | debug: 49 | var: item.log_lines 50 | loop: '{{ debug_logs.results }}' 51 | 52 | - name: Re-emit failure 53 | vars: 54 | failed_task: 55 | result: '{{ ansible_failed_result }}' 56 | fail: 57 | msg: '{{ failed_task }}' 58 | -------------------------------------------------------------------------------- /molecule/kind/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | 7 | tasks: 8 | - name: Build operator image 9 | docker_image: 10 | build: 11 | path: '{{ project_dir }}' 12 | pull: no 13 | name: '{{ operator_image }}' 14 | tag: latest 15 | push: no 16 | source: build 17 | force_source: yes 18 | 19 | - name: Load image into kind cluster 20 | command: kind load docker-image --name osdk-test '{{ operator_image }}' 21 | register: result 22 | changed_when: '"not yet present" in result.stdout' 23 | 24 | - import_playbook: ../default/converge.yml 25 | -------------------------------------------------------------------------------- /molecule/kind/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | tasks: 7 | - name: Create test kind cluster 8 | command: kind create cluster --name osdk-test --kubeconfig {{ kubeconfig }} 9 | -------------------------------------------------------------------------------- /molecule/kind/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - kubernetes.core 8 | 9 | tasks: 10 | - name: Destroy test kind cluster 11 | command: kind delete cluster --name osdk-test --kubeconfig {{ kubeconfig }} 12 | 13 | - name: Unset pull policy 14 | command: '{{ kustomize }} edit remove patch pull_policy/{{ operator_pull_policy }}.yaml' 15 | args: 16 | chdir: '{{ config_dir }}/testing' 17 | -------------------------------------------------------------------------------- /molecule/kind/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: delegated 6 | platforms: 7 | - name: cluster 8 | groups: 9 | - k8s 10 | provisioner: 11 | name: ansible 12 | playbooks: 13 | prepare: ../default/prepare.yml 14 | verify: ../default/verify.yml 15 | inventory: 16 | group_vars: 17 | all: 18 | namespace: ${TEST_OPERATOR_NAMESPACE:-osdk-test} 19 | host_vars: 20 | localhost: 21 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 22 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 23 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 24 | project_dir: ${MOLECULE_PROJECT_DIRECTORY} 25 | operator_image: testing-operator 26 | operator_pull_policy: "Never" 27 | kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" 28 | kustomize: ${KUSTOMIZE_PATH:-kustomize} 29 | env: 30 | K8S_AUTH_KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 31 | KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 32 | verifier: 33 | name: ansible 34 | -------------------------------------------------------------------------------- /playbooks/gitea.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Persistent Gitea deployment playbook. 3 | # 4 | # The Playbook expects the following variables to be set in the CR: 5 | # (Note that Camel case gets converted by the ansible-operator to Snake case) 6 | # - PostgresqlVolumeSize 7 | # - GiteaVolumeSize 8 | # - GiteaSSL 9 | # The following variables come from the ansible-operator 10 | # - ansible_operator_meta.namespace 11 | # - ansible_operator_meta.name (from the name of the CR) 12 | 13 | - hosts: localhost 14 | gather_facts: no 15 | collections: 16 | - kubernetes.core 17 | - operator_sdk.util 18 | tasks: 19 | - name: Set up PostgreSQL 20 | when: postgresql_setup | default(true) | bool 21 | ansible.builtin.include_role: 22 | name: ../roles/postgresql-ocp 23 | vars: 24 | _postgresql_namespace: "{{ ansible_operator_meta.namespace }}" 25 | _postgresql_name: "{{ postgresql_service_name | default('postgresql-' + ansible_operator_meta.name) }}" 26 | _postgresql_database_name: "{{ postgresql_database_name | default('giteadb') }}" 27 | _postgresql_user: "{{ postgresql_user | default('giteauser') }}" 28 | _postgresql_password: "{{ postgresql_password | default('giteapassword') }}" 29 | _postgresql_volume_size: "{{ postgresql_volume_size | default('4Gi') }}" 30 | _postgresql_volume_storage_class: "{{ postgresql_volume_storage_class | default('') }}" 31 | _postgresql_image: "{{ postgresql_image | default('registry.redhat.io/rhel8/postgresql-12') }}" 32 | _postgresql_image_tag: "{{ postgresql_image_tag | default('latest') }}" 33 | _postgresql_image_pull_policy: "{{ postgresql_image_pull_policy | default('IfNotPresent') }}" 34 | _postgresql_memory_request: "{{ postgresql_memory_request | default('512Mi') }}" 35 | _postgresql_memory_limit: "{{ postgresql_memory_limit | default('512Mi') }}" 36 | _postgresql_cpu_request: "{{ postgresql_cpu_request | default('200m') }}" 37 | _postgresql_cpu_limit: "{{ postgresql_cpu_limit | default('500m') }}" 38 | 39 | - name: Set up Gitea 40 | ansible.builtin.include_role: 41 | name: ../roles/gitea-ocp 42 | vars: 43 | _gitea_namespace: "{{ ansible_operator_meta.namespace }}" 44 | _gitea_name: "{{ gitea_service_name | default(ansible_operator_meta.name) }}" 45 | _gitea_image: "{{ gitea_image | default('quay.io/rhpds/gitea') }}" 46 | _gitea_image_tag: "{{ gitea_image_tag | default('latest') }}" 47 | _gitea_image_pull_policy: "{{ gitea_image_pull_policy | default('IfNotPresent') }}" 48 | _gitea_volume_size: "{{ gitea_volume_size | default('4Gi') }}" 49 | _gitea_volume_storage_class: "{{ gitea_volume_storage_class | default('') }}" 50 | _gitea_memory_request: "{{ gitea_memory_request | default('1Gi') }}" 51 | _gitea_memory_limit: "{{ gitea_memory_limit | default('1Gi') }}" 52 | _gitea_cpu_request: "{{ gitea_cpu_request | default('200m') }}" 53 | _gitea_cpu_limit: "{{ gitea_cpu_limit | default('500m') }}" 54 | 55 | _gitea_config_map_name: "{{ gitea_config_map_name | default('') }}" 56 | _gitea_ssl: "{{ gitea_ssl | default(false) | bool }}" 57 | _gitea_hostname: "{{ gitea_hostname | default('') }}" 58 | 59 | _gitea_postgresql_service_name: "{{ gitea_postgresql_service_name | default('postgresql-' + ansible_operator_meta.name) }}" 60 | _gitea_postgresql_database_name: "{{ gitea_postgresql_database_name | default('giteadb') }}" 61 | _gitea_postgresql_user: "{{ gitea_postgresql_user | default('giteauser') }}" 62 | _gitea_postgresql_password: "{{ gitea_postgresql_password | default('giteapassword') }}" 63 | 64 | _gitea_admin_user: "{{ gitea_admin_user | default('') }}" 65 | _gitea_admin_email: "{{ gitea_admin_email | default('notset@notset.org') }}" 66 | _gitea_admin_password_secret_name: "{{ gitea_admin_password_secret_name | default('') }}" 67 | _gitea_admin_password: "{{ gitea_admin_password | default('') }}" 68 | _gitea_admin_password_length: "{{ gitea_admin_password_length | default(16) }}" 69 | 70 | _gitea_create_users: "{{ gitea_create_users | default(false) }}" 71 | _gitea_user_number: "{{ gitea_user_number | default(2) }}" 72 | _gitea_generate_user_format: "{{ gitea_generate_user_format | default('user%d') }}" 73 | _gitea_user_email_domain: "{{ gitea_user_email_domain | default('example.com') }}" 74 | _gitea_user_password_secret_name: "{{ gitea_user_password_secret_name | default('') }}" 75 | _gitea_user_password: "{{ gitea_user_password | default('') }}" 76 | _gitea_user_password_length: "{{ gitea_user_password_length | default(16) }}" 77 | 78 | _gitea_migrate_repositories: "{{ gitea_migrate_repositories | default(false) }}" 79 | _gitea_repositories_list: "{{ gitea_repositories_list | default([]) }}" 80 | 81 | _gitea_http_port: "{{ gitea_http_port | default(3000) }}" 82 | _gitea_ssh_port: "{{ gitea_ssh_port | default(2022) }}" 83 | _gitea_disable_ssh: "{{ gitea_disable_ssh | default(true) | bool }}" 84 | _gitea_start_ssh_server: "{{ gitea_start_ssh_server | default(false) | bool }}" 85 | _gitea_start_lfs_server: "{{ gitea_start_lfs_server | default(true) | bool }}" 86 | _gitea_disable_registration: "{{ gitea_disable_registration | default(false) | bool }}" 87 | _gitea_enable_captcha: "{{ gitea_enable_captcha | default(false) | bool }}" 88 | _gitea_allow_create_organization: "{{ gitea_allow_create_organization | default(true) | bool }}" 89 | _gitea_allow_local_network_migration: "{{ gitea_allow_local_network_migration | default(false) | bool }}" 90 | _gitea_webhook_allowed_host_list: "{{ gitea_webhook_allowed_host_list | default('external,private') }}" 91 | _gitea_webhook_skip_tls_verify: "{{ gitea_webhook_skip_tls_verify | default(false) }}" 92 | 93 | _gitea_mailer_enabled: "{{ gitea_mailer_enabled | default(false) | bool }}" 94 | _gitea_mailer_from: "{{ gitea_mailer_from | default('') }}" 95 | _gitea_mailer_protocol: "{{ gitea_mailer_protocol | default('smtps') }}" 96 | _gitea_mailer_host: "{{ gitea_mailer_host | default('') }}" 97 | _gitea_mailer_port: "{{ gitea_mailer_port | default(465) }}" 98 | _gitea_mailer_user: "{{ gitea_mailer_user | default('') }}" 99 | _gitea_mailer_password: "{{ gitea_mailer_password | default('') }}" 100 | _gitea_mailer_helo_hostname: "{{ gitea_mailer_helo_hostname | default('') }}" 101 | _gitea_register_email_confirm: "{{ gitea_register_email_confirm | default(false) | bool }}" 102 | _gitea_enable_notify_mail: "{{ gitea_enable_notify_mail | default(false) | bool }}" 103 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - name: operator_sdk.util 4 | version: "0.5.0" 5 | - name: kubernetes.core 6 | version: "3.2.0" 7 | - name: cloud.common 8 | version: "3.0.0" 9 | - name: community.docker 10 | version: "3.12.1" 11 | -------------------------------------------------------------------------------- /roles/gitea-ocp/README.adoc: -------------------------------------------------------------------------------- 1 | = gitea-ocp 2 | 3 | == Requirements 4 | 5 | This role is designed to set up Gitea ("Git with a cup of Tea") on an OpenShift cluster. The intended use for this role is in an Operator. The role does not set up Kubernetes controllers (like Deployments or ReplicaSets) but creates the Gitea Pod directly - this is the preferred approach to be used by an Operator. 6 | 7 | This role also requires a PostgreSQL Database to be running in the same OpenShift project as the Gitea Server. This database needs to be set up first. 8 | 9 | == Role Variables 10 | 11 | [cols="2,1,1,4",options="header"] 12 | |==== 13 | |Variable Name|Default|Required|Description 14 | |_gitea_namespace|gitea|Yes|Project Name to install Gitea into 15 | |_gitea_name|gitea|Yes|Name of the Gitea service 16 | |_gitea_ssl|False|No|Set up HTTPS for the Gitea Route 17 | |_gitea_postgresql_service_name|postgresql|Yes|Name of the PostgreSQL service to connect (should be listening on port 5432) 18 | |_gitea_postgresql_database_name|postgresql|Yes|Name of Database connect to 19 | |_gitea_postgresql_user|postgresql|Yes|Database User Name 20 | |_gitea_postgresql_password|postgresql|Yes|Database Password 21 | |_gitea_volume_size|1Gi|No|Size of Persistent Volume to be created 22 | |_gitea_memory_request|512Mi|No|Minimum Memory Requirement 23 | |_gitea_memory_limit|512Mi|No|Maximum Memory Requirement 24 | |_gitea_cpu_request|200m|No|Minimum CPU Requirement 25 | |_gitea_cpu_limit|500m|No|Maximum CPU Requirement 26 | |_gitea_wait_for_init|true|No|Wait for the database pod to be running and ready 27 | |_gitea_image|docker.io/wkulhanek/gitea|Yes|Container image for Gitea 28 | |_gitea_image_tag|latest|Yes|Tag for Gitea container image 29 | |==== 30 | 31 | == Dependencies 32 | 33 | * k8s module. 34 | * Running PostgreSQL database listening on port 5432 35 | * Working .kube/config configuration. 36 | * The Project/Namespace must exist 37 | 38 | == Example Playbook 39 | 40 | [source,yaml] 41 | ---- 42 | - hosts: localhost 43 | gather_facts: no 44 | tasks: 45 | - name: Set up PostgreSQL 46 | include_role: 47 | name: ./roles/postgresql-ocp 48 | vars: 49 | _postgresql_namespace: "gitea" 50 | _postgresql_name: "postgresql-gitea" 51 | _postgresql_database_name: "giteadb" 52 | _postgresql_user: "giteauser" 53 | _postgresql_password: "giteapassword" 54 | _postgresql_volume_size: "4Gi" 55 | 56 | - name: Set up gitea 57 | include_role: 58 | name: ./roles/gitea-ocp 59 | vars: 60 | _gitea_namespace: "gitea" 61 | _gitea_name: "gitea" 62 | _gitea_ssl: "True| 63 | _gitea_volume_size: "4Gi" 64 | _gitea_postgresql_service_name: "postgresql-gitea" 65 | _gitea_postgresql_database_name: giteadb 66 | _gitea_postgresql_user: giteauser 67 | _gitea_postgresql_password: giteapassword 68 | ---- 69 | 70 | == License 71 | 72 | BSD 73 | -------------------------------------------------------------------------------- /roles/gitea-ocp/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | _gitea_namespace: gitea 3 | _gitea_name: gitea 4 | 5 | # It is possible to specify a ConfigMap that already exists for Gitea configuration 6 | # The ConfigMap has to have a file `app.ini` in it with the entire Gitea configuration 7 | # This way it is possible to set values that have not been exposed via the operator CR 8 | # If the ConfigMap name is empty (default) the ConfigMap will be generated via the custom 9 | # resource variables specified 10 | # If the ConfigMap is provided then _gitea_hostname and _gitea_ssl also must be passed 11 | # in order for the app.ini to match the route 12 | _gitea_config_map_name: "" 13 | 14 | _gitea_ssl: false 15 | _gitea_port: 3000 16 | _gitea_postgresql_database_name: postgresql 17 | _gitea_postgresql_port: 5432 18 | _gitea_postgresql_service_name: postgresql 19 | _gitea_postgresql_user: postgresql 20 | _gitea_postgresql_password: postgresql 21 | _gitea_volume_size: 1Gi 22 | _gitea_volume_storage_class: "" 23 | _gitea_memory_request: 1Gi 24 | _gitea_memory_limit: 1Gi 25 | _gitea_cpu_request: 200m 26 | _gitea_cpu_limit: 500m 27 | _gitea_image: quay.io/rhpds/gitea 28 | _gitea_image_tag: latest 29 | 30 | # If Admin user is specified the Admin User will 31 | # be created. If no admin user is specified, or empty no admin user will be created 32 | # Password can be set directly via the CR or from a secret that must 33 | # exist in the namespace with key `adminPassword`. 34 | # If no password is specified a password with specified length will be created 35 | # If a secret name is specified that takes precedence over the variable _gitea_admin_password 36 | _gitea_admin_user: opentlc-mgr 37 | _gitea_admin_email: "opentlc-mgr@open.redhat.com" 38 | _gitea_admin_password_secret_name: "" 39 | _gitea_admin_password: "" 40 | _gitea_admin_password_length: 16 41 | 42 | # Create users in Gitea. If set to true an Admin user *must* be created. 43 | _gitea_create_users: false 44 | # Format for the users to create. E.g. user1, user2, ... 45 | # When _gitea_user_number=1 specify just the 46 | # user name: e.g. lab-user 47 | _gitea_user_number: 2 48 | _gitea_generate_user_format: "user%d" 49 | _gitea_user_email_domain: example.com 50 | # Password can be set directly via the CR or from a secret that must 51 | # exist in the namespace with key `userPassword`. 52 | # If no password is specified a password with specified length will be created 53 | # If a secret name is specified that takes precedence over the variable _gitea_user_password 54 | _gitea_user_password_secret_name: "" 55 | _gitea_user_password: "" 56 | _gitea_user_password_length: 16 57 | 58 | # Set up repositories for all created users - also handled 59 | # by the workload, not the operator 60 | _gitea_migrate_repositories: false 61 | _gitea_repositories_list: 62 | - repo: "https://github.com/someuser/thing1" 63 | name: "thing1" 64 | private: "true" 65 | - repo: "https://github.com/anotheruser/someotherthing" 66 | name: "someotherthing" 67 | private: "false" 68 | 69 | # Gitea Settings 70 | _gitea_http_port: 3000 71 | _gitea_ssh_port: 2022 72 | _gitea_disable_ssh: false 73 | _gitea_start_ssh_server: true 74 | _gitea_start_lfs_server: true 75 | _gitea_disable_registration: false 76 | _gitea_enable_captcha: false 77 | _gitea_allow_create_organization: true 78 | _gitea_allow_local_network_migration: false 79 | 80 | # Which targets a webhook is allowed to call 81 | # See https://docs.gitea.com/next/administration/config-cheat-sheet#webhook-webhook 82 | _gitea_webhook_allowed_host_list: "external,private" 83 | _gitea_webhook_skip_tls_verify: false 84 | 85 | # Gitea e-mail Setup 86 | _gitea_mailer_enabled: false 87 | _gitea_mailer_from: gitea@mydomain.com 88 | _gitea_mailer_protocol: smtps 89 | _gitea_mailer_host: mail.mydomain.com 90 | _gitea_mailer_port: 465 91 | _gitea_mailer_user: gitea@mydomain.com 92 | _gitea_mailer_password: password 93 | _gitea_mailer_helo_hostname: "" 94 | 95 | _gitea_register_email_confirm: false 96 | _gitea_enable_notify_mail: false 97 | 98 | # Set to a valid hostname for the cluster to make a nicer route than svc-project.apps. Leave empty for default route 99 | # Must be specified if a configmap name is provided (because the app.ini can not be generated in that case) 100 | _gitea_hostname: "" 101 | 102 | # Internal variables. Do not change 103 | # --------------------------------- 104 | # Actual name of the ConfigMap holding app.ini 105 | _gitea_actual_configmap_name: "" 106 | 107 | # Actual Gitea route 108 | _gitea_actual_route_url: "" 109 | _gitea_actual_route_hostname: "" 110 | 111 | # Actual Gitea admin password 112 | _gitea_actual_admin_password: "" 113 | 114 | # Gitea user name (for use in loops) 115 | _gitea_user_name: "" 116 | 117 | # Actual Gitea user password 118 | _gitea_actual_user_password: "" 119 | -------------------------------------------------------------------------------- /roles/gitea-ocp/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Wolfgang Kulhanek 3 | description: Role to set up Gitea on an OpenShift (Kubernetes) Cluster 4 | company: Red Hat 5 | license: Apache 6 | min_ansible_version: 2.10 7 | galaxy_tags: 8 | - openshift 9 | - kubernetes 10 | - k8s 11 | - ansible-operator 12 | - gitea 13 | - git 14 | - repository 15 | dependencies: [] 16 | -------------------------------------------------------------------------------- /roles/gitea-ocp/tasks/create_user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set the username as a fact for easy reuse (single user) 3 | when: _gitea_user_number | int == 1 4 | ansible.builtin.set_fact: 5 | _gitea_user_name: "{{ _gitea_generate_user_format }}" 6 | 7 | - name: Set the username as a fact for easy reuse (multiple users) 8 | when: _gitea_user_number | int > 1 9 | ansible.builtin.set_fact: 10 | _gitea_user_name: "{{ _gitea_generate_user_format | format( item ) }}" 11 | 12 | - name: Check if user exists 13 | ansible.builtin.uri: 14 | url: "{{ _gitea_actual_route_url }}/api/v1/users/{{ _gitea_user_name }}" 15 | method: GET 16 | validate_certs: false 17 | user: "{{ _gitea_admin_user }}" 18 | password: "{{ _gitea_actual_admin_password }}" 19 | force_basic_auth: true 20 | status_code: [ 200, 404 ] 21 | register: r_gitea_insystem_user 22 | retries: 20 23 | delay: 5 24 | until: r_gitea_insystem_user is succeeded 25 | 26 | - name: Create the user 27 | when: r_gitea_insystem_user.status == 404 28 | ansible.builtin.uri: 29 | url: "{{ _gitea_actual_route_url }}/api/v1/admin/users" 30 | method: POST 31 | body: "{{ body }}" 32 | status_code: 201 33 | body_format: json 34 | validate_certs: false 35 | user: "{{ _gitea_admin_user }}" 36 | password: "{{ _gitea_actual_admin_password }}" 37 | force_basic_auth: true 38 | vars: 39 | body: >- 40 | { 41 | "email": "{{ _gitea_user_name }}@{{ _gitea_user_email_domain }}", 42 | "login_name": "{{ _gitea_user_name }}", 43 | "must_change_password": false, 44 | "password": "{{ _gitea_actual_user_password }}", 45 | "send_notify": false, 46 | "username": "{{ _gitea_user_name }}" 47 | } 48 | register: r_create_user 49 | retries: 20 50 | delay: 5 51 | until: r_create_user.status == 201 52 | -------------------------------------------------------------------------------- /roles/gitea-ocp/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Sanity check to see if hostname is passed if configmap with app.ini is provided 3 | when: _gitea_config_map_name | default("") | length > 0 4 | ansible.builtin.assert: 5 | that: _gitea_hostname | default("") | length > 0 6 | fail_msg: "giteaHostname must be specified if giteaConfigMapName is set." 7 | 8 | - name: Create OpenShift objects for Gitea 9 | kubernetes.core.k8s: 10 | state: present 11 | definition: "{{ lookup('template', item) | from_yaml }}" 12 | loop: 13 | - serviceaccount.yaml.j2 14 | - persistentvolumeclaim.yaml.j2 15 | - service.yaml.j2 16 | - route.yaml.j2 17 | 18 | - name: Get Gitea route hostname 19 | kubernetes.core.k8s_info: 20 | api_version: route.openshift.io/v1 21 | kind: Route 22 | name: "{{ _gitea_name }}" 23 | namespace: "{{ _gitea_namespace }}" 24 | register: r_route 25 | 26 | - name: Store Gitea route hostname 27 | ansible.builtin.set_fact: 28 | _gitea_actual_route_url: "{{ 'http' if not _gitea_ssl | default(false) | bool else 'https' }}://{{ r_route.resources[0].status.ingress[0].host }}" 29 | _gitea_actual_route_hostname: "{{ r_route.resources[0].status.ingress[0].host }}" 30 | 31 | - name: Create ConfigMap with app.ini if not provided 32 | when: _gitea_config_map_name | default('') | length == 0 33 | kubernetes.core.k8s: 34 | state: present 35 | definition: "{{ lookup('template', 'configmap.yaml.j2') | from_yaml }}" 36 | 37 | - name: Create Gitea deployment 38 | kubernetes.core.k8s: 39 | state: present 40 | definition: "{{ lookup('template', 'deployment.yaml.j2') | from_yaml }}" 41 | 42 | - name: Wait until application is available 43 | kubernetes.core.k8s_info: 44 | api_version: apps/v1 45 | kind: Deployment 46 | name: "{{ _gitea_name }}" 47 | namespace: "{{ _gitea_namespace }}" 48 | register: r_deployment 49 | until: 50 | - r_deployment.resources[0].status.availableReplicas is defined 51 | - r_deployment.resources[0].status.availableReplicas == 1 52 | retries: 50 53 | delay: 10 54 | ignore_errors: true 55 | 56 | - name: Save route in custom resource status 57 | operator_sdk.util.k8s_status: 58 | api_version: pfe.rhpds.com/v1 59 | kind: Gitea 60 | name: "{{ _gitea_name }}" 61 | namespace: "{{ _gitea_namespace }}" 62 | status: 63 | giteaRoute: "{{ _gitea_actual_route_url }}" 64 | giteaHostname: "{{ _gitea_actual_route_hostname }}" 65 | 66 | - name: Retrieve Gitea resource to check admin setup 67 | kubernetes.core.k8s_info: 68 | api_version: pfe.rhpds.com/v1 69 | kind: Gitea 70 | name: "{{ _gitea_name }}" 71 | namespace: "{{ _gitea_namespace }}" 72 | register: r_gitea_admin 73 | 74 | - name: Set up admin user 75 | when: 76 | - not r_gitea_admin.resources[0].status.adminSetupComplete | default(false) | bool 77 | - _gitea_admin_user | default("") | length > 0 78 | block: 79 | - name: Check if secret is specified with admin password 80 | when: _gitea_admin_password_secret_name | default("") | length > 0 81 | kubernetes.core.k8s_info: 82 | api_version: v1 83 | kind: Secret 84 | name: "{{ _gitea_admin_password_secret_name }}" 85 | namespace: "{{ _gitea_namespace }}" 86 | register: r_admin_password_secret 87 | 88 | - name: Set admin password from secret 89 | when: 90 | - r_admin_password_secret.resources is defined 91 | - r_admin_password_secret.resources | length > 0 92 | - r_admin_password_secret.resources[0].data is defined 93 | - r_admin_password_secret.resources[0].data.adminPassword is defined 94 | - r_admin_password_secret.resources[0].data.adminPassword | length > 0 95 | ansible.builtin.set_fact: 96 | _gitea_actual_admin_password: "{{ r_admin_password_secret.resources[0].data.adminPassword | b64decode }}" 97 | 98 | - name: Set admin password 99 | when: 100 | - _gitea_admin_password_secret_name | default("") | length == 0 101 | - _gitea_admin_password | default("") | length > 0 102 | ansible.builtin.set_fact: 103 | _gitea_actual_admin_password: "{{ _gitea_admin_password }}" 104 | 105 | - name: Generate admin password 106 | when: 107 | - _gitea_admin_password_secret_name | default("") | length == 0 108 | - _gitea_admin_password | default("") | length == 0 109 | ansible.builtin.set_fact: 110 | _gitea_actual_admin_password: >- 111 | {{ lookup('password', '/dev/null length={{ _gitea_admin_password_length | default( 16 ) }} chars=ascii_letters,digits') }} 112 | 113 | - name: Check if Gitea admin user already exists 114 | ansible.builtin.uri: 115 | url: "{{ _gitea_actual_route_url }}/api/v1/users/{{ _gitea_admin_user }}" 116 | method: GET 117 | validate_certs: false 118 | status_code: 200, 404 119 | register: r_gitea_admin_user 120 | retries: 20 121 | delay: 5 122 | until: r_gitea_admin_user.status == 200 or r_gitea_admin_user.status == 404 123 | failed_when: r_gitea_admin_user.status != 200 and r_gitea_admin_user.status != 404 124 | 125 | - name: Create Gitea admin user 126 | when: r_gitea_admin_user.status == 404 127 | block: 128 | - name: Retrieve Gitea pod 129 | kubernetes.core.k8s_info: 130 | kind: Pod 131 | namespace: "{{ _gitea_namespace }}" 132 | label_selectors: 133 | - app = {{ _gitea_name }} 134 | register: r_gitea_pod 135 | 136 | - name: Create Gitea admin user 137 | kubernetes.core.k8s_exec: 138 | namespace: "{{ _gitea_namespace }}" 139 | pod: "{{ r_gitea_pod.resources[0].metadata.name }}" 140 | command: >- 141 | /usr/bin/giteacmd admin user create 142 | --username {{ _gitea_admin_user }} 143 | --password {{ _gitea_actual_admin_password }} 144 | --email {{ _gitea_admin_email }} 145 | --must-change-password=false 146 | --admin 147 | register: r_create_admin_user 148 | 149 | - name: Save admin password and status in custom resource status 150 | when: r_create_admin_user.return_code == 0 151 | operator_sdk.util.k8s_status: 152 | api_version: pfe.rhpds.com/v1 153 | kind: Gitea 154 | name: "{{ _gitea_name }}" 155 | namespace: "{{ _gitea_namespace }}" 156 | status: 157 | adminPassword: "{{ _gitea_actual_admin_password }}" 158 | adminSetupComplete: true 159 | 160 | - name: Retrieve Gitea resource to check user setup 161 | when: _gitea_create_users | bool 162 | kubernetes.core.k8s_info: 163 | api_version: pfe.rhpds.com/v1 164 | kind: Gitea 165 | name: "{{ _gitea_name }}" 166 | namespace: "{{ _gitea_namespace }}" 167 | register: r_gitea_users 168 | 169 | - name: Sanity check to only create users when admin user created 170 | when: 171 | - _gitea_create_users | bool 172 | - _gitea_admin_user | default("") | length == 0 173 | operator_sdk.util.k8s_status: 174 | api_version: pfe.rhpds.com/v1 175 | kind: Gitea 176 | name: "{{ _gitea_name }}" 177 | namespace: "{{ _gitea_namespace }}" 178 | status: 179 | userSetupError: "Error, can not create users without admin user." 180 | userSetupComplete: true 181 | 182 | - name: Set up users in Gitea (only if admin user has been created also) 183 | when: 184 | - _gitea_create_users | bool 185 | - _gitea_admin_user | default("") | length > 0 186 | - _gitea_user_number | default(0) | int > 0 187 | - r_gitea_users.resources[0].status.adminPassword | default("") | length > 0 188 | - not r_gitea_users.resources[0].status.userSetupComplete | default(false) | bool 189 | block: 190 | - name: Set actual admin password 191 | ansible.builtin.set_fact: 192 | _gitea_actual_admin_password: "{{ r_gitea_users.resources[0].status.adminPassword }}" 193 | 194 | - name: Check if secret is specified with user password 195 | when: _gitea_user_password_secret_name | default("") | length > 0 196 | kubernetes.core.k8s_info: 197 | api_version: v1 198 | kind: Secret 199 | name: "{{ _gitea_user_password_secret_name }}" 200 | namespace: "{{ _gitea_namespace }}" 201 | register: r_user_password_secret 202 | 203 | - name: Set user password from secret 204 | when: 205 | - r_user_password_secret.resources is defined 206 | - r_user_password_secret.resources | length > 0 207 | - r_user_password_secret.resources[0].data is defined 208 | - r_user_password_secret.resources[0].data.userPassword is defined 209 | - r_user_password_secret.resources[0].data.userPassword | length > 0 210 | ansible.builtin.set_fact: 211 | _gitea_actual_user_password: "{{ r_user_password_secret.resources[0].data.userPassword | b64decode }}" 212 | 213 | - name: Set user password 214 | when: 215 | - _gitea_user_password_secret_name | default("") | length == 0 216 | - _gitea_user_password | default("") | length > 0 217 | ansible.builtin.set_fact: 218 | _gitea_actual_user_password: "{{ _gitea_user_password }}" 219 | 220 | - name: Generate user password 221 | when: 222 | - _gitea_user_password_secret_name | default("") | length == 0 223 | - _gitea_user_password | default("") | length == 0 224 | ansible.builtin.set_fact: 225 | _gitea_actual_user_password: >- 226 | {{ lookup('password', '/dev/null length={{ _gitea_user_password_length | default( 16 ) }} chars=ascii_letters,digits') }} 227 | 228 | - name: Create the users in Gitea 229 | ansible.builtin.include_tasks: create_user.yml 230 | loop: "{{ range(1, _gitea_user_number | int + 1) | list }}" 231 | 232 | - name: Save user password and status in custom resource status 233 | operator_sdk.util.k8s_status: 234 | api_version: pfe.rhpds.com/v1 235 | kind: Gitea 236 | name: "{{ _gitea_name }}" 237 | namespace: "{{ _gitea_namespace }}" 238 | status: 239 | userPassword: "{{ _gitea_actual_user_password }}" 240 | userSetupComplete: true 241 | 242 | - name: Retrieve Gitea resource to check repo status 243 | kubernetes.core.k8s_info: 244 | api_version: pfe.rhpds.com/v1 245 | kind: Gitea 246 | name: "{{ _gitea_name }}" 247 | namespace: "{{ _gitea_namespace }}" 248 | register: r_gitea_repos 249 | 250 | - name: Migrate repositories only if users and admin user have been created 251 | when: 252 | - _gitea_create_users | bool 253 | - _gitea_admin_user | default("") | length > 0 254 | - _gitea_user_number | default(0) | int > 0 255 | - _gitea_migrate_repositories | bool 256 | - r_gitea_repos.resources[0].status.userSetupComplete | default(false) | bool 257 | - r_gitea_repos.resources[0].status.adminPassword | default("") | length > 0 258 | - not r_gitea_repos.resources[0].status.repoMigrationComplete | default(false) | bool 259 | block: 260 | - name: Set actual admin password 261 | ansible.builtin.set_fact: 262 | _gitea_actual_admin_password: "{{ r_gitea_repos.resources[0].status.adminPassword }}" 263 | 264 | - name: Migrate repositories 265 | ansible.builtin.include_tasks: migrate_repos.yml 266 | loop: >- 267 | {{ 268 | ( range(1, _gitea_user_number | int + 1) | list ) 269 | | product(_gitea_repositories_list) 270 | | list 271 | }} 272 | 273 | - name: Save repo migration status in custom resource status 274 | operator_sdk.util.k8s_status: 275 | api_version: pfe.rhpds.com/v1 276 | kind: Gitea 277 | name: "{{ _gitea_name }}" 278 | namespace: "{{ _gitea_namespace }}" 279 | status: 280 | repoMigrationComplete: true 281 | -------------------------------------------------------------------------------- /roles/gitea-ocp/tasks/migrate_repos.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # item[0] has the number for the username 3 | # item[1]['repo'] has the repo url 4 | # item[1]['name'] has the repo name 5 | # item[1]['private'] is a boolean flag to make the repo private 6 | - name: Set the username as a fact for easy reuse (single user) 7 | when: _gitea_user_number | int == 1 8 | ansible.builtin.set_fact: 9 | _gitea_user_name: "{{ _gitea_generate_user_format }}" 10 | 11 | - name: Set the username as a fact for easy reuse (multiple users) 12 | when: _gitea_user_number | int > 1 13 | ansible.builtin.set_fact: 14 | _gitea_user_name: "{{ _gitea_generate_user_format | format(item[0]) }}" 15 | 16 | - name: Check if the repository exists 17 | ansible.builtin.uri: 18 | url: "{{ _gitea_actual_route_url }}/api/v1/repos/{{ _gitea_user_name }}/{{ item[1]['name'] }}" 19 | method: GET 20 | validate_certs: false 21 | user: "{{ _gitea_admin_user }}" 22 | password: "{{ _gitea_actual_admin_password }}" 23 | force_basic_auth: true 24 | status_code: 200, 404 25 | register: r_gitea_repo_exists 26 | # ignore_errors: true 27 | 28 | - name: Gitea Repository does not exist yet 29 | when: r_gitea_repo_exists.status == 404 30 | block: 31 | - name: Get the Gitea user info to obtain the uid 32 | ansible.builtin.uri: 33 | url: "{{ _gitea_actual_route_url }}/api/v1/users/{{ _gitea_user_name }}" 34 | method: GET 35 | validate_certs: false 36 | user: "{{ _gitea_admin_user }}" 37 | password: "{{ _gitea_actual_admin_password }}" 38 | force_basic_auth: true 39 | register: r_gitea_insystem_user 40 | 41 | - name: Create the specified repository for the user 42 | ansible.builtin.uri: 43 | url: "{{ _gitea_actual_route_url }}/api/v1/repos/migrate" 44 | method: POST 45 | body: "{{ body }}" 46 | status_code: 201 47 | body_format: json 48 | validate_certs: false 49 | user: "{{ _gitea_admin_user }}" 50 | password: "{{ _gitea_actual_admin_password }}" 51 | force_basic_auth: true 52 | timeout: 60 53 | vars: 54 | body: >- 55 | { 56 | "clone_addr": "{{ item[1]['repo'] }}", 57 | "description": "", 58 | "issues": false, 59 | "milestones": false, 60 | "mirror": false, 61 | "private": {{ item[1]['private'] | bool }}, 62 | "repo_name": "{{ item[1]['name'] }}", 63 | "uid": {{ r_gitea_insystem_user.json.id | int }} 64 | } 65 | -------------------------------------------------------------------------------- /roles/gitea-ocp/templates/configmap.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: ConfigMap 3 | apiVersion: v1 4 | metadata: 5 | name: "{{ _gitea_name }}-config" 6 | namespace: "{{ _gitea_namespace }}" 7 | data: 8 | app.ini: | 9 | APP_NAME = {{ _gitea_name }} 10 | RUN_MODE = prod 11 | RUN_USER = gitea 12 | 13 | [security] 14 | INTERNAL_TOKEN = ${GITEA_INTERNAL_TOKEN} 15 | INSTALL_LOCK = true 16 | SECRET_KEY = ${GITEA_SECRET_KEY} 17 | PASSWORD_COMPLEXITY = off 18 | 19 | [oauth2] 20 | ENABLED = false 21 | 22 | [database] 23 | DB_TYPE = postgres 24 | HOST = {{ _gitea_postgresql_service_name }}:{{ _gitea_postgresql_port }} 25 | NAME = {{ _gitea_postgresql_database_name }} 26 | USER = {{ _gitea_postgresql_user }} 27 | PASSWD = {{ _gitea_postgresql_password }} 28 | SSL_MODE = disable 29 | 30 | [repository] 31 | ROOT = /gitea-repositories 32 | 33 | [server] 34 | ROOT_URL = {{ _gitea_actual_route_url }}/ 35 | SSH_DOMAIN = {{ _gitea_actual_route_hostname }} 36 | DOMAIN = {{ _gitea_actual_route_hostname }} 37 | HTTP_PORT = {{ _gitea_http_port }} 38 | SSH_PORT = {{ _gitea_ssh_port }} 39 | DISABLE_SSH = {{ _gitea_disable_ssh | bool }} 40 | START_SSH_SERVER = {{ _gitea_start_ssh_server | bool }} 41 | LFS_START_SERVER = {{ _gitea_start_lfs_server | bool }} 42 | OFFLINE_MODE = False 43 | 44 | {% if _gitea_allow_local_network_migration | bool %} 45 | [migrations] 46 | ALLOW_LOCALNETWORKS = true 47 | 48 | {% endif %} 49 | [mailer] 50 | ENABLED = {{ _gitea_mailer_enabled | bool }} 51 | {% if _gitea_mailer_enabled | bool %} 52 | FROM = {{ _gitea_mailer_from }} 53 | PROTOCOL = {{ _gitea_mailer_protocol }} 54 | SMTP_ADDR = {{ _gitea_mailer_host }} 55 | SMTP_PORT = {{ _gitea_mailer_port }} 56 | USER = {{ _gitea_mailer_user }} 57 | PASSWD = `{{ _gitea_mailer_password }}` 58 | HELO_HOSTNAME = {{ _gitea_mailer_helo_hostname }} 59 | 60 | {% endif %} 61 | [service] 62 | REGISTER_EMAIL_CONFIRM = {{ _gitea_register_email_confirm | bool }} 63 | ENABLE_NOTIFY_MAIL = {{ _gitea_enable_notify_mail | bool }} 64 | DISABLE_REGISTRATION = {{ _gitea_disable_registration | bool }} 65 | ENABLE_CAPTCHA = {{ _gitea_enable_captcha | bool }} 66 | REQUIRE_SIGNIN_VIEW = false 67 | DEFAULT_KEEP_EMAIL_PRIVATE = false 68 | DEFAULT_ALLOW_CREATE_ORGANIZATION = {{ _gitea_allow_create_organization | bool }} 69 | DEFAULT_ENABLE_TIMETRACKING = true 70 | NO_REPLY_ADDRESS = noreply.example.org 71 | 72 | [picture] 73 | DISABLE_GRAVATAR = false 74 | ENABLE_FEDERATED_AVATAR = true 75 | 76 | [openid] 77 | ENABLE_OPENID_SIGNIN = false 78 | ENABLE_OPENID_SIGNUP = false 79 | 80 | [webhook] 81 | ALLOWED_HOST_LIST = {{ _gitea_webhook_allowed_host_list }} 82 | SKIP_TLS_VERIFY = {{ _gitea_webhook_skip_tls_verify | bool }} 83 | 84 | [session] 85 | PROVIDER = file 86 | 87 | [log] 88 | MODE = file 89 | LEVEL = Info 90 | ROOT_PATH = /home/gitea/log 91 | 92 | [markup.asciidoc] 93 | ENABLED = true 94 | FILE_EXTENSIONS = .adoc,.asciidoc 95 | RENDER_COMMAND = "asciidoc --backend=xhtml11 --no-header-footer --attribute source-highlighter=source-highlight --out-file=- -" 96 | IS_INPUT_FILE = false 97 | -------------------------------------------------------------------------------- /roles/gitea-ocp/templates/deployment.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: "{{ _gitea_name }}" 6 | namespace: "{{ _gitea_namespace }}" 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: "{{ _gitea_name }}" 12 | strategy: 13 | type: Recreate 14 | template: 15 | metadata: 16 | labels: 17 | app: "{{ _gitea_name }}" 18 | spec: 19 | serviceAccountName: "{{ _gitea_name }}" 20 | containers: 21 | - name: gitea 22 | image: "{{ _gitea_image }}:{{ _gitea_image_tag }}" 23 | imagePullPolicy: "{{ _gitea_image_pull_policy }}" 24 | ports: 25 | - containerPort: 3000 26 | protocol: TCP 27 | readinessProbe: 28 | httpGet: 29 | path: / 30 | port: 3000 31 | scheme: HTTP 32 | initialDelaySeconds: 15 33 | timeoutSeconds: 1 34 | periodSeconds: 20 35 | successThreshold: 1 36 | failureThreshold: 3 37 | livenessProbe: 38 | httpGet: 39 | path: / 40 | port: 3000 41 | scheme: HTTP 42 | initialDelaySeconds: 15 43 | timeoutSeconds: 1 44 | periodSeconds: 10 45 | successThreshold: 1 46 | failureThreshold: 3 47 | resources: 48 | requests: 49 | cpu: "{{ _gitea_cpu_request }}" 50 | memory: "{{ _gitea_memory_request}}" 51 | limits: 52 | cpu: "{{ _gitea_cpu_limit}}" 53 | memory: "{{ _gitea_memory_limit }}" 54 | volumeMounts: 55 | - name: gitea-repositories 56 | mountPath: /gitea-repositories 57 | - name: gitea-config 58 | mountPath: /home/gitea/conf-import 59 | volumes: 60 | - name: gitea-repositories 61 | persistentVolumeClaim: 62 | claimName: "{{ _gitea_name }}-pvc" 63 | - name: gitea-config 64 | configMap: 65 | {% if _gitea_config_map_name | default('') | length > 0 %} 66 | name: "{{ _gitea_config_map_name }}" 67 | {% else %} 68 | name: "{{ _gitea_name }}-config" 69 | {% endif %} 70 | items: 71 | - key: app.ini 72 | path: app.ini -------------------------------------------------------------------------------- /roles/gitea-ocp/templates/persistentvolumeclaim.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: PersistentVolumeClaim 3 | apiVersion: v1 4 | metadata: 5 | name: "{{ _gitea_name }}-pvc" 6 | namespace: "{{ _gitea_namespace }}" 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: "{{ _gitea_volume_size }}" 13 | {% if _gitea_volume_storage_class | length > 0 %} 14 | storageClassName: "{{ _gitea_volume_storage_class }}" 15 | {% endif %} 16 | volumeMode: Filesystem -------------------------------------------------------------------------------- /roles/gitea-ocp/templates/route.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Route 3 | apiVersion: route.openshift.io/v1 4 | metadata: 5 | name: {{ _gitea_name }} 6 | namespace: {{ _gitea_namespace }} 7 | labels: 8 | app: {{ _gitea_name }} 9 | spec: 10 | to: 11 | kind: Service 12 | name: {{ _gitea_name }} 13 | {% if _gitea_hostname | default("") | length > 0 %} 14 | host: {{ _gitea_hostname }} 15 | {% endif %} 16 | {% if _gitea_ssl | default(false) | bool %} 17 | tls: 18 | insecureEdgeTerminationPolicy: Redirect 19 | termination: edge 20 | {% endif %} 21 | -------------------------------------------------------------------------------- /roles/gitea-ocp/templates/service.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: Service 3 | apiVersion: v1 4 | metadata: 5 | name: "{{ _gitea_name }}" 6 | namespace: "{{ _gitea_namespace }}" 7 | labels: 8 | app: "{{ _gitea_name }}" 9 | spec: 10 | selector: 11 | app: "{{ _gitea_name }}" 12 | ports: 13 | - name: gitea 14 | port: 3000 15 | protocol: TCP 16 | targetPort: 3000 17 | sessionAffinity: None 18 | type: ClusterIP 19 | -------------------------------------------------------------------------------- /roles/gitea-ocp/templates/serviceaccount.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: ServiceAccount 3 | apiVersion: v1 4 | metadata: 5 | name: "{{ _gitea_name }}" 6 | namespace: "{{ _gitea_namespace }}" 7 | -------------------------------------------------------------------------------- /roles/gitea-ocp/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | _gitea_actual_route_url: "" 3 | _gitea_actual_route_hostname: "" 4 | _gitea_actual_admin_password: "" 5 | _gitea_actual_user_password: "" 6 | -------------------------------------------------------------------------------- /roles/postgresql-ocp/README.adoc: -------------------------------------------------------------------------------- 1 | postgresql-ocp 2 | ============== 3 | 4 | Requirements 5 | ------------ 6 | 7 | This role is designed to set up PostgreSQL on an OpenShift cluster. The intended use for this role is in an Operator. The role does not set up Kubernetes controllers (like Deployments or ReplicaSets) but creates the PostgreSQL Pod directly - this is the preferred approach to be used by an Operator. 8 | 9 | Role Variables 10 | -------------- 11 | 12 | [cols="2,1,1,4",options="header"] 13 | |==== 14 | |Variable Name|Default|Required|Description 15 | |_postgresql_namespace|postgresql|Yes|Project Name to install the database into 16 | |_postgresql_name|postgresql|No|Name of the database service 17 | |_postgresql_database_name|postgresql|No|Name of Database to be created 18 | |_postgresql_user|postgresql|No|Database User Name 19 | |_postgresql_password|postgresql|No|Database Password 20 | |_postgresql_volume_size|1Gi|No|Size of Persistent Volume to be created 21 | |_postgresql_volume_storage_class|""|Storage class to use for the PostgreSQL volume. Default storage class is used when empty 22 | |_postgresql_memory_request|512Mi|No|Minimum Memory Requirement 23 | |_postgresql_memory_limit|512Mi|No|Maximum Memory Requirement 24 | |_postgresql_cpu_request|200m|No|Minimum CPU Requirement 25 | |_postgresql_cpu_limit|500m|No|Maximum CPU Requirement 26 | |_postgresql_wait_for_init|true|No|Wait for the database pod to be running and ready 27 | |_postgresql_image|registry.redhat.io/rhscl/postgresql-96-rhel7|Yes|The image to use for PostgreSQL 28 | |_postgresql_image_tag|latest|Yes|The image tag to use for PostgreSQL 29 | |==== 30 | 31 | Dependencies 32 | ------------ 33 | 34 | * k8s module. 35 | * Working .kube/config configuration. 36 | * The Project/Namespace must exist 37 | 38 | Example Playbook 39 | ---------------- 40 | 41 | [source,yaml] 42 | ---- 43 | - hosts: localhost 44 | gather_facts: no 45 | tasks: 46 | - name: Set up PostgreSQL 47 | include_role: 48 | name: ./roles/postgresql-ocp 49 | vars: 50 | _postgresql_namespace: "postgresql" 51 | _postgresql_name: "postgresql" 52 | _postgresql_database_name: "gogsdb" 53 | _postgresql_user: "gogsuser" 54 | _postgresql_password: "gogspassword" 55 | _postgresql_volume_size: "4Gi" 56 | ---- 57 | 58 | License 59 | ------- 60 | 61 | BSD 62 | -------------------------------------------------------------------------------- /roles/postgresql-ocp/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | _postgresql_namespace: postgresql 3 | _postgresql_name: postgresql 4 | 5 | _postgresql_database_name: postgresql 6 | _postgresql_user: postgresql 7 | _postgresql_password: postgresql 8 | _postgresql_volume_size: 1Gi 9 | _postgresql_volume_storage_class: "" 10 | _postgresql_memory_request: 512Mi 11 | _postgresql_memory_limit: 512Mi 12 | _postgresql_cpu_request: 200m 13 | _postgresql_cpu_limit: 500m 14 | 15 | _postgresql_wait_for_init: true 16 | _postgresql_image: registry.redhat.io/rhel8/postgresql-12 17 | _postgresql_image_tag: latest 18 | -------------------------------------------------------------------------------- /roles/postgresql-ocp/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Wolfgang Kulhanek 3 | description: Role to set up PostgreSQL on an OpenShift (Kubernetes) Cluster 4 | company: Red Hat 5 | license: Apache 6 | min_ansible_version: 2.10 7 | galaxy_tags: 8 | - openshift 9 | - kubernetes 10 | - k8s 11 | - ansible-operator 12 | - postgresql 13 | - database 14 | dependencies: [] 15 | -------------------------------------------------------------------------------- /roles/postgresql-ocp/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create OpenShift objects for PostgreSQL 3 | kubernetes.core.k8s: 4 | state: present 5 | definition: "{{ lookup('template', item ) | from_yaml }}" 6 | loop: 7 | - secret.yaml.j2 8 | - service.yaml.j2 9 | - persistentvolumeclaim.yaml.j2 10 | - deployment.yaml.j2 11 | 12 | - name: Wait until application is available 13 | when: _postgresql_wait_for_init|bool 14 | kubernetes.core.k8s_info: 15 | api_version: apps/v1 16 | kind: Deployment 17 | name: "{{ _postgresql_name }}" 18 | namespace: "{{ _postgresql_namespace }}" 19 | register: r_deployment 20 | until: 21 | - r_deployment.resources[0].status.availableReplicas is defined 22 | - r_deployment.resources[0].status.availableReplicas == 1 23 | retries: 50 24 | delay: 10 25 | -------------------------------------------------------------------------------- /roles/postgresql-ocp/templates/deployment.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: "{{ _postgresql_name }}" 6 | namespace: "{{ _postgresql_namespace }}" 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | name: "{{ _postgresql_name }}" 12 | strategy: 13 | type: Recreate 14 | template: 15 | metadata: 16 | labels: 17 | name: "{{ _postgresql_name }}" 18 | spec: 19 | containers: 20 | - env: 21 | - name: POSTGRESQL_USER 22 | valueFrom: 23 | secretKeyRef: 24 | key: database-user 25 | name: "{{ _postgresql_name }}" 26 | - name: POSTGRESQL_PASSWORD 27 | valueFrom: 28 | secretKeyRef: 29 | key: database-password 30 | name: "{{ _postgresql_name }}" 31 | - name: POSTGRESQL_DATABASE 32 | valueFrom: 33 | secretKeyRef: 34 | key: database-name 35 | name: "{{ _postgresql_name }}" 36 | image: "{{ _postgresql_image }}:{{ _postgresql_image_tag }}" 37 | imagePullPolicy: "{{ _postgresql_image_pull_policy }}" 38 | livenessProbe: 39 | exec: 40 | command: 41 | - /usr/libexec/check-container 42 | - --live 43 | failureThreshold: 3 44 | initialDelaySeconds: 120 45 | periodSeconds: 10 46 | successThreshold: 1 47 | timeoutSeconds: 10 48 | name: postgresql 49 | ports: 50 | - containerPort: 5432 51 | protocol: TCP 52 | readinessProbe: 53 | exec: 54 | command: 55 | - /usr/libexec/check-container 56 | failureThreshold: 3 57 | initialDelaySeconds: 5 58 | periodSeconds: 10 59 | successThreshold: 1 60 | timeoutSeconds: 1 61 | resources: 62 | requests: 63 | cpu: "{{ _postgresql_cpu_request }}" 64 | memory: "{{ _postgresql_memory_request }}" 65 | limits: 66 | cpu: "{{ _postgresql_cpu_limit }}" 67 | memory: "{{ _postgresql_memory_limit }}" 68 | securityContext: 69 | capabilities: {} 70 | privileged: false 71 | terminationMessagePath: /dev/termination-log 72 | terminationMessagePolicy: File 73 | volumeMounts: 74 | - mountPath: /var/lib/pgsql/data 75 | name: postgresql-data 76 | dnsPolicy: ClusterFirst 77 | restartPolicy: Always 78 | schedulerName: default-scheduler 79 | securityContext: {} 80 | terminationGracePeriodSeconds: 30 81 | volumes: 82 | - name: postgresql-data 83 | persistentVolumeClaim: 84 | claimName: "{{ _postgresql_name }}-pvc" 85 | -------------------------------------------------------------------------------- /roles/postgresql-ocp/templates/persistentvolumeclaim.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: "{{ _postgresql_name }}-pvc" 6 | namespace: "{{ _postgresql_namespace }}" 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: "{{ _postgresql_volume_size }}" 13 | {% if _postgresql_volume_storage_class | length > 0 %} 14 | storageClassName: "{{ _postgresql_volume_storage_class }}" 15 | {% endif %} 16 | volumeMode: Filesystem -------------------------------------------------------------------------------- /roles/postgresql-ocp/templates/secret.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: "{{ _postgresql_name }}" 6 | namespace: "{{ _postgresql_namespace }}" 7 | data: 8 | database-name: "{{ _postgresql_database_name | b64encode }}" 9 | database-user: "{{ _postgresql_user | b64encode }}" 10 | database-password: "{{ _postgresql_password | b64encode }}" 11 | -------------------------------------------------------------------------------- /roles/postgresql-ocp/templates/service.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "{{ _postgresql_name }}" 6 | namespace: "{{ _postgresql_namespace }}" 7 | spec: 8 | ports: 9 | - name: postgresql 10 | port: 5432 11 | protocol: TCP 12 | targetPort: 5432 13 | selector: 14 | name: "{{ _postgresql_name }}" 15 | sessionAffinity: None 16 | type: ClusterIP 17 | -------------------------------------------------------------------------------- /watches.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use the 'create api' subcommand to add watches to this file. 3 | - version: v1 4 | group: pfe.rhpds.com 5 | kind: Gitea 6 | playbook: playbooks/gitea.yml 7 | # +kubebuilder:scaffold:watch 8 | --------------------------------------------------------------------------------