├── docs ├── index.md ├── roles │ ├── redis.md │ ├── postgres.md │ ├── galaxy-api.md │ ├── galaxy-common.md │ ├── galaxy-web.md │ ├── galaxybackup.md │ ├── galaxyrestore.md │ ├── galaxy-route.md │ ├── galaxy-status.md │ ├── galaxy-worker.md │ └── galaxy-content.md ├── requirements.txt ├── images │ └── galaxy.png ├── contributing.md ├── development.md ├── container.md └── maintainers │ └── release.md ├── roles ├── galaxy-config │ ├── defaults │ │ └── main.yml │ ├── templates │ │ └── galaxy-server.secret.yaml.j2 │ ├── tasks │ │ ├── main.yml │ │ └── galaxy_server_secret.yml │ └── meta │ │ └── main.yml ├── common │ ├── vars │ │ └── main.yml │ ├── templates │ │ ├── labels │ │ │ ├── version.yaml.j2 │ │ │ └── common.yaml.j2 │ │ ├── galaxy-db-fields-encryption.secret.yaml.j2 │ │ ├── galaxy-admin-password.secret.yaml.j2 │ │ ├── postgres.secret.yaml.j2 │ │ └── service_account.yaml.j2 │ ├── meta │ │ └── main.yml │ ├── tasks │ │ ├── set_bundle_cacert.yml │ │ ├── object_storage_configuration.yml │ │ ├── idle_deployment.yml │ │ ├── set_images.yml │ │ ├── check_k8s_or_openshift.yml │ │ ├── check_existing.yml │ │ ├── signing_service.yml │ │ └── db_fields_encryption_configuration.yml │ └── README.md ├── postgres │ ├── vars │ │ └── main.yml │ ├── templates │ │ ├── event.yaml.j2 │ │ ├── postgres_upgrade.secret.yaml.j2 │ │ └── postgres_extra_settings.configmap.yaml.j2 │ ├── tasks │ │ ├── error_handling.yml │ │ └── scale_down_deployments.yml │ ├── meta │ │ └── main.yml │ ├── README.md │ └── defaults │ │ └── main.yml ├── galaxy-worker │ ├── defaults │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── README.md │ └── tasks │ │ └── main.yml ├── backup │ ├── vars │ │ └── main.yml │ ├── tasks │ │ ├── cleanup.yml │ │ ├── remove_management_pod.yml │ │ ├── error_handling.yml │ │ ├── dump_secret.yml │ │ ├── main.yml │ │ ├── dump_generated_secret.yml │ │ ├── custom_resource.yml │ │ └── secrets.yml │ ├── templates │ │ ├── event.yaml.j2 │ │ ├── backup.pvc.yaml.j2 │ │ ├── management-pod.yaml.j2 │ │ └── backup-content-k8s-job.yaml.j2 │ ├── meta │ │ └── main.yml │ ├── defaults │ │ └── main.yml │ └── README.md ├── restore │ ├── templates │ │ ├── galaxy_object.yaml.j2 │ │ ├── event.yaml.j2 │ │ ├── secrets.yml.j2 │ │ ├── management-pod.yaml.j2 │ │ └── restore-content-k8s-job.yaml.j2 │ ├── tasks │ │ ├── remove_management_pod.yml │ │ ├── error_handling.yml │ │ ├── scale_down_deployments.yml │ │ ├── deploy_galaxy.yml │ │ ├── import_vars.yml │ │ ├── update_status.yml │ │ └── main.yml │ ├── vars │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── defaults │ │ └── main.yml │ └── README.md ├── redis │ ├── defaults │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── README.md │ ├── templates │ │ ├── redis.pvc.yaml.j2 │ │ └── redis.service.yaml.j2 │ └── tasks │ │ └── main.yml ├── galaxy-status │ ├── defaults │ │ └── main.yml │ ├── README.md │ ├── meta │ │ └── main.yml │ └── tasks │ │ └── version.yml ├── galaxy-api │ ├── templates │ │ ├── galaxy-container-auth.secret.yaml.j2 │ │ └── galaxy-api.service.yaml.j2 │ ├── meta │ │ └── main.yml │ ├── defaults │ │ └── main.yml │ ├── tasks │ │ ├── container_auth_configuration.yml │ │ └── resource_configuration.yml │ └── README.md ├── galaxy-content │ ├── defaults │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── templates │ │ ├── galaxy-file-storage.pvc.yaml.j2 │ │ └── galaxy-content.service.yaml.j2 │ ├── README.md │ └── tasks │ │ └── create-content-pvc.yml ├── galaxy-route │ ├── meta │ │ └── main.yml │ ├── README.md │ ├── tasks │ │ ├── load_route_tls_secret.yml │ │ └── main.yml │ ├── defaults │ │ └── main.yml │ └── templates │ │ └── galaxy.ingress.yaml.j2 └── galaxy-web │ ├── meta │ └── main.yml │ ├── README.md │ ├── defaults │ └── main.yml │ └── templates │ └── galaxy-web.service.yaml.j2 ├── molecule ├── kind │ ├── requirements.yml │ ├── config.yml │ ├── create.yml │ ├── destroy.yml │ ├── converge.yml │ └── molecule.yml └── default │ ├── requirements.yml │ ├── create.yml │ ├── resources.yml │ ├── converge.yml │ ├── kustomize.yml │ ├── destroy.yml │ ├── prepare.yml │ ├── molecule.yml │ └── verify.yml ├── config ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── service_account.yaml │ ├── auth_proxy_client_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── cluster_role.yaml │ ├── leader_election_role_binding.yaml │ ├── cluster_role_binding.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_service.yaml │ ├── pulp_viewer_role.yaml │ ├── role_binding.yaml │ ├── pulp_editor_role.yaml │ ├── leader_election_role.yaml │ ├── kustomization.yaml │ └── role.yaml ├── scorecard │ ├── bases │ │ └── config.yaml │ ├── patches │ │ ├── basic.config.yaml │ │ └── olm.config.yaml │ └── kustomization.yaml ├── samples │ ├── galaxy_v1beta1_galaxybackup_cr.ci.yaml │ ├── galaxy_v1beta1_galaxyrestore_cr.ci.yaml │ ├── galaxy_v1beta1_galaxyrestore_cr.default.yaml │ ├── galaxy_v1beta1_galaxybackup_cr.default.yaml │ ├── galaxy_v1beta1_galaxy_cr.unauthenticated-demo.yaml │ ├── kustomization.yaml │ ├── galaxy_v1beta1_galaxy_cr.galaxy-demo.yaml │ ├── galaxy_v1beta1_galaxy_cr.object_storage.aws.yaml │ ├── galaxy_v1beta1_galaxy_cr.object_storage.azure.yaml │ ├── galaxy_v1beta1_galaxy_cr.galaxy.yaml │ ├── galaxy_v1beta1_galaxy_cr.molecule.ci.yaml │ ├── galaxy_v1beta1_galaxy_cr.ci.yaml │ ├── galaxy_v1beta1_galaxy_cr.ocp.ci.yaml │ ├── galaxy_v1beta1_galaxy_cr.s6.ci.yaml │ ├── galaxy_v1beta1_galaxy_cr.galaxy.ocp.ci.yaml │ ├── galaxy_v1beta1_galaxy_cr.galaxy.s6.ci.yaml │ ├── galaxy_v1beta1_galaxy_cr.galaxy.externaldb.ci.yaml │ ├── galaxy_v1beta1_galaxy_cr.galaxy.azure.ci.yaml │ ├── galaxy_v1beta1_galaxy_cr.galaxy.s3.ci.yaml │ ├── galaxy_v1beta1_galaxy_cr.galaxy.ci.yaml │ └── galaxy_v1beta1_galaxy_cr.default.yaml ├── testing │ ├── pull_policy │ │ ├── Never.yaml │ │ ├── Always.yaml │ │ └── IfNotPresent.yaml │ ├── debug_logs_patch.yaml │ ├── manager_image.yaml │ └── kustomization.yaml ├── manager │ ├── controller_manager_config.yaml │ └── kustomization.yaml ├── crd │ └── kustomization.yaml ├── default │ ├── manager_config_patch.yaml │ ├── kustomization.yaml │ └── manager_auth_proxy_patch.yaml └── manifests │ └── kustomization.yaml ├── CHANGES ├── 928.bugfix ├── 929.bugfix ├── 994.bugfix ├── 1001.bugfix ├── 717.feature └── .TEMPLATE.md ├── .ci ├── scripts │ ├── validate-bundle.sh │ ├── prepare-external-database.sh │ ├── molecule.sh │ ├── generate-cal-version.py │ ├── retry.sh │ ├── quay-push.sh │ ├── signing_metadata.sh │ ├── kubelinter.sh │ ├── prepare-object-storage.sh │ └── galaxy_tests.sh └── assets │ ├── httpie │ └── config.json │ └── kubernetes │ ├── galaxy-admin-password.secret.yaml │ ├── .kube-linter.yaml │ ├── galaxy-object-storage.aws.secret.yaml │ ├── galaxy-external-database.secret.yaml │ ├── galaxy-object-storage.azure.secret.yaml │ ├── galaxy-container-auth.secret.yaml │ └── signing_scripts.configmap.yaml ├── dev ├── admin-password-secret.yml └── cr-examples │ ├── galaxy-db-configuration.cr.yml │ └── galaxy.cr.yml ├── vendor └── galaxy.ansible.com │ ├── kubernetes │ └── core │ │ └── kubernetes-core-3.2.0.tar.gz │ ├── community │ └── crypto │ │ └── community-crypto-2.17.1.tar.gz │ └── operator_sdk │ └── util │ └── operator_sdk-util-0.4.0.tar.gz ├── requirements.yml ├── .github ├── ISSUE_TEMPLATE │ ├── task.md │ ├── bug_report.md │ └── feature_request.md ├── workflows │ ├── scripts │ │ ├── secrets.py │ │ └── show_logs.sh │ └── build-operator-image.yaml ├── pull_request_template.md └── stale.yml ├── kustomization.yaml ├── galaxy-cr.yaml ├── .gitignore ├── PROJECT ├── .readthedocs.yaml ├── watches.yaml ├── COPYRIGHT ├── .gitleaks.toml ├── Dockerfile ├── bundle.Dockerfile ├── down.sh └── ansible-down.sh /docs/index.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /docs/roles/redis.md: -------------------------------------------------------------------------------- 1 | ../../roles/redis/README.md -------------------------------------------------------------------------------- /roles/galaxy-config/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /docs/roles/postgres.md: -------------------------------------------------------------------------------- 1 | ../../roles/postgres/README.md -------------------------------------------------------------------------------- /molecule/kind/requirements.yml: -------------------------------------------------------------------------------- 1 | ../../requirements.yml -------------------------------------------------------------------------------- /docs/roles/galaxy-api.md: -------------------------------------------------------------------------------- 1 | ../../roles/galaxy-api/README.md -------------------------------------------------------------------------------- /docs/roles/galaxy-common.md: -------------------------------------------------------------------------------- 1 | ../../roles/common/README.md -------------------------------------------------------------------------------- /docs/roles/galaxy-web.md: -------------------------------------------------------------------------------- 1 | ../../roles/galaxy-web/README.md -------------------------------------------------------------------------------- /docs/roles/galaxybackup.md: -------------------------------------------------------------------------------- 1 | ../../roles/backup/README.md -------------------------------------------------------------------------------- /docs/roles/galaxyrestore.md: -------------------------------------------------------------------------------- 1 | ../../roles/restore/README.md -------------------------------------------------------------------------------- /molecule/default/requirements.yml: -------------------------------------------------------------------------------- 1 | ../../requirements.yml -------------------------------------------------------------------------------- /docs/roles/galaxy-route.md: -------------------------------------------------------------------------------- 1 | ../../roles/galaxy-route/README.md -------------------------------------------------------------------------------- /docs/roles/galaxy-status.md: -------------------------------------------------------------------------------- 1 | ../../roles/galaxy-status/README.md -------------------------------------------------------------------------------- /docs/roles/galaxy-worker.md: -------------------------------------------------------------------------------- 1 | ../../roles/galaxy-worker/README.md -------------------------------------------------------------------------------- /docs/roles/galaxy-content.md: -------------------------------------------------------------------------------- 1 | ../../roles/galaxy-content/README.md -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /CHANGES/928.bugfix: -------------------------------------------------------------------------------- 1 | Fixed a bug with signing scripts not being mounted. 2 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs-ansible 2 | mkdocs-git-revision-date-plugin 3 | -------------------------------------------------------------------------------- /CHANGES/929.bugfix: -------------------------------------------------------------------------------- 1 | Fixed a bug with signing scripts not getting registered in database. 2 | -------------------------------------------------------------------------------- /CHANGES/994.bugfix: -------------------------------------------------------------------------------- 1 | Fixed signing-scripts to be compatible with passphrase in the gpg key. 2 | -------------------------------------------------------------------------------- /CHANGES/1001.bugfix: -------------------------------------------------------------------------------- 1 | Fixed a bug where the gpg keys were not available in all API and worker pods. 2 | -------------------------------------------------------------------------------- /docs/images/galaxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible/galaxy-operator/HEAD/docs/images/galaxy.png -------------------------------------------------------------------------------- /.ci/scripts/validate-bundle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | #!/usr/bin/env bash 3 | 4 | VERSION=0.0.0 make bundle 5 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: sa 5 | -------------------------------------------------------------------------------- /.ci/assets/httpie/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_options": [ 3 | "--ignore-stdin", 4 | "--pretty=format" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /roles/common/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | _entrypoint_dir: /venv/bin 3 | supported_pg_version: 15 4 | # version check 5 | gating_version: '' 6 | -------------------------------------------------------------------------------- /molecule/default/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | tasks: [] 7 | -------------------------------------------------------------------------------- /CHANGES/717.feature: -------------------------------------------------------------------------------- 1 | Utilize the renamed `pulp-minimal` and `galaxy-minimal` images. Also have CI test the new big s6-contining images `pulp` and `pulp-galaxy-ng`. 2 | -------------------------------------------------------------------------------- /dev/admin-password-secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: 'admin-password-secret' 6 | stringData: 7 | password: 'password' 8 | 9 | -------------------------------------------------------------------------------- /roles/postgres/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | supported_pg_version: 15 3 | _previous_upgraded_pg_version: 0 4 | old_postgres_pod: [] 5 | _postgres_data_path: '/var/lib/pgsql/data/userdata' 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.ci/assets/kubernetes/galaxy-admin-password.secret.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: 'example-galaxy-admin-password' 6 | stringData: 7 | password: 'password' 8 | -------------------------------------------------------------------------------- /vendor/galaxy.ansible.com/kubernetes/core/kubernetes-core-3.2.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible/galaxy-operator/HEAD/vendor/galaxy.ansible.com/kubernetes/core/kubernetes-core-3.2.0.tar.gz -------------------------------------------------------------------------------- /vendor/galaxy.ansible.com/community/crypto/community-crypto-2.17.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible/galaxy-operator/HEAD/vendor/galaxy.ansible.com/community/crypto/community-crypto-2.17.1.tar.gz -------------------------------------------------------------------------------- /vendor/galaxy.ansible.com/operator_sdk/util/operator_sdk-util-0.4.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible/galaxy-operator/HEAD/vendor/galaxy.ansible.com/operator_sdk/util/operator_sdk-util-0.4.0.tar.gz -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - name: community.crypto 4 | version: ">=2.15.0" 5 | - name: operator_sdk.util 6 | version: "0.5.0" 7 | - name: kubernetes.core 8 | version: "3.2.0" 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/task.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🗒️ Task 3 | about: Documentation, CI/CD, refactors, investigations 4 | title: '' 5 | labels: Task, Triage-Needed 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: ["/metrics"] 7 | verbs: ["get"] 8 | -------------------------------------------------------------------------------- /roles/common/templates/labels/version.yaml.j2: -------------------------------------------------------------------------------- 1 | app.kubernetes.io/version: '{{ _image.split(':')[-1] | truncate(63, True, '', 0) }}' 2 | app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}' 3 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxybackup_cr.ci.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: galaxy.ansible.com/v1beta1 3 | kind: GalaxyBackup 4 | metadata: 5 | name: ci-galaxybackup 6 | spec: 7 | deployment_name: example-galaxy 8 | no_log: false 9 | -------------------------------------------------------------------------------- /molecule/kind/config.yml: -------------------------------------------------------------------------------- 1 | kind: Cluster 2 | apiVersion: kind.x-k8s.io/v1alpha4 3 | kubeadmConfigPatches: 4 | - | 5 | kind: ClusterConfiguration 6 | apiServer: 7 | extraArgs: 8 | "service-node-port-range": "24810-32767" 9 | -------------------------------------------------------------------------------- /kustomization.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: kustomize.config.k8s.io/v1beta1 3 | kind: Kustomization 4 | resources: 5 | - config/default 6 | 7 | namespace: galaxy 8 | 9 | images: 10 | - name: quay.io/ansible/galaxy-operator 11 | newTag: main -------------------------------------------------------------------------------- /roles/galaxy-worker/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Here we use _galaxy_ansible_com_galaxy to get un-modified cr 3 | # see: https://github.com/operator-framework/operator-sdk/issues/1770 4 | raw_spec: "{{ vars['_galaxy_ansible_com_galaxy']['spec'] }}" 5 | -------------------------------------------------------------------------------- /roles/backup/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | deployment_kind: Galaxy 3 | deployment_api_version: v1beta1 4 | deployment_type: "galaxy" 5 | _postgres_image: quay.io/sclorg/postgresql-15-c9s:latest 6 | 7 | supported_pg_version: 15 8 | 9 | is_k8s: false 10 | is_openshift: false 11 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxyrestore_cr.ci.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: galaxy.ansible.com/v1beta1 3 | kind: GalaxyRestore 4 | metadata: 5 | name: ci-galaxyrestore 6 | spec: 7 | deployment_name: example-galaxy 8 | backup_name: ci-galaxybackup 9 | no_log: false 10 | -------------------------------------------------------------------------------- /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 }} --config config.yml 9 | -------------------------------------------------------------------------------- /.ci/assets/kubernetes/.kube-linter.yaml: -------------------------------------------------------------------------------- 1 | checks: 2 | addAllBuiltIn: true 3 | exclude: 4 | - "latest-tag" 5 | - "no-read-only-root-fs" 6 | - "run-as-non-root" 7 | - "minimum-three-replicas" 8 | - "deprecated-service-account-field" 9 | - "exposed-services" 10 | -------------------------------------------------------------------------------- /roles/restore/templates/galaxy_object.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: '{{ deployment_api_version }}' 3 | kind: '{{ deployment_kind }}' 4 | metadata: 5 | name: '{{ deployment_name }}' 6 | namespace: '{{ ansible_operator_meta.namespace }}' 7 | spec: 8 | {{ spec | to_yaml | indent(2) }} 9 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxyrestore_cr.default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: galaxy.ansible.com/v1beta 3 | kind: GalaxyRestore 4 | metadata: 5 | name: example-galaxy-restore 6 | spec: 7 | backup_name: example-galaxy-backup 8 | no_log: true 9 | postgres_label_selector: '' 10 | -------------------------------------------------------------------------------- /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: galaxy-operator 12 | imagePullPolicy: Never 13 | -------------------------------------------------------------------------------- /galaxy-cr.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: galaxy.ansible.com/v1beta1 3 | kind: Galaxy 4 | metadata: 5 | name: galaxy 6 | spec: 7 | hostname: localhost 8 | ingress_type: ingress 9 | storage_type: File 10 | file_storage_access_mode: ReadWriteOnce 11 | file_storage_size: 8Gi 12 | no_log: false -------------------------------------------------------------------------------- /roles/backup/tasks/cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Delete any existing management pod 4 | k8s: 5 | name: "{{ ansible_operator_meta.name }}-db-management" 6 | kind: Pod 7 | api_version: v1 8 | namespace: "{{ backup_pvc_namespace }}" 9 | state: absent 10 | force: true 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore pyenv file 2 | .python-version 3 | 4 | # Ignore pipenv files 5 | Pipfile 6 | Pipfile.lock 7 | 8 | # Ignore development and testing artifacts 9 | .vscode 10 | bin 11 | hacking/* 12 | test* 13 | testing/* 14 | testbin/* 15 | hacking/ 16 | 17 | # Ignore mkdocs builds 18 | site 19 | -------------------------------------------------------------------------------- /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: galaxy-operator 12 | imagePullPolicy: Always 13 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: sa 12 | -------------------------------------------------------------------------------- /roles/backup/tasks/remove_management_pod.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Delete any existing management pod 3 | k8s: 4 | name: "{{ ansible_operator_meta.name }}-db-management" 5 | kind: Pod 6 | api_version: v1 7 | namespace: "{{ backup_pvc_namespace }}" 8 | state: absent 9 | force: true 10 | -------------------------------------------------------------------------------- /roles/restore/tasks/remove_management_pod.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Delete any existing management pod 3 | k8s: 4 | name: "{{ ansible_operator_meta.name }}-db-management" 5 | kind: Pod 6 | api_version: v1 7 | namespace: "{{ backup_pvc_namespace }}" 8 | state: absent 9 | force: true 10 | -------------------------------------------------------------------------------- /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: galaxy-operator 12 | imagePullPolicy: IfNotPresent 13 | -------------------------------------------------------------------------------- /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.23.0 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /config/manager/controller_manager_config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 2 | kind: ControllerManagerConfig 3 | health: 4 | healthProbeBindAddress: :6789 5 | metrics: 6 | bindAddress: 127.0.0.1:8080 7 | 8 | leaderElection: 9 | leaderElect: true 10 | resourceName: 811c9dc5.ansible.com 11 | -------------------------------------------------------------------------------- /roles/redis/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | _redis_image: redis:7 3 | 4 | redis_storage_size: 1Gi 5 | # Here we use _galaxy_ansible_com_galaxy to get un-modified cr 6 | # see: https://github.com/operator-framework/operator-sdk/issues/1770 7 | raw_spec: "{{ vars['_galaxy_ansible_com_galaxy']['spec'] }}" 8 | redis_data_persistence: true 9 | -------------------------------------------------------------------------------- /config/rbac/cluster_role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: galaxy-operator-cluster-role 6 | rules: 7 | - apiGroups: 8 | - config.openshift.io 9 | resources: 10 | - ingresses 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: sa 12 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxybackup_cr.default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: galaxy.ansible.com/v1beta 3 | kind: GalaxyBackup 4 | metadata: 5 | name: example-galaxy-backup 6 | spec: 7 | deployment_name: example-galaxy 8 | no_log: true 9 | backup_pvc: '' 10 | backup_storage_requirements: '' 11 | backup_storage_class: '' 12 | -------------------------------------------------------------------------------- /roles/common/templates/galaxy-db-fields-encryption.secret.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: '{{ ansible_operator_meta.name }}-db-fields-encryption' 6 | namespace: '{{ ansible_operator_meta.namespace }}' 7 | stringData: 8 | database_fields.symmetric.key: | 9 | {{ db_fields_encryption_key }} 10 | -------------------------------------------------------------------------------- /roles/galaxy-status/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | postgres_migrated_from_secret: '' 3 | 4 | ingress_type: none 5 | 6 | # The TLS termination mechanism to use to access 7 | # the services. Supported mechanism are: edge, passthrough 8 | # 9 | route_tls_termination_mechanism: edge 10 | 11 | status_protocol: 'http' 12 | status_port: '24880' 13 | -------------------------------------------------------------------------------- /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: galaxy-operator 12 | env: 13 | - name: ANSIBLE_DEBUG_LOGS 14 | value: "TRUE" 15 | -------------------------------------------------------------------------------- /.ci/assets/kubernetes/galaxy-object-storage.aws.secret.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: 'example-galaxy-object-storage' 6 | stringData: 7 | s3-access-key-id: 'AKIAIT2Z5TDYPX3ARJBA' 8 | s3-secret-access-key: 'fqRvjWaPU5o0fCqQuUWbj9Fainj2pVZtBCiDiieS' 9 | s3-bucket-name: 'galaxy' 10 | s3-region: 'us-east-1' 11 | -------------------------------------------------------------------------------- /config/rbac/cluster_role_binding.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: galaxy-operator-cluster-rolebinding 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: galaxy-operator-cluster-role 10 | subjects: 11 | - kind: ServiceAccount 12 | name: sa 13 | -------------------------------------------------------------------------------- /roles/common/templates/galaxy-admin-password.secret.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: '{{ ansible_operator_meta.name }}-admin-password' 6 | namespace: '{{ ansible_operator_meta.namespace }}' 7 | stringData: 8 | password: '{{ lookup('password', '/tmp/ts' + ansible_operator_meta.name + 'pg length=32 chars=ascii_letters,digits') }}' 9 | -------------------------------------------------------------------------------- /.ci/assets/kubernetes/galaxy-external-database.secret.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: 'example-galaxy-external-database' 6 | stringData: 7 | host: 'galaxy-postgresql' 8 | port: '5555' 9 | database: 'galaxy' 10 | username: 'galaxy' 11 | password: 'galaxy' 12 | sslmode: 'prefer' 13 | type: 'unmanaged' 14 | type: Opaque 15 | -------------------------------------------------------------------------------- /roles/common/templates/labels/common.yaml.j2: -------------------------------------------------------------------------------- 1 | # https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/ 2 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}' 3 | app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}' 4 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 5 | app.kubernetes.io/component: '{{ deployment_type }}' 6 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: proxy-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/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: galaxy-operator 12 | image: testing 13 | env: 14 | - name: OPERATOR_SA_NAME 15 | value: osdk-sa 16 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | protocol: TCP 13 | targetPort: https 14 | selector: 15 | control-plane: controller-manager 16 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: ansible.com 2 | layout: 3 | - ansible.sdk.operatorframework.io/v1 4 | plugins: 5 | manifests.sdk.operatorframework.io/v2: {} 6 | scorecard.sdk.operatorframework.io/v2: {} 7 | projectName: galaxy-operator 8 | resources: 9 | - api: 10 | crdVersion: v1 11 | namespaced: true 12 | domain: ansible.com 13 | group: galaxy 14 | kind: Galaxy 15 | version: v1beta1 16 | version: "3" -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.11" 12 | nodejs: "16" 13 | 14 | mkdocs: 15 | configuration: mkdocs.yaml 16 | 17 | python: 18 | install: 19 | - requirements: ./docs/requirements.txt -------------------------------------------------------------------------------- /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/galaxy_v1beta1_galaxy_crd.yaml 6 | - bases/galaxy_v1beta1_galaxybackup_crd.yaml 7 | - bases/galaxy_v1beta1_galaxyrestore_crd.yaml 8 | #+kubebuilder:scaffold:crdkustomizeresource -------------------------------------------------------------------------------- /molecule/default/resources.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set resources to {{ state }} 3 | k8s: 4 | definition: "{{ lookup('template', '/'.join([deploy_dir, item])) }}" 5 | namespace: '{{ namespace }}' 6 | state: '{{ state }}' 7 | with_items: 8 | - service_account.yaml 9 | - role.yaml 10 | - cluster_role.yaml 11 | - role_binding.yaml 12 | - cluster_role_binding.yaml 13 | - operator.yaml 14 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | configMapGenerator: 8 | - files: 9 | - controller_manager_config.yaml 10 | name: galaxy-operator-config 11 | 12 | apiVersion: kustomize.config.k8s.io/v1beta1 13 | kind: Kustomization 14 | images: 15 | - name: controller 16 | newName: quay.io/ansible/galaxy-operator 17 | newTag: main -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /watches.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use the 'create api' subcommand to add watches to this file. 3 | - version: v1beta1 4 | group: galaxy.ansible.com 5 | kind: Galaxy 6 | playbook: playbooks/galaxy.yaml 7 | 8 | - version: v1beta1 9 | group: galaxy.ansible.com 10 | kind: GalaxyBackup 11 | role: backup 12 | 13 | - version: v1beta1 14 | group: galaxy.ansible.com 15 | kind: GalaxyRestore 16 | role: restore 17 | 18 | # +kubebuilder:scaffold:watch -------------------------------------------------------------------------------- /.ci/scripts/prepare-external-database.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | #!/usr/bin/env bash 3 | 4 | if [[ "$CI_TEST_DATABASE" == "external" ]]; then 5 | docker volume create postgresql 6 | docker run -d -p 5555:5432 --name postgresql -e POSTGRESQL_USER=galaxy -e POSTGRESQL_PASSWORD=galaxy -e POSTGRESQL_DATABASE=galaxy -v postgresql:/var/lib/pgsql/data quay.io/sclorg/postgresql-15-c9s:latest 7 | echo $(minikube ip) galaxy-postgresql | sudo tee -a /etc/hosts 8 | fi 9 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | patchesJson6902: 4 | - path: patches/basic.config.yaml 5 | target: 6 | group: scorecard.operatorframework.io 7 | version: v1alpha3 8 | kind: Configuration 9 | name: config 10 | - path: patches/olm.config.yaml 11 | target: 12 | group: scorecard.operatorframework.io 13 | version: v1alpha3 14 | kind: Configuration 15 | name: config 16 | #+kubebuilder:scaffold:patchesJson6902 -------------------------------------------------------------------------------- /roles/galaxy-config/templates/galaxy-server.secret.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: "{{ ansible_operator_meta.name }}-server" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | stringData: 8 | settings.py: | 9 | {% for setting, value in pulp_combined_settings.items() %} 10 | {{ setting | upper }} = {{ value | string | capitalize if value | string in ["True", "False"] else value | to_json }} 11 | {% endfor %} 12 | -------------------------------------------------------------------------------- /roles/galaxy-api/templates/galaxy-container-auth.secret.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: '{{ ansible_operator_meta.name }}-container-auth' 6 | namespace: '{{ ansible_operator_meta.namespace }}' 7 | stringData: 8 | container_auth_private_key.pem: |- 9 | {{ lookup('file', '/tmp/private_key.pem') | indent(width=6, first=True) }} 10 | container_auth_public_key.pem: |- 11 | {{ lookup('file', '/tmp/public_key.pem') | indent(width=6, first=True) }} 12 | -------------------------------------------------------------------------------- /roles/galaxy-config/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Configure Galaxy Server Secret 4 | include_tasks: combine_galaxy_settings.yml 5 | 6 | - name: Include redis role 7 | include_role: 8 | name: redis 9 | when: pulp_combined_settings.cache_enabled 10 | 11 | - include_tasks: 12 | file: sso-configuration.yml 13 | when: 14 | - sso_secret is defined 15 | - sso_secret | length 16 | 17 | - name: Configure Galaxy Server Secret 18 | include_tasks: galaxy_server_secret.yml 19 | -------------------------------------------------------------------------------- /roles/backup/templates/event.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Event 4 | metadata: 5 | name: backup-error.{{ now }} 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | involvedObject: 8 | apiVersion: galaxy.ansible.com/v1beta1 9 | kind: {{ kind }} 10 | name: {{ ansible_operator_meta.name }} 11 | namespace: "{{ ansible_operator_meta.namespace }}" 12 | message: {{ error_msg }} 13 | reason: BackupFailed 14 | type: Warning 15 | firstTimestamp: {{ now }} 16 | lastTimestamp: {{ now }} 17 | count: 1 18 | -------------------------------------------------------------------------------- /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 --path pull_policy/{{ operator_pull_policy }}.yaml' 15 | args: 16 | chdir: '{{ config_dir }}/testing' 17 | -------------------------------------------------------------------------------- /roles/postgres/templates/event.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Event 4 | metadata: 5 | name: install-error.{{ now }} 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | involvedObject: 8 | apiVersion: galaxy.ansible.com/v1beta1 9 | kind: {{ kind }} 10 | name: {{ ansible_operator_meta.name }} 11 | namespace: "{{ ansible_operator_meta.namespace }}" 12 | message: {{ error_msg }} 13 | reason: InstallFailed 14 | type: Warning 15 | firstTimestamp: {{ now }} 16 | lastTimestamp: {{ now }} 17 | count: 1 18 | -------------------------------------------------------------------------------- /roles/restore/templates/event.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Event 4 | metadata: 5 | name: backup-error.{{ now }} 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | involvedObject: 8 | apiVersion: galaxy.ansible.com/v1beta1 9 | kind: {{ kind }} 10 | name: {{ ansible_operator_meta.name }} 11 | namespace: "{{ ansible_operator_meta.namespace }}" 12 | message: {{ error_msg }} 13 | reason: RestoreFailed 14 | type: Warning 15 | firstTimestamp: {{ now }} 16 | lastTimestamp: {{ now }} 17 | count: 1 18 | -------------------------------------------------------------------------------- /roles/galaxy-content/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # common default values 3 | client_request_timeout: 30 4 | ingress_type: none 5 | 6 | # Here we use _galaxy_ansible_com_galaxy to get un-modified cr 7 | # see: https://github.com/operator-framework/operator-sdk/issues/1770 8 | raw_spec: "{{ vars['_galaxy_ansible_com_galaxy']['spec'] }}" 9 | 10 | # gunicorn default values 11 | gunicorn_timeout: "{{ (([(client_request_timeout | int), 10] | max) / 3) | int }}" 12 | gunicorn_timeout_grace_period: 2 13 | gunicorn_content_workers: 2 14 | -------------------------------------------------------------------------------- /roles/backup/tasks/error_handling.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Set apiVersion and kind variables 4 | set_fact: 5 | api_version: '{{ hostvars["localhost"]["inventory_file"].split("/")[4:6] | join("/") }}' 6 | kind: '{{ hostvars["localhost"]["inventory_file"].split("/")[6] }}' 7 | 8 | - name: Determine the timestamp 9 | set_fact: 10 | now: '{{ lookup("pipe", "date +%FT%TZ") }}' 11 | 12 | - name: Emit ocp event with error 13 | k8s: 14 | state: present 15 | definition: "{{ lookup('template', 'templates/event.yaml.j2') }}" 16 | -------------------------------------------------------------------------------- /config/rbac/pulp_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view galaxies. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: galaxy-viewer-role 6 | rules: 7 | - apiGroups: 8 | - galaxy.ansible.com 9 | resources: 10 | - galaxies 11 | - galaxybackups 12 | - galaxyrestores 13 | verbs: 14 | - get 15 | - list 16 | - watch 17 | - apiGroups: 18 | - galaxy.ansible.com 19 | resources: 20 | - galaxies/status 21 | - galaxybackups/status 22 | - galaxyrestores/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /roles/postgres/tasks/error_handling.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Set apiVersion and kind variables 4 | set_fact: 5 | api_version: '{{ hostvars["localhost"]["inventory_file"].split("/")[4:6] | join("/") }}' 6 | kind: '{{ hostvars["localhost"]["inventory_file"].split("/")[6] }}' 7 | 8 | - name: Determine the timestamp 9 | set_fact: 10 | now: '{{ lookup("pipe", "date +%FT%TZ") }}' 11 | 12 | - name: Emit ocp event with error 13 | k8s: 14 | state: present 15 | definition: "{{ lookup('template', 'templates/event.yaml.j2') }}" 16 | -------------------------------------------------------------------------------- /roles/restore/tasks/error_handling.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Set apiVersion and kind variables 4 | set_fact: 5 | api_version: '{{ hostvars["localhost"]["inventory_file"].split("/")[4:6] | join("/") }}' 6 | kind: '{{ hostvars["localhost"]["inventory_file"].split("/")[6] }}' 7 | 8 | - name: Determine the timestamp 9 | set_fact: 10 | now: '{{ lookup("pipe", "date +%FT%TZ") }}' 11 | 12 | - name: Emit ocp event with error 13 | k8s: 14 | state: present 15 | definition: "{{ lookup('template', 'templates/event.yaml.j2') }}" 16 | -------------------------------------------------------------------------------- /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: yes 15 | loop: '{{ resources.stdout | from_yaml_all | list }}' 16 | -------------------------------------------------------------------------------- /roles/galaxy-status/README.md: -------------------------------------------------------------------------------- 1 | Galaxy Status 2 | =========== 3 | 4 | A role to update Galaxy deployment status. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 10 | 11 | Dependencies 12 | ------------ 13 | 14 | collections: 15 | 16 | - kubernetes.core 17 | - operator_sdk.util 18 | 19 | License 20 | ------- 21 | 22 | GPLv2+ 23 | 24 | Author Information 25 | ------------------ 26 | 27 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 28 | -------------------------------------------------------------------------------- /roles/restore/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | supported_pg_version: 15 3 | deployment_type: "galaxy" 4 | _postgres_image: quay.io/sclorg/postgresql-15-c9s:latest 5 | 6 | custom_resource_key: '_galaxy_ansible_com_galaxyrestore' 7 | 8 | deployment_kind: Galaxy 9 | deployment_api_version: v1beta1 10 | 11 | backup_api_version: 'v1beta1' 12 | backup_kind: 'GalaxyBackup' 13 | 14 | # If set to true, the restore process will delete the existing database and create a new one 15 | force_drop_db: false 16 | pg_drop_create: '' 17 | 18 | is_k8s: false 19 | is_openshift: false 20 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright © 2024 Red Hat. 2 | 3 | This software is licensed to you under the GNU General Public 4 | License as published by the Free Software Foundation; either version 5 | 2 of the License (GPLv2) or (at your option) any later version. 6 | There is NO WARRANTY for this software, express or implied, 7 | including the implied warranties of MERCHANTABILITY, 8 | NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should 9 | have received a copy of GPLv2 along with this software; if not, see 10 | http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 11 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.unauthenticated-demo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: galaxy.ansible.com/v1beta1 3 | kind: Galaxy 4 | metadata: 5 | name: galaxy 6 | namespace: galaxy 7 | spec: 8 | hostname: galaxy.local 9 | ingress_type: ingress 10 | storage_type: File 11 | file_storage_access_mode: ReadWriteOnce 12 | file_storage_size: 8Gi 13 | no_log: false 14 | image_pull_policy: IfNotPresent 15 | pulp_settings: 16 | GALAXY_ENABLE_UNAUTHENTICATED_COLLECTION_ACCESS: true 17 | GALAXY_ENABLE_UNAUTHENTICATED_COLLECTION_DOWNLOAD: true 18 | -------------------------------------------------------------------------------- /roles/redis/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to setup Galaxy redis service 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /.github/workflows/scripts/secrets.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import sys 4 | 5 | secrets = json.loads(sys.argv[1]) 6 | for key, value in secrets.items(): 7 | print(f"Setting {key} ...") 8 | lines = len(value.split("\n")) 9 | if lines > 1: 10 | os.system(f"/bin/bash -c \"echo '{key}<> $GITHUB_ENV\"") 11 | os.system(f"/bin/bash -c \"echo '{value}' >> $GITHUB_ENV\"") 12 | os.system("/bin/bash -c \"echo 'EOF' >> $GITHUB_ENV\"") 13 | else: 14 | os.system(f"/bin/bash -c \"echo '{key}={value}' >> $GITHUB_ENV\"") 15 | -------------------------------------------------------------------------------- /roles/backup/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to backup a Galaxy deployment 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/common/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to setup Galaxy shared obects 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/galaxy-api/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to setup Galaxy API service 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/restore/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to restore a Galaxy deployment 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/galaxy-config/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to setup Galaxy configuration 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/galaxy-route/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to setup Galaxy routes service 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/galaxy-worker/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to setup Galaxy worker service 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/postgres/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to setup Galaxy postgres service 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/galaxy-status/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to update Galaxy deployment status 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/galaxy-web/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to setup Galaxy NGINX web proxy service 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/galaxy-content/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Galaxy-Operator Team 4 | description: A role to setup Galaxy content serving service 5 | issue_tracker_url: https://github.com/ansible/galaxy-operator/issues/new 6 | license: GPL-2.0-or-later 7 | company: Red Hat 8 | galaxy_tags: 9 | - galaxy 10 | - pulp 11 | - pulpcore 12 | dependencies: [] 13 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 14 | # if you add dependencies to this list. 15 | collections: 16 | - operator_sdk.util 17 | - kubernetes.core 18 | -------------------------------------------------------------------------------- /roles/common/tasks/set_bundle_cacert.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Retrieve bundle Certificate Authority Secret 3 | k8s_info: 4 | kind: Secret 5 | namespace: '{{ ansible_operator_meta.namespace }}' 6 | name: '{{ bundle_cacert_secret }}' 7 | register: bundle_cacert 8 | no_log: "{{ no_log }}" 9 | 10 | - name: Load bundle Certificate Authority Secret content 11 | set_fact: 12 | bundle_ca_crt: '{{ bundle_cacert["resources"][0]["data"]["bundle-ca.crt"] | b64decode }}' 13 | no_log: "{{ no_log }}" 14 | when: '"bundle-ca.crt" in bundle_cacert["resources"][0]["data"]' 15 | ... 16 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: RoleBinding 4 | metadata: 5 | name: galaxy-operator-rolebinding 6 | annotations: # About kube-linter checks: https://docs.kubelinter.io/#/generated/checks 7 | ignore-check.kube-linter.io/access-to-create-pods: "Operator needs to create pods" 8 | ignore-check.kube-linter.io/access-to-secrets: "Operator needs to create secrets" 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: Role 12 | name: galaxy-operator-role 13 | subjects: 14 | - kind: ServiceAccount 15 | name: sa 16 | -------------------------------------------------------------------------------- /roles/postgres/templates/postgres_upgrade.secret.yaml.j2: -------------------------------------------------------------------------------- 1 | # Postgres Secret. 2 | --- 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: '{{ ansible_operator_meta.name }}-postgres-configuration' 7 | namespace: '{{ ansible_operator_meta.namespace }}' 8 | stringData: 9 | password: '{{ postgres_pass }}' 10 | username: '{{ postgres_user }}' 11 | database: '{{ postgres_database }}' 12 | port: '{{ postgres_port }}' 13 | host: {{ ansible_operator_meta.name }}-postgres-{{ supported_pg_version }} 14 | sslmode: {{ postgres_sslmode | default('prefer') }} 15 | type: 'managed' 16 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | scheme: https 15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 16 | tlsConfig: 17 | insecureSkipVerify: true 18 | selector: 19 | matchLabels: 20 | control-plane: controller-manager 21 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples you want in your CSV to this file as resources ## 2 | resources: 3 | - galaxy_v1beta1_galaxy_cr.galaxy-demo.yaml 4 | - galaxy_v1beta1_galaxybackup_cr.default.yaml 5 | - galaxy_v1beta1_galaxyrestore_cr.default.yaml 6 | # - galaxy_v1beta1_galaxybackup_cr.ci.yaml 7 | # - galaxy_v1beta1_galaxyrestore_cr.ci.yaml 8 | # - galaxy_v1beta1_galaxy_cr.ocp.ci.yaml 9 | - galaxy_v1beta1_galaxy_cr.galaxy.yaml 10 | - galaxy_v1beta1_galaxy_cr.object_storage.aws.yaml 11 | - galaxy_v1beta1_galaxy_cr.object_storage.azure.yaml 12 | #+kubebuilder:scaffold:manifestskustomizesamples -------------------------------------------------------------------------------- /config/rbac/pulp_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit galaxies. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: galaxy-editor-role 6 | rules: 7 | - apiGroups: 8 | - galaxy.ansible.com 9 | resources: 10 | - galaxies 11 | - galaxybackups 12 | - galaxyrestores 13 | verbs: 14 | - create 15 | - delete 16 | - get 17 | - list 18 | - patch 19 | - update 20 | - watch 21 | - apiGroups: 22 | - galaxy.ansible.com 23 | resources: 24 | - galaxies/status 25 | - galaxybackups/status 26 | - galaxyrestores/status 27 | verbs: 28 | - get 29 | -------------------------------------------------------------------------------- /roles/galaxy-route/README.md: -------------------------------------------------------------------------------- 1 | Galaxy Route 2 | =========== 3 | 4 | A role to setup Galaxy routes in Openshift, yielding the following objects: 5 | 6 | * Ingress/Route 7 | 8 | Requirements 9 | ------------ 10 | 11 | Requires the `openshift` Python library to interact with Openshift: `pip install openshift`. 12 | 13 | Dependencies 14 | ------------ 15 | 16 | collections: 17 | 18 | - kubernetes.core 19 | - operator_sdk.util 20 | 21 | License 22 | ------- 23 | 24 | GPLv2+ 25 | 26 | Author Information 27 | ------------------ 28 | 29 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 30 | -------------------------------------------------------------------------------- /config/default/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: galaxy-operator 11 | args: 12 | - "--config=controller_manager_config.yaml" 13 | volumeMounts: 14 | - name: galaxy-operator-config 15 | mountPath: /controller_manager_config.yaml 16 | subPath: controller_manager_config.yaml 17 | volumes: 18 | - name: galaxy-operator-config 19 | configMap: 20 | name: galaxy-operator-config 21 | -------------------------------------------------------------------------------- /roles/common/templates/postgres.secret.yaml.j2: -------------------------------------------------------------------------------- 1 | # Postgres Secret. 2 | --- 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: '{{ ansible_operator_meta.name }}-postgres-configuration' 7 | namespace: '{{ ansible_operator_meta.namespace }}' 8 | stringData: 9 | password: '{{ lookup('password', '/tmp/p' + ansible_operator_meta.name + 'pg length=32 chars=ascii_letters,digits') }}' 10 | username: '{{ deployment_type }}' 11 | database: '{{ deployment_type }}' 12 | port: '5432' 13 | host: {{ ansible_operator_meta.name }}-postgres-{{ supported_pg_version }} 14 | sslmode: {{ postgres_sslmode | default('prefer') }} 15 | type: 'managed' 16 | -------------------------------------------------------------------------------- /.ci/assets/kubernetes/galaxy-object-storage.azure.secret.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: 'example-galaxy-object-storage' 6 | stringData: 7 | azure-account-name: 'devstoreaccount1' 8 | azure-account-key: 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==' 9 | azure-container: 'galaxy-test' 10 | azure-container-path: 'galaxy' 11 | azure-connection-string: "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://galaxy-azurite:10000/devstoreaccount1;" 12 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.galaxy-demo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | spec: 6 | storage_type: File 7 | # This doesn't really matter for minikube. Single node by design, 8 | # but the storage provisioner allows for ReadWriteMany. So let's 9 | # stick to our default. 10 | file_storage_access_mode: "ReadWriteMany" 11 | # The minikube VM won't go any larger. 12 | file_storage_size: "375Gi" 13 | # Default images: 14 | image: quay.io/ansible/galaxy-ng 15 | image_version: latest 16 | image_web: quay.io/ansible/galaxy-ui 17 | image_web_version: latest 18 | -------------------------------------------------------------------------------- /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 --path pull_policy/{{ operator_pull_policy }}.yaml' 23 | args: 24 | chdir: '{{ config_dir }}/testing' 25 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ##### SUMMARY 2 | 3 | 4 | 9 | 10 | ##### ADDITIONAL INFORMATION 11 | 16 | 17 | 18 | ``` 19 | 20 | ``` 21 | -------------------------------------------------------------------------------- /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 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - coordination.k8s.io 21 | resources: 22 | - leases 23 | verbs: 24 | - get 25 | - list 26 | - watch 27 | - create 28 | - update 29 | - patch 30 | - delete 31 | - apiGroups: 32 | - "" 33 | resources: 34 | - events 35 | verbs: 36 | - create 37 | - patch 38 | -------------------------------------------------------------------------------- /.ci/assets/kubernetes/galaxy-container-auth.secret.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: container-auth 6 | stringData: 7 | tls.key: |- 8 | -----BEGIN EC PRIVATE KEY----- 9 | MHcCAQEEIMgYinafJVjYHHnAk8w3EOiZxJLK5sXaXtSMT6APWYq+oAoGCCqGSM49 10 | AwEHoUQDQgAEzWXdtJTvHinfnN2xzn71etmfkBTmDMZHMMQmSIofMNMcl8ATneZU 11 | pGplUzS5UspaSUomdfkJ0d92Mk0DDXLV7A== 12 | -----END EC PRIVATE KEY----- 13 | tls.crt: |- 14 | -----BEGIN PUBLIC KEY----- 15 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzWXdtJTvHinfnN2xzn71etmfkBTm 16 | DMZHMMQmSIofMNMcl8ATneZUpGplUzS5UspaSUomdfkJ0d92Mk0DDXLV7A== 17 | -----END PUBLIC KEY----- 18 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | [allowlist] 2 | description = "Our test install exports a test only PRIVATE KEY" 3 | paths = [ 4 | ".ci/assets/kubernetes/galaxy-container-auth.secret.yaml", 5 | ".ci/assets/kubernetes/galaxy-external-database.secret.yaml", 6 | ".ci/assets/kubernetes/galaxy-object-storage.aws.secret.yaml", 7 | ".ci/assets/kubernetes/galaxy-object-storage.azure.secret.yaml", 8 | ".ci/assets/kubernetes/galaxy_sign.secret.yaml", 9 | ".ci/scripts/prepare-external-database.sh", 10 | ".ci/scripts/prepare-object-storage.sh", 11 | "config/samples/galaxyproject_v1beta1_galaxy_cr.default.yaml", 12 | "containers/compose/certs/database_fields.symmetric.key", 13 | ] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: Issue, Triage-Needed 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Version** 11 | Please provide the versions of the galaxy-operator and galaxy images in use. 12 | 13 | **Describe the bug** 14 | A clear and concise description of what the bug is. 15 | 16 | **To Reproduce** 17 | Steps to reproduce the behavior: 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Additional context** 23 | Add any other context about the problem here. Please provide links to any previous discussions via Discourse or Bugzilla. 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ✨ Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: Feature, Triage-Needed 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context about the feature request here. 21 | -------------------------------------------------------------------------------- /.ci/scripts/molecule.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | #!/usr/bin/env bash 3 | 4 | if command -v KIND_URL > /dev/null; then 5 | echo "kind is installed already" 6 | else 7 | KIND_URL=https://kind.sigs.k8s.io/dl/v0.11.1/kind-linux-amd64 8 | 9 | if [[ "$OSTYPE" == "darwin"* ]]; then 10 | KIND_URL=https://kind.sigs.k8s.io/dl/v0.11.1/kind-darwin-amd64 11 | fi 12 | 13 | curl -Lo ./kind $KIND_URL 14 | chmod +x ./kind 15 | mv ./kind /usr/local/bin/kind 16 | 17 | fi 18 | 19 | make kustomize 20 | kustomize version 21 | 22 | find ./roles/*/templates/*.yaml.j2 -exec sed -i 's/{{ deployment_type }}-operator-sa/osdk-sa/g' {} \; 23 | 24 | echo "Starting molecule test" 25 | molecule -v test -s kind --destroy never 26 | -------------------------------------------------------------------------------- /roles/redis/README.md: -------------------------------------------------------------------------------- 1 | Redis 2 | ===== 3 | 4 | A role to setup Galaxyredis, yielding the following objects: 5 | 6 | * Deployment 7 | * Service 8 | * PersistentVolumeClaim 9 | 10 | Role Variables 11 | -------------- 12 | 13 | * `redis_image`: The redis image name. Default: redis:7 14 | 15 | Requirements 16 | ------------ 17 | 18 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 19 | 20 | Dependencies 21 | ------------ 22 | 23 | collections: 24 | 25 | - kubernetes.core 26 | - operator_sdk.util 27 | 28 | License 29 | ------- 30 | 31 | GPLv2+ 32 | 33 | Author Information 34 | ------------------ 35 | 36 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 37 | -------------------------------------------------------------------------------- /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/galaxy-operator.clusterserviceversion.yaml 5 | - ../default 6 | - ../samples 7 | - ../scorecard 8 | 9 | apiVersion: kustomize.config.k8s.io/v1beta1 10 | kind: Kustomization 11 | replacements: 12 | - source: 13 | kind: Deployment 14 | name: galaxy-operator-controller-manager 15 | fieldPath: spec.template.spec.containers.1.image 16 | targets: 17 | - select: 18 | kind: ClusterServiceVersion 19 | fieldPaths: 20 | - metadata.annotations.containerImage 21 | options: 22 | create: true 23 | -------------------------------------------------------------------------------- /.ci/scripts/generate-cal-version.py: -------------------------------------------------------------------------------- 1 | # generate_version.py 2 | import requests 3 | import datetime 4 | import os 5 | 6 | version = os.getenv('INPUT_VERSION') 7 | gh_repo = os.getenv('GH_REPO') 8 | 9 | response = requests.get(f'https://api.github.com/repos/{gh_repo}/releases') 10 | releases = [release['tag_name'] for release in response.json()] 11 | if version in releases: 12 | raise ValueError(f"A release with version {version} already exists.") 13 | 14 | if not version: 15 | date = datetime.datetime.now() 16 | version = f'{date.year}.{date.month}.{date.day}' 17 | suffix = 2 18 | while version in releases: 19 | version = f'{date.year}.{date.month}.{date.day}-{suffix}' 20 | suffix += 1 21 | 22 | print(version) 23 | -------------------------------------------------------------------------------- /.ci/scripts/retry.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function retry() 4 | { 5 | local n=0 6 | local try=$1 7 | local cmd="${@: 2}" 8 | [[ $# -le 1 ]] && { 9 | echo "Usage $0 "; } 10 | 11 | until [[ $n -ge $try ]] 12 | do 13 | $cmd && break || { 14 | echo "Command Fail.." 15 | ((n++)) 16 | echo "retry $n ::" 17 | if [[ $n -ge $try ]]; then 18 | set -eu 19 | $cmd 20 | fi 21 | sleep 30; 22 | } 23 | 24 | done 25 | } 26 | 27 | retry $* 28 | -------------------------------------------------------------------------------- /roles/redis/templates/redis.pvc.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: '{{ ansible_operator_meta.name }}-redis-data' 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | labels: 8 | app.kubernetes.io/name: 'redis' 9 | app.kubernetes.io/instance: 'redis-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: cache 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | spec: 14 | resources: 15 | requests: 16 | storage: '{{ redis_storage_size }}' 17 | accessModes: 18 | - ReadWriteOnce 19 | {% if redis_storage_class is defined %} 20 | storageClassName: '{{ redis_storage_class }}' 21 | {% endif %} 22 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.object_storage.aws.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: galaxy-s3 5 | spec: 6 | ingress_type: Ingress 7 | admin_password_secret: "example-galaxy-admin-password" 8 | settings: 9 | debug: "True" 10 | storage_type: S3 11 | object_storage_s3_secret: example-galaxy-object-storage 12 | content: 13 | replicas: 1 14 | resource_requirements: 15 | requests: 16 | cpu: 150m 17 | memory: 256Mi 18 | worker: 19 | replicas: 1 20 | resource_requirements: 21 | requests: 22 | cpu: 150m 23 | memory: 256Mi 24 | web: 25 | replicas: 1 26 | resource_requirements: 27 | requests: 28 | cpu: 100m 29 | memory: 256Mi 30 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.object_storage.azure.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: galaxy-azure 5 | spec: 6 | ingress_type: Ingress 7 | admin_password_secret: "example-galaxy-admin-password" 8 | settings: 9 | debug: "True" 10 | storage_type: Azure 11 | object_storage_azure_secret: example-galaxy-object-storage 12 | content: 13 | replicas: 1 14 | resource_requirements: 15 | requests: 16 | cpu: 150m 17 | memory: 256Mi 18 | worker: 19 | replicas: 1 20 | resource_requirements: 21 | requests: 22 | cpu: 150m 23 | memory: 256Mi 24 | web: 25 | replicas: 1 26 | resource_requirements: 27 | requests: 28 | cpu: 100m 29 | memory: 256Mi 30 | -------------------------------------------------------------------------------- /roles/restore/tasks/scale_down_deployments.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check for presence of Deployment 3 | k8s_info: 4 | api_version: apps/v1 5 | kind: Deployment 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | label_selectors: 8 | - 'app.kubernetes.io/part-of={{ deployment_name }}' 9 | - 'app.kubernetes.io/managed-by={{ deployment_type }}-operator' 10 | register: _deployments 11 | 12 | - name: Scale down Deployment for migration 13 | kubernetes.core.k8s_scale: 14 | api_version: apps/v1 15 | kind: Deployment 16 | name: "{{ item }}" 17 | namespace: "{{ ansible_operator_meta.namespace }}" 18 | replicas: 0 19 | wait: yes 20 | loop: "{{ _deployments.resources | map(attribute='metadata.name') | list }}" 21 | when: _deployments.resources | length 22 | -------------------------------------------------------------------------------- /roles/backup/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Required: specify name of galaxy deployment to backup from 3 | # deployment_name: '' 4 | 5 | # Specify a pre-created PVC (name) to backup to 6 | backup_pvc: '' 7 | backup_pvc_namespace: "{{ ansible_operator_meta.namespace }}" # deprecated 8 | 9 | # Size of backup PVC if created dynamically 10 | backup_storage_requirements: '' 11 | 12 | # Specify storage class to determine how to dynamically create PVC's with 13 | backup_storage_class: '' 14 | 15 | custom_resource_key: '_galaxy_ansible_com_galaxybackup' 16 | 17 | database_type: 'unmanaged' 18 | 19 | azure_container_path: '' 20 | 21 | # Default resource requirements 22 | backup_resource_requirements: 23 | limits: 24 | cpu: "1000m" 25 | memory: "4096Mi" 26 | requests: 27 | cpu: "25m" 28 | memory: "32Mi" 29 | -------------------------------------------------------------------------------- /roles/postgres/tasks/scale_down_deployments.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check for presence of Deployment 3 | k8s_info: 4 | api_version: apps/v1 5 | kind: Deployment 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | label_selectors: 8 | - 'app.kubernetes.io/part-of={{ ansible_operator_meta.name }}' 9 | - 'app.kubernetes.io/managed-by={{ deployment_type }}-operator' 10 | register: _deployments 11 | 12 | - name: Scale down Deployment for migration 13 | kubernetes.core.k8s_scale: 14 | api_version: apps/v1 15 | kind: Deployment 16 | name: "{{ item }}" 17 | namespace: "{{ ansible_operator_meta.namespace }}" 18 | replicas: 0 19 | wait: yes 20 | loop: "{{ _deployments.resources | map(attribute='metadata.name') | list }}" 21 | when: _deployments.resources | length 22 | -------------------------------------------------------------------------------- /roles/restore/templates/secrets.yml.j2: -------------------------------------------------------------------------------- 1 | {% for secret in secrets %} 2 | --- 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: '{{ secrets[secret]['name'] }}' 7 | namespace: '{{ ansible_operator_meta.namespace }}' 8 | labels: 9 | app.kubernetes.io/name: '{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/part-of: '{{ ansible_operator_meta.name }}' 11 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 12 | app.kubernetes.io/component: '{{ deployment_type }}' 13 | app.kubernetes.io/operator-version: '{{ lookup("env", "OPERATOR_VERSION") }}' 14 | type: '{{ secrets[secret]['type'] }}' 15 | stringData: 16 | {% for key, value in secrets[secret]['data'].items() %} 17 | {{ key }}: |- 18 | {{ value | b64decode | indent(4) }} 19 | {% endfor %} 20 | 21 | {% endfor %} 22 | -------------------------------------------------------------------------------- /roles/galaxy-route/tasks/load_route_tls_secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Retrieve Route TLS Secret 3 | kubernetes.core.k8s_info: 4 | kind: Secret 5 | namespace: '{{ ansible_operator_meta.namespace }}' 6 | name: '{{ route_tls_secret }}' 7 | register: route_tls 8 | no_log: "{{ no_log }}" 9 | 10 | - name: Load Route TLS Secret content 11 | ansible.builtin.set_fact: 12 | route_tls_key: '{{ route_tls["resources"][0]["data"]["tls.key"] | b64decode }}' 13 | route_tls_crt: '{{ route_tls["resources"][0]["data"]["tls.crt"] | b64decode }}' 14 | no_log: "{{ no_log }}" 15 | 16 | - name: Load Route TLS Secret content 17 | ansible.builtin.set_fact: 18 | route_ca_crt: '{{ route_tls["resources"][0]["data"]["ca.crt"] | b64decode }}' 19 | no_log: "{{ no_log }}" 20 | when: '"ca.crt" in route_tls["resources"][0]["data"]' 21 | -------------------------------------------------------------------------------- /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 | - cluster_role.yaml 10 | - role_binding.yaml 11 | - cluster_role_binding.yaml 12 | - leader_election_role.yaml 13 | - leader_election_role_binding.yaml 14 | # Comment the following 4 lines if you want to disable 15 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 16 | # which protects your /metrics endpoint. 17 | - auth_proxy_service.yaml 18 | - auth_proxy_role.yaml 19 | - auth_proxy_role_binding.yaml 20 | - auth_proxy_client_role.yaml 21 | -------------------------------------------------------------------------------- /roles/restore/tasks/deploy_galaxy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Combine spec_overrides with spec 4 | set_fact: 5 | spec: "{{ spec | default({}) | combine(spec_overrides) }}" 6 | no_log: "{{ no_log }}" 7 | 8 | - name: Deploy Galaxy 9 | k8s: 10 | state: "{{ state | default('present') }}" 11 | namespace: "{{ ansible_operator_meta.namespace }}" 12 | apply: yes 13 | definition: "{{ lookup('template', 'galaxy_object.yaml.j2') }}" 14 | wait: true 15 | 16 | - name: Remove ownerReferences to prevent garbage collection of new Galaxy CRO 17 | k8s: 18 | definition: 19 | apiVersion: '{{ deployment_api_version }}' 20 | kind: '{{ deployment_kind }}' 21 | metadata: 22 | name: '{{ deployment_name }}' 23 | namespace: '{{ ansible_operator_meta.namespace }}' 24 | ownerReferences: null 25 | -------------------------------------------------------------------------------- /roles/backup/templates/backup.pvc.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: {{ deployment_name }}-backup-claim 6 | namespace: "{{ backup_pvc_namespace }}" 7 | labels: 8 | app.kubernetes.io/name: '{{ deployment_type }}-backup-storage' 9 | app.kubernetes.io/instance: '{{ deployment_type }}-backup-storage-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: backup-storage 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | spec: 14 | accessModes: 15 | - ReadWriteOnce 16 | {% if backup_storage_class != '' %} 17 | storageClassName: {{ backup_storage_class }} 18 | {% endif %} 19 | resources: 20 | requests: 21 | storage: {{ backup_storage_requirements | default('5Gi', true) }} 22 | -------------------------------------------------------------------------------- /roles/common/tasks/object_storage_configuration.yml: -------------------------------------------------------------------------------- 1 | - set_fact: 2 | object_storage_secret: "{{ object_storage_s3_secret }}" 3 | when: 4 | - object_storage_s3_secret is defined 5 | 6 | - set_fact: 7 | object_storage_secret: "{{ object_storage_azure_secret }}" 8 | when: 9 | - object_storage_azure_secret is defined 10 | 11 | - set_fact: 12 | is_file_storage: false 13 | when: 14 | - object_storage_secret is defined 15 | 16 | - include_tasks: 17 | file: s3-storage-configuration.yml 18 | when: 19 | - not is_file_storage 20 | - object_storage_s3_secret is defined 21 | - object_storage_s3_secret | length 22 | 23 | - include_tasks: 24 | file: azure-storage-configuration.yml 25 | when: 26 | - not is_file_storage 27 | - object_storage_azure_secret is defined 28 | - object_storage_azure_secret | length 29 | 30 | -------------------------------------------------------------------------------- /roles/restore/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Required: specify name of Galaxy backup 3 | backup_name: '' 4 | 5 | # Required: specify a pre-created PVC (name) to restore from 6 | backup_pvc: '' 7 | backup_pvc_namespace: "{{ ansible_operator_meta.namespace }}" # deprecated 8 | 9 | # Required: backup name, found on the backup object 10 | backup_dir: '' 11 | 12 | admin_password_name: '' 13 | db_secret_name: '' 14 | storage_secret: '' 15 | signing_secret: '' 16 | container_token_secret: '' 17 | db_fields_encryption_secret: '' 18 | sso_secret: '' 19 | 20 | # Default cluster name 21 | cluster_name: '' # On most clusters, this is 'cluster.local' 22 | 23 | # Default resource requirements 24 | restore_resource_requirements: 25 | limits: 26 | cpu: "1000m" 27 | memory: "4096Mi" 28 | requests: 29 | cpu: "25m" 30 | memory: "32Mi" 31 | 32 | spec_overrides: {} 33 | -------------------------------------------------------------------------------- /roles/galaxy-worker/README.md: -------------------------------------------------------------------------------- 1 | Galaxy Worker 2 | =========== 3 | 4 | A role to setup Galaxy worker, yielding the following objects: 5 | 6 | * Deployment 7 | 8 | Role Variables 9 | -------------- 10 | 11 | * `worker`: A dictionary of galaxy-worker configuration 12 | * `replicas`: Number of pod replicas. 13 | * `image`: The image name. Default: quay.io/ansible/galaxy-ng 14 | * `image_version`: The image tag. Default: main 15 | 16 | Requirements 17 | ------------ 18 | 19 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 20 | 21 | Dependencies 22 | ------------ 23 | 24 | collections: 25 | 26 | - kubernetes.core 27 | - operator_sdk.util 28 | 29 | License 30 | ------- 31 | 32 | GPLv2+ 33 | 34 | Author Information 35 | ------------------ 36 | 37 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 38 | -------------------------------------------------------------------------------- /roles/postgres/templates/postgres_extra_settings.configmap.yaml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: '{{ ansible_operator_meta.name }}-postgres-extra-settings' 5 | namespace: '{{ ansible_operator_meta.namespace }}' 6 | labels: 7 | app.kubernetes.io/name: 'postgres-extra-settings' 8 | app.kubernetes.io/instance: 'postgres-{{ ansible_operator_meta.name }}' 9 | app.kubernetes.io/component: database 10 | app.kubernetes.io/version: '{{ supported_pg_version }}' 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | data: 14 | 99-overrides.conf: | 15 | {% for pg_setting in postgres_extra_settings %} 16 | {% if pg_setting.value is string %} 17 | {{ pg_setting.setting }} = '{{ pg_setting.value }}' 18 | {% else %} 19 | {{ pg_setting.setting }} = {{ pg_setting.value }} 20 | {% endif %} 21 | {% endfor %} 22 | -------------------------------------------------------------------------------- /roles/restore/tasks/import_vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Import cr_object variables 4 | block: 5 | - name: "Get {{ deployment_type }} object definition from pvc" 6 | k8s_exec: 7 | namespace: "{{ backup_pvc_namespace }}" 8 | pod: "{{ ansible_operator_meta.name }}-db-management" 9 | command: >- 10 | bash -c "cat '{{ backup_dir }}/cr_object'" 11 | register: cr_object 12 | 13 | - name: Create temp file for spec dict 14 | tempfile: 15 | state: file 16 | register: tmp_spec 17 | 18 | - name: Write spec vars to temp file 19 | copy: 20 | content: "{{ cr_object.stdout }}" 21 | dest: "{{ tmp_spec.path }}" 22 | mode: '0644' 23 | 24 | - name: Include spec vars to save them as a dict 25 | include_vars: "{{ tmp_spec.path }}" 26 | 27 | - name: Remove temp spec file 28 | ansible.builtin.file: 29 | path: "{{ tmp_spec.path }}" 30 | state: absent 31 | -------------------------------------------------------------------------------- /roles/galaxy-content/templates/galaxy-file-storage.pvc.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: "{{ ansible_operator_meta.name }}-file-storage" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | labels: 8 | app.kubernetes.io/name: '{{ deployment_type }}-storage' 9 | app.kubernetes.io/instance: '{{ deployment_type }}-storage-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: storage 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | spec: 14 | {% if file_storage_size is defined %} 15 | resources: 16 | requests: 17 | storage: "{{ file_storage_size }}" 18 | {% endif %} 19 | accessModes: 20 | - "{{ file_storage_access_mode }}" 21 | {% if file_storage_storage_class is defined %} 22 | storageClassName: '{{ file_storage_storage_class }}' 23 | {% endif %} 24 | -------------------------------------------------------------------------------- /roles/backup/tasks/dump_secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Get Secret Name 4 | set_fact: 5 | _name: "{{ galaxy_spec.spec[item] | default('') }}" 6 | 7 | - name: Backup secret if defined 8 | block: 9 | - name: Get secret 10 | k8s_info: 11 | version: v1 12 | kind: Secret 13 | namespace: '{{ ansible_operator_meta.namespace }}' 14 | name: "{{ _name }}" 15 | register: _secret 16 | no_log: "{{ no_log }}" 17 | 18 | - name: Set secret key 19 | set_fact: 20 | _data: "{{ _secret['resources'][0]['data'] }}" 21 | _type: "{{ _secret['resources'][0]['type'] }}" 22 | no_log: "{{ no_log }}" 23 | 24 | - name: Create and Add secret names and data to dictionary 25 | set_fact: 26 | secret_dict: "{{ secret_dict | default({}) | combine({item: { 'name': _name, 'data': _data, 'type': _type }}) }}" 27 | no_log: "{{ no_log }}" 28 | when: _name != '' 29 | -------------------------------------------------------------------------------- /roles/redis/templates/redis.service.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "{{ ansible_operator_meta.name }}-redis-svc" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | labels: 8 | app.kubernetes.io/name: 'redis' 9 | app.kubernetes.io/instance: 'redis-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: cache 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | spec: 14 | selector: 15 | app.kubernetes.io/name: 'redis' 16 | app.kubernetes.io/instance: 'redis-{{ ansible_operator_meta.name }}' 17 | app.kubernetes.io/component: cache 18 | app.kubernetes.io/part-of: '{{ deployment_type }}' 19 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 20 | ports: 21 | - protocol: TCP 22 | targetPort: 6379 23 | name: redis-6379 24 | port: 6379 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.ci/scripts/quay-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | #!/usr/bin/env bash 3 | 4 | # quay-push.sh: Push (Upload) image to quay. 5 | # Image must be already tagged. 6 | 7 | # TODO: These are already hardcoded in .travis.yml for the build task 8 | # 9 | # Ansible is an organization (not an individual user account) on Quay: 10 | # https://quay.io/organization/ansible 11 | # For test publishes, one can override this to any org or user. 12 | QUAY_PROJECT_NAME=${QUAY_PROJECT_NAME:-ansible} 13 | # The image name, AKA the Quay repo 14 | QUAY_REPO_NAME=${QUAY_REPO_NAME:-galaxy-operator} 15 | # The image tag 16 | QUAY_IMAGE_TAG=${QUAY_IMAGE_TAG:-latest} 17 | 18 | # TODO - Update with galaxy bot 19 | QUAY_BOT_USERNAME=${QUAY_BOT_USERNAME:-pulp+github} 20 | 21 | # Reference: https://adriankoshka.github.io/blog/posts/travis-and-quay/ 22 | echo "$QUAY_BOT_PASSWORD" | docker login -u "$QUAY_BOT_USERNAME" --password-stdin quay.io 23 | docker push "quay.io/$QUAY_PROJECT_NAME/$QUAY_REPO_NAME:$QUAY_IMAGE_TAG" 24 | -------------------------------------------------------------------------------- /CHANGES/.TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | {# TOWNCRIER TEMPLATE #} 3 | {% for section, _ in sections.items() %} 4 | {% set underline = underlines[0] %}{% if section %}{{section}} 5 | {{ underline * section|length }}{% set underline = underlines[1] %} 6 | 7 | {% endif %} 8 | 9 | {% if sections[section] %} 10 | {% for category, val in definitions.items() if category in sections[section]%} 11 | {{ definitions[category]['name'] }} 12 | {{ underline * definitions[category]['name']|length }} 13 | 14 | {% if definitions[category]['showcontent'] %} 15 | {% for text, values in sections[section][category].items() %} 16 | - {{ text }} 17 | {{ values|join(',\n ') }} 18 | {% endfor %} 19 | 20 | {% else %} 21 | - {{ sections[section][category]['']|join(', ') }} 22 | 23 | {% endif %} 24 | {% if sections[section][category]|length == 0 %} 25 | No significant changes. 26 | 27 | {% else %} 28 | {% endif %} 29 | 30 | {% endfor %} 31 | {% else %} 32 | No significant changes. 33 | 34 | 35 | {% endif %} 36 | {% endfor %} 37 | ---- 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/operator-framework/ansible-operator:v1.36.1 2 | 3 | ARG DEFAULT_GALAXY_VERSION 4 | ARG DEFAULT_GALAXY_UI_VERSION 5 | ARG OPERATOR_VERSION 6 | ENV DEFAULT_GALAXY_VERSION=${DEFAULT_GALAXY_VERSION} 7 | ENV DEFAULT_GALAXY_UI_VERSION=${DEFAULT_GALAXY_UI_VERSION} 8 | ENV OPERATOR_VERSION=${OPERATOR_VERSION} 9 | 10 | ENV ANSIBLE_FORCE_COLOR=true 11 | ENV ANSIBLE_SHOW_TASK_PATH_ON_FAILURE=true 12 | 13 | USER root 14 | RUN dnf update --security --bugfix -y 15 | 16 | USER ${USER_UID} 17 | 18 | COPY requirements.yml ${HOME}/requirements.yml 19 | RUN ansible-galaxy collection install --force -r ${HOME}/requirements.yml \ 20 | && chmod -R ug+rwx ${HOME}/.ansible 21 | 22 | COPY watches.yaml ${HOME}/watches.yaml 23 | COPY roles/ ${HOME}/roles/ 24 | COPY playbooks/ ${HOME}/playbooks/ 25 | 26 | ENTRYPOINT ["/tini", "--", "/usr/local/bin/ansible-operator", "run", \ 27 | "--watches-file=./watches.yaml", \ 28 | "--reconcile-period=0s", \ 29 | "--ansible-log-events=Tasks" \ 30 | ] 31 | -------------------------------------------------------------------------------- /roles/common/tasks/idle_deployment.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Scale down {{ deployment_type }} deployments 4 | kubernetes.core.k8s: 5 | state: present 6 | definition: 7 | apiVersion: apps/v1 8 | kind: Deployment 9 | metadata: 10 | name: "{{ ansible_operator_meta.name }}-{{ item }}" 11 | namespace: "{{ ansible_operator_meta.namespace }}" 12 | spec: 13 | replicas: 0 14 | loop: 15 | - api 16 | - content 17 | - web 18 | - worker 19 | - redis 20 | 21 | - name: Scale down PostgreSQL Statefulset 22 | kubernetes.core.k8s: 23 | state: present 24 | definition: 25 | apiVersion: apps/v1 26 | kind: StatefulSet 27 | metadata: 28 | name: "{{ ansible_operator_meta.name }}-postgres-{{ supported_pg_version }}" 29 | namespace: "{{ ansible_operator_meta.namespace }}" 30 | spec: 31 | replicas: 0 32 | when: managed_database | bool 33 | 34 | - name: End Playbook 35 | ansible.builtin.meta: end_play 36 | -------------------------------------------------------------------------------- /roles/galaxy-web/README.md: -------------------------------------------------------------------------------- 1 | Galaxy Web 2 | ======== 3 | 4 | A role to setup Galaxy NGINX web, yielding the following objects: 5 | 6 | * Deployment 7 | * Service 8 | * ConfigMap 9 | * Stores NGINX configs 10 | 11 | Role Variables 12 | -------------- 13 | 14 | * `web`: A dictionary of pulp-web configuration 15 | * `replicas`: Number of pod replicas. 16 | * `image_web`: The image name. Default: quay.io/ansible/galaxy-ui 17 | * `image_web_version`: The image tag. Default: main 18 | * `nginx_client_max_body_size`: Sets the maximum allowed size of the client request body. 19 | 20 | Requirements 21 | ------------ 22 | 23 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 24 | 25 | Dependencies 26 | ------------ 27 | 28 | collections: 29 | 30 | - kubernetes.core 31 | - operator_sdk.util 32 | 33 | License 34 | ------- 35 | 36 | GPLv2+ 37 | 38 | Author Information 39 | ------------------ 40 | 41 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 42 | -------------------------------------------------------------------------------- /roles/galaxy-api/templates/galaxy-api.service.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "{{ ansible_operator_meta.name }}-api-svc" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | labels: 8 | app.kubernetes.io/name: '{{ deployment_type }}-api' 9 | app.kubernetes.io/instance: '{{ deployment_type }}-api-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: api 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | spec: 14 | selector: 15 | app.kubernetes.io/name: '{{ deployment_type }}-api' 16 | app.kubernetes.io/instance: '{{ deployment_type }}-api-{{ ansible_operator_meta.name }}' 17 | app.kubernetes.io/component: api 18 | app.kubernetes.io/part-of: '{{ deployment_type }}' 19 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 20 | ports: 21 | - protocol: TCP 22 | targetPort: 8000 23 | name: api-8000 24 | port: 8000 25 | -------------------------------------------------------------------------------- /.ci/scripts/signing_metadata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | # get galaxy admin password 6 | GALAXY_ADMIN_PASSWORD=$(kubectl get secret/example-galaxy-admin-password -ojsonpath='{.data.password}'|base64 -d) 7 | 8 | # verify the list of signing services (keeping it in a different variable to make troubleshooting/debug easier) 9 | SIGNING_SVC=$(kubectl exec deployment/example-galaxy-api -- curl -u admin:$GALAXY_ADMIN_PASSWORD -sL localhost:24817/galaxy/api/v3/signing-services/) 10 | 11 | # get only the count of services found 12 | SVC_COUNT=$(echo $SIGNING_SVC | jq .count) 13 | 14 | # check if the 2 services were found 15 | if [[ $SVC_COUNT != 2 ]] ; then 16 | echo "Could not find all signing services!" 17 | exit 1 18 | fi 19 | 20 | # check if the the gpg key is in the api's keyring 21 | kubectl exec deployment/example-galaxy-api -- gpg -k joe@foo.bar 2>/dev/null 22 | 23 | # check if the the gpg key is in the worker's keyring 24 | kubectl exec deployment/example-galaxy-worker -- gpg -k joe@foo.bar 2>/dev/null 25 | 26 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.galaxy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: galaxy-demo 5 | spec: 6 | ingress_type: Ingress 7 | image: quay.io/ansible/galaxy-ng 8 | image_version: latest 9 | image_web: quay.io/ansible/galaxy-ui 10 | image_web_version: latest 11 | admin_password_secret: "example-galaxy-admin-password" 12 | container_token_secret: "container-auth" 13 | storage_type: File 14 | # k3s local-path requires this 15 | file_storage_access_mode: "ReadWriteMany" 16 | # We have a little over 10GB free on GHA VMs/instances 17 | file_storage_size: "10Gi" 18 | content: 19 | replicas: 1 20 | resource_requirements: 21 | requests: 22 | cpu: 150m 23 | memory: 256Mi 24 | worker: 25 | replicas: 1 26 | resource_requirements: 27 | requests: 28 | cpu: 150m 29 | memory: 256Mi 30 | web: 31 | replicas: 1 32 | resource_requirements: 33 | requests: 34 | cpu: 100m 35 | memory: 256Mi 36 | -------------------------------------------------------------------------------- /roles/galaxy-content/README.md: -------------------------------------------------------------------------------- 1 | Galaxy Content 2 | ============ 3 | 4 | A role to setup content serving in Galaxy, yielding the following objects: 5 | 6 | * Deployment 7 | * Service 8 | 9 | Role Variables 10 | -------------- 11 | 12 | * `content`: A dictionary of galaxy-content configuration 13 | * `replicas`: Number of pod replicas. 14 | * `log_level`: The desired log level. 15 | * `image`: The image name. Default: quay.io/ansible/galaxy-ng 16 | * `image_version`: The image tag. Default: main 17 | * `gunicorn_timeout`: The timeout for the gunicorn process. Default: computed based on `client_request_timeout` 18 | 19 | Requirements 20 | ------------ 21 | 22 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 23 | 24 | Dependencies 25 | ------------ 26 | 27 | collections: 28 | 29 | - kubernetes.core 30 | - operator_sdk.util 31 | 32 | License 33 | ------- 34 | 35 | GPLv2+ 36 | 37 | Author Information 38 | ------------------ 39 | 40 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 41 | -------------------------------------------------------------------------------- /roles/galaxy-content/templates/galaxy-content.service.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "{{ ansible_operator_meta.name }}-content-svc" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | labels: 8 | app.kubernetes.io/name: '{{ deployment_type }}-content' 9 | app.kubernetes.io/instance: '{{ deployment_type }}-content-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: content-server 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | spec: 14 | selector: 15 | app.kubernetes.io/name: '{{ deployment_type }}-content' 16 | app.kubernetes.io/instance: '{{ deployment_type }}-content-{{ ansible_operator_meta.name }}' 17 | app.kubernetes.io/component: content-server 18 | app.kubernetes.io/part-of: '{{ deployment_type }}' 19 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 20 | ports: 21 | - protocol: TCP 22 | targetPort: 24816 23 | name: content-24816 24 | port: 24816 25 | -------------------------------------------------------------------------------- /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=galaxy-operator 8 | LABEL operators.operatorframework.io.bundle.channels.v1=alpha 9 | LABEL operators.operatorframework.io.bundle.channel.default.v1=alpha 10 | LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.23.0 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 | -------------------------------------------------------------------------------- /roles/galaxy-api/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Here we use _galaxy_ansible_com_galaxy to get un-modified cr 3 | # see: https://github.com/operator-framework/operator-sdk/issues/1770 4 | raw_spec: "{{ vars['_galaxy_ansible_com_galaxy']['spec'] }}" 5 | 6 | container_auth_public_key_name: 'container_auth_public_key.pem' 7 | container_auth_private_key_name: 'container_auth_private_key.pem' 8 | 9 | # common default values 10 | client_request_timeout: 30 11 | 12 | # Keycloack SSO settings 13 | keycloak_admin_role: hubadmin 14 | keycloak_port: 443 15 | keycloak_protocol: https 16 | 17 | sso_secret_data_available: false 18 | social_auth_keycloak_key_available: false 19 | social_auth_keycloak_secret_available: false 20 | social_auth_keycloak_public_key_available: false 21 | keycloak_host_available: false 22 | keycloak_protocol_available: false 23 | keycloak_port_available: false 24 | keycloak_realm_available: false 25 | 26 | # gunicorn default values 27 | gunicorn_timeout: "{{ (([(client_request_timeout | int), 10] | max) / 3) | int }}" 28 | gunicorn_timeout_grace_period: 2 29 | gunicorn_api_workers: 2 30 | -------------------------------------------------------------------------------- /roles/galaxy-config/tasks/galaxy_server_secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - k8s_status: 4 | api_version: "{{ api_version }}" 5 | kind: "{{ kind }}" 6 | name: "{{ ansible_operator_meta.name }}" 7 | namespace: "{{ ansible_operator_meta.namespace }}" 8 | conditions: 9 | - type: "{{ deployment_type|capitalize }}-API-Ready" 10 | message: "Creating {{ ansible_operator_meta.name }}-server Secret resource" 11 | reason: CreatingServerSecret 12 | status: "False" 13 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 14 | 15 | - name: galaxy-server secret 16 | k8s: 17 | state: "{{ deployment_state }}" 18 | definition: "{{ lookup('template', 'templates/galaxy-server.secret.yaml.j2') | from_yaml }}" 19 | register: galaxy_server_secret 20 | no_log: "{{ no_log }}" 21 | 22 | - name: Get galaxy-server secret 23 | k8s_info: 24 | kind: Secret 25 | namespace: '{{ ansible_operator_meta.namespace }}' 26 | name: '{{ ansible_operator_meta.name }}-server' 27 | register: galaxy_server_secret_contents 28 | no_log: "{{ no_log }}" 29 | -------------------------------------------------------------------------------- /.ci/scripts/kubelinter.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # coding=utf-8 3 | 4 | set -euo pipefail 5 | 6 | RELEASE_INFO=$(curl --silent --show-error --fail https://api.github.com/repos/stackrox/kube-linter/releases/latest) 7 | RELEASE_NAME=$(echo "${RELEASE_INFO}" | jq --raw-output ".name") 8 | LOCATION=$(echo "${RELEASE_INFO}" \ 9 | | jq --raw-output ".assets[].browser_download_url" \ 10 | | grep --fixed-strings kube-linter-linux.tar.gz) 11 | 12 | # Remove trailing whitespace 13 | RELEASE_NAME="${RELEASE_NAME%"${RELEASE_NAME##*[![:space:]]}"}" 14 | TARGET=kube-linter-linux-${RELEASE_NAME}.tar.gz 15 | # Skip downloading release if downloaded already, e.g. when the action is used multiple times. 16 | if [ ! -e $TARGET ]; then 17 | curl --silent --show-error --fail --location --output $TARGET "$LOCATION" 18 | tar -xf $TARGET 19 | fi 20 | mkdir -p lint 21 | sudo -E kubectl get galaxy,galaxybackup,galaxyrestore,pvc,configmap,serviceaccount,secret,networkpolicy,ingress,service,deployment,statefulset,hpa,job,cronjob -o yaml > ./lint/k8s-all.yaml 22 | ./kube-linter lint ./lint --config .ci/assets/kubernetes/.kube-linter.yaml 23 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.molecule.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | spec: 6 | image_version: nightly 7 | image_web_version: nightly 8 | admin_password_secret: "example-galaxy-admin-password" 9 | storage_type: File 10 | ingress_type: nodeport 11 | # k3s local-path requires this 12 | file_storage_access_mode: "ReadWriteOnce" 13 | # We have a little over 10GB free on GHA VMs/instances 14 | file_storage_size: "10Gi" 15 | content: 16 | replicas: 1 17 | resource_requirements: 18 | requests: 19 | cpu: 150m 20 | memory: 256Mi 21 | limits: 22 | cpu: 800m 23 | memory: 1Gi 24 | worker: 25 | replicas: 1 26 | resource_requirements: 27 | requests: 28 | cpu: 150m 29 | memory: 256Mi 30 | limits: 31 | cpu: 800m 32 | memory: 1Gi 33 | web: 34 | replicas: 1 35 | resource_requirements: 36 | requests: 37 | cpu: 100m 38 | memory: 256Mi 39 | limits: 40 | cpu: 800m 41 | memory: 1Gi 42 | -------------------------------------------------------------------------------- /roles/restore/tasks/update_status.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set apiVersion and kind variables 3 | set_fact: 4 | api_version: '{{ hostvars["localhost"]["inventory_file"].split("/")[4:6] | join("/") }}' 5 | kind: '{{ hostvars["localhost"]["inventory_file"].split("/")[6] }}' 6 | 7 | - name: Update restore status 8 | operator_sdk.util.k8s_status: 9 | api_version: '{{ api_version }}' 10 | kind: "{{ kind }}" 11 | name: "{{ ansible_operator_meta.name }}" 12 | namespace: "{{ ansible_operator_meta.namespace }}" 13 | status: 14 | restoreComplete: true 15 | when: restore_complete is defined 16 | 17 | - name: Add RestoreComplete condition 18 | operator_sdk.util.k8s_status: 19 | api_version: '{{ api_version }}' 20 | kind: "{{ kind }}" 21 | name: "{{ ansible_operator_meta.name }}" 22 | namespace: "{{ ansible_operator_meta.namespace }}" 23 | conditions: 24 | - type: RestoreComplete 25 | status: "True" 26 | reason: Successful 27 | message: "The Restore has been completed" 28 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 29 | -------------------------------------------------------------------------------- /roles/galaxy-web/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ingress_type: none 3 | 4 | # TLS secret for the ingress. The secret either has to exist before hand with 5 | # the corresponding cert and key or just be an indicator for where an automated 6 | # process like cert-manager (enabled via annotations) will store the TLS 7 | # certificate and key. 8 | ingress_tls_secret: '' 9 | 10 | loadbalancer_protocol: 'http' 11 | loadbalancer_port: '80' 12 | 13 | service_annotations: '' 14 | 15 | # The TLS termination mechanism to use to access 16 | # the services. Supported mechanism are: edge, passthrough 17 | # 18 | route_tls_termination_mechanism: edge 19 | 20 | # Secret to lookup that provide the TLS specific 21 | # credentials to deploy 22 | # 23 | route_tls_secret: '' 24 | 25 | ca_trust_bundle: "/etc/pki/tls/certs/ca-bundle.crt" 26 | 27 | galaxy_webserver_static_dir: "/opt/app-root/src" 28 | galaxy_nginx_conf_dir: "/etc/nginx/default.d" 29 | 30 | # Here we use _galaxy_ansible_com_galaxy to get un-modified cr 31 | # see: https://github.com/operator-framework/operator-sdk/issues/1770 32 | raw_spec: "{{ vars['_galaxy_ansible_com_galaxy']['spec'] }}" 33 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: galaxy 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: galaxy-operator- 10 | 11 | # Labels to add to all resources and selectors. 12 | #- includeSelectors: true 13 | # pairs: 14 | # someName: someValue 15 | 16 | resources: 17 | - ../crd 18 | - ../rbac 19 | - ../manager 20 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 21 | #- ../prometheus 22 | 23 | # Protect the /metrics endpoint by putting it behind auth. 24 | # If you want your controller-manager to expose the /metrics 25 | # endpoint w/o any authn/z, please comment the following line. 26 | apiVersion: kustomize.config.k8s.io/v1beta1 27 | kind: Kustomization 28 | patches: 29 | - path: manager_auth_proxy_patch.yaml 30 | 31 | # Mount the controller config file for loading manager configurations 32 | # through a ComponentConfig type 33 | #- manager_config_patch.yaml 34 | -------------------------------------------------------------------------------- /roles/common/tasks/set_images.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set default galaxy-api image 3 | set_fact: 4 | _default_image: "{{ _image }}:{{ _image_version }}" 5 | 6 | - name: Set user provided galaxy-api image 7 | set_fact: 8 | _custom_image: "{{ image }}:{{ image_version }}" 9 | when: 10 | - image is defined and image != '' 11 | - image_version is defined and image_version != '' 12 | 13 | - name: Set galaxy-api image URL 14 | set_fact: 15 | _image: "{{ _custom_image | default(lookup('env', 'RELATED_IMAGE_GALAXY')) | default(_default_image, true) }}" 16 | 17 | - name: Set default galaxy-web image 18 | set_fact: 19 | _default_image_web: "{{ _image_web }}:{{ _image_web_version }}" 20 | 21 | - name: Set user provided galaxy-web image 22 | set_fact: 23 | _custom_image_web: "{{ image_web }}:{{ image_web_version }}" 24 | when: 25 | - image_web is defined and image_web != '' 26 | - image_web_version is defined and image_web_version != '' 27 | 28 | - name: Set galaxy-web image URL 29 | set_fact: 30 | _image_web: "{{ _custom_image_web | default(lookup('env', 'RELATED_IMAGE_GALAXY_WEB')) | default(_default_image_web, true) }}" 31 | -------------------------------------------------------------------------------- /roles/backup/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Obtain custom resource information 4 | set_fact: 5 | custom_resource: "{{ hostvars[inventory_hostname][custom_resource_key] }}" 6 | custom_resource_status: "{{ hostvars[inventory_hostname][custom_resource_key]['status'] | default({}) }}" 7 | 8 | - block: 9 | - include_tasks: ../../common/tasks/check_k8s_or_openshift.yml 10 | 11 | - include_tasks: init.yml 12 | 13 | - include_tasks: postgres.yml 14 | 15 | - include_tasks: custom_resource.yml 16 | 17 | - include_tasks: secrets.yml 18 | 19 | - include_tasks: remove_management_pod.yml 20 | 21 | - name: Apply kubernetes job to backup /var/lib/pulp content 22 | k8s: 23 | state: present 24 | definition: "{{ lookup('template', 'backup-content-k8s-job.yaml.j2') }}" 25 | when: storage_type | lower == 'file' 26 | 27 | - name: Set flag signifying this backup was successful 28 | set_fact: 29 | backup_complete: "{{ _backup_dir }}" 30 | 31 | - include_tasks: cleanup.yml 32 | 33 | when: 34 | - custom_resource_status['backupDirectory'] is not defined 35 | 36 | - name: Update status variables 37 | include_tasks: update_status.yml 38 | -------------------------------------------------------------------------------- /down.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # down.sh: Delete galaxy-operator from K8s 4 | 5 | # CentOS 7 /etc/sudoers , and non-interactive shells (vagrant provisions) 6 | # do not include /usr/local/bin , Which k3s installs to. 7 | # But we do not want to break other possible kubectl implementations by 8 | # hardcoding /usr/local/bin/kubectl . 9 | # And this entire script may be run with sudo. 10 | # So if kubectl is not in the PATH, assume /usr/local/bin/kubectl . 11 | if command -v kubectl > /dev/null; then 12 | KUBECTL=$(command -v kubectl) 13 | elif [ -x /usr/local/bin/kubectl ]; then 14 | KUBECTL=/usr/local/bin/kubectl 15 | else 16 | echo "$0: ERROR 1: Cannot find kubectl" 17 | fi 18 | 19 | make undeploy 20 | 21 | # It doesn't matter which cr we specify; the metadata up top is the same. 22 | $KUBECTL delete -f config/samples/galaxy_v1beta1_galaxy_cr.default.yaml 23 | $KUBECTL delete -f config/crd/bases/galaxy_v1beta1_galaxy_crd.yaml 24 | $KUBECTL delete -f config/crd/bases/galaxy_v1beta1_galaxybackup_crd.yaml 25 | $KUBECTL delete -f config/crd/bases/galaxy_v1beta1_galaxyrestore_crd.yaml 26 | 27 | if [[ "$CI_TEST" == "true" ]]; then 28 | $KUBECTL delete -f .ci/assets/kubernetes/galaxy-admin-password.secret.yaml 29 | fi 30 | -------------------------------------------------------------------------------- /roles/restore/README.md: -------------------------------------------------------------------------------- 1 | Restore 2 | ======== 3 | 4 | The purpose of this role is to restore your Galaxy deployment from a backup. This includes: 5 | - backup of the PostgreSQL database 6 | - custom user config file 7 | 8 | Role Variables 9 | -------------- 10 | 11 | * `backup_name`: The name of the galaxy backup custom resource to restore from 12 | * `postgres_label_selector`: The label selector for an external container based database 13 | * `restore_resource_requirements`: The resources limits and requests for restore CR 14 | 15 | 16 | Defining resources limits and requests for restore CR 17 | 18 | ``` 19 | restore_resource_requirements: 20 | limits: 21 | cpu: "1000m" 22 | memory: "4096Mi" 23 | requests: 24 | cpu: "25m" 25 | memory: "32Mi" 26 | ``` 27 | 28 | Requirements 29 | ------------ 30 | 31 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 32 | 33 | Dependencies 34 | ------------ 35 | 36 | collections: 37 | 38 | - kubernetes.core 39 | - operator_sdk.util 40 | 41 | License 42 | ------- 43 | 44 | GPLv2+ 45 | 46 | Author Information 47 | ------------------ 48 | 49 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 50 | -------------------------------------------------------------------------------- /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 | #- includeSelectors: true 8 | # pairs: 9 | # someName: someValue 10 | 11 | patches: 12 | - patch: |- 13 | - op: replace 14 | path: /subjects/0/namespace 15 | value: osdk-test 16 | target: 17 | kind: RoleBinding 18 | - patch: |- 19 | - op: replace 20 | path: /subjects/0/namespace 21 | value: osdk-test 22 | target: 23 | kind: ClusterRoleBinding 24 | - patch: |- 25 | - op: replace 26 | path: /subjects/0/name 27 | value: osdk-sa 28 | target: 29 | kind: RoleBinding 30 | - patch: |- 31 | - op: replace 32 | path: /subjects/0/name 33 | value: osdk-sa 34 | target: 35 | kind: ClusterRoleBinding 36 | 37 | patchesStrategicMerge: 38 | - manager_image.yaml 39 | - debug_logs_patch.yaml 40 | - ../default/manager_auth_proxy_patch.yaml 41 | 42 | apiVersion: kustomize.config.k8s.io/v1beta1 43 | kind: Kustomization 44 | resources: 45 | - ../crd 46 | - ../rbac 47 | - ../manager 48 | images: 49 | - name: testing 50 | newName: testing-operator 51 | -------------------------------------------------------------------------------- /roles/restore/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Obtain custom resource information 3 | set_fact: 4 | custom_resource: "{{ hostvars[inventory_hostname][custom_resource_key] }}" 5 | custom_resource_status: "{{ hostvars[inventory_hostname][custom_resource_key]['status'] | default({}) }}" 6 | 7 | - block: 8 | - include_tasks: ../../common/tasks/check_k8s_or_openshift.yml 9 | 10 | - include_tasks: init.yml 11 | 12 | - include_tasks: import_vars.yml 13 | 14 | - include_tasks: secrets.yml 15 | 16 | - include_tasks: deploy_galaxy.yml 17 | 18 | - include_tasks: postgres.yml 19 | 20 | - include_tasks: remove_management_pod.yml 21 | 22 | - name: Apply kubernetes job to restore /var/lib/pulp content 23 | k8s: 24 | state: present 25 | definition: "{{ lookup('template', 'restore-content-k8s-job.yaml.j2') }}" 26 | when: storage_type | lower == 'file' 27 | 28 | - name: Set flag signifying this restore was successful 29 | set_fact: 30 | restore_complete: True 31 | 32 | - include_tasks: cleanup.yml 33 | 34 | when: 35 | - custom_resource_status['restoreComplete'] is not defined 36 | 37 | - name: Update status variables 38 | include_tasks: update_status.yml 39 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | spec: 6 | no_log: false 7 | admin_password_secret: "example-galaxy-admin-password" 8 | storage_type: File 9 | ingress_type: nodeport 10 | # k3s local-path requires this 11 | file_storage_access_mode: "ReadWriteMany" 12 | # We have a little over 10GB free on GHA VMs/instances 13 | file_storage_size: "10Gi" 14 | container_token_secret: "container-auth" 15 | pulp_settings: 16 | allowed_export_paths: 17 | - /tmp 18 | allowed_import_paths: 19 | - /tmp 20 | telemetry: false 21 | content: 22 | replicas: 1 23 | resource_requirements: 24 | requests: 25 | cpu: 150m 26 | memory: 256Mi 27 | limits: 28 | cpu: 800m 29 | memory: 1Gi 30 | worker: 31 | replicas: 1 32 | resource_requirements: 33 | requests: 34 | cpu: 150m 35 | memory: 256Mi 36 | limits: 37 | cpu: 800m 38 | memory: 1Gi 39 | web: 40 | replicas: 1 41 | resource_requirements: 42 | requests: 43 | cpu: 100m 44 | memory: 256Mi 45 | limits: 46 | cpu: 800m 47 | memory: 1Gi 48 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | options: 5 | requirements-file: requirements.yml 6 | driver: 7 | name: delegated 8 | lint: | 9 | set -e 10 | yamllint -d "{extends: relaxed, rules: {line-length: {max: 120}}}" . 11 | platforms: 12 | - name: cluster 13 | groups: 14 | - k8s 15 | provisioner: 16 | name: ansible 17 | config_options: 18 | defaults: 19 | stdout_callback: debug 20 | callback_enabled: profile_tasks 21 | lint: | 22 | set -e 23 | ansible-lint 24 | inventory: 25 | group_vars: 26 | all: 27 | namespace: ${TEST_OPERATOR_NAMESPACE:-osdk-test} 28 | host_vars: 29 | localhost: 30 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 31 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 32 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 33 | operator_image: ${OPERATOR_IMAGE:-"quay.io/ansible/galaxy-operator:main"} 34 | operator_pull_policy: ${OPERATOR_PULL_POLICY:-"Always"} 35 | kustomize: ${KUSTOMIZE_PATH:-kustomize} 36 | env: 37 | K8S_AUTH_KUBECONFIG: ${KUBECONFIG:-"~/.kube/config"} 38 | verifier: 39 | name: ansible 40 | lint: | 41 | set -e 42 | ansible-lint 43 | -------------------------------------------------------------------------------- /roles/backup/tasks/dump_generated_secret.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Get secret name 4 | set_fact: 5 | _name: "{{ status[item] }}" 6 | 7 | - name: "Fail if status is not set on {{ deployment_type }} CR" 8 | block: 9 | - name: Set error message 10 | set_fact: 11 | error_msg: "{{ item }} status is not set on {{ deployment_type }} object yet" 12 | 13 | - name: Handle error 14 | import_tasks: error_handling.yml 15 | 16 | - name: Fail early if secret name status is not set 17 | fail: 18 | msg: "{{ error_msg }}" 19 | when: _name is not defined or _name == '' 20 | 21 | - name: Get secret 22 | k8s_info: 23 | version: v1 24 | kind: Secret 25 | namespace: '{{ ansible_operator_meta.namespace }}' 26 | name: "{{ _name }}" 27 | register: _secret 28 | no_log: "{{ no_log }}" 29 | 30 | - name: Set secret data 31 | set_fact: 32 | _data: "{{ _secret['resources'][0]['data'] }}" 33 | _type: "{{ _secret['resources'][0]['type'] }}" 34 | no_log: "{{ no_log }}" 35 | 36 | - name: Create and Add secret names and data to dictionary 37 | set_fact: 38 | secret_dict: "{{ secret_dict | default({}) | combine({ item: {'name': _name, 'data': _data, 'type': _type }}) }}" 39 | no_log: "{{ no_log }}" 40 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.ocp.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: ocp-example 5 | spec: 6 | no_log: false 7 | admin_password_secret: "example-galaxy-admin-password" 8 | storage_type: File 9 | ingress_type: route 10 | route_host: route_host_placeholder 11 | route_tls_termination_mechanism: edge 12 | # k3s local-path requires this 13 | file_storage_access_mode: "ReadWriteOnce" 14 | # We have a little over 10GB free on GHA VMs/instances 15 | file_storage_size: "10Gi" 16 | pulp_settings: 17 | allowed_export_paths: 18 | - /tmp 19 | allowed_import_paths: 20 | - /tmp 21 | telemetry: false 22 | content: 23 | replicas: 1 24 | resource_requirements: 25 | requests: 26 | cpu: 150m 27 | memory: 256Mi 28 | limits: 29 | cpu: 800m 30 | memory: 1Gi 31 | worker: 32 | replicas: 1 33 | resource_requirements: 34 | requests: 35 | cpu: 150m 36 | memory: 256Mi 37 | limits: 38 | cpu: 800m 39 | memory: 1Gi 40 | web: 41 | replicas: 1 42 | resource_requirements: 43 | requests: 44 | cpu: 100m 45 | memory: 256Mi 46 | limits: 47 | cpu: 800m 48 | memory: 1Gi 49 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Pull Request Checklist 5 | ------------------------ 6 | 1. Make sure your change does not break idempotency tests. See [Testing](#Testing) 7 | (or let CI run the tests for you if you are certain it is idempotent.) 8 | If a task cannot be made idempotent, add the tag [molecule-idempotence-notest](https://github.com/ansible-community/molecule/issues/816#issuecomment-573319053). 9 | 2. Unless a change is small or doesn't affect users, create an issue on 10 | [github](https://github.com/ansible/galaxy-operator/issues/new). 11 | 3. Push your branch to your fork and open a [Pull request across forks.](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) 12 | 4. Add GitHub labels as appropriate. 13 | 14 | Development Guide 15 | ----------------- 16 | 17 | See the Openshift [development guide](./development.md) for details on how to deploy on Openshift. 18 | 19 | Docs Testing 20 | ------------ 21 | 22 | Cross-platform: 23 | ``` 24 | pip install mkdocs pymdown-extensions mkdocs-material mike mkdocs-git-revision-date-plugin 25 | ``` 26 | 27 | Then: 28 | ``` 29 | mkdocs serve 30 | ``` 31 | Click the link it outputs. As you save changes to files modified in your editor, 32 | the browser will automatically show the new content. 33 | -------------------------------------------------------------------------------- /.github/workflows/build-operator-image.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: Build Operator Image 4 | 5 | on: 6 | push: 7 | branches: [main] 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | name: Push main image 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Log into registry ghcr.io 17 | uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 18 | with: 19 | registry: ghcr.io 20 | username: ${{ github.actor }} 21 | password: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | - name: Log into registry quay.io 24 | uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 25 | with: 26 | registry: quay.io/ansible/ 27 | username: ${{ secrets.QUAY_USER }} 28 | password: ${{ secrets.QUAY_TOKEN }} 29 | 30 | - name: Build and Store Image @ghcr 31 | run: | 32 | IMG=ghcr.io/${{ github.repository_owner }}/galaxy-operator:${{ github.sha }} make docker-buildx 33 | 34 | - name: Publish Image to quay.io/ansible/galaxy-operator:main 35 | run: | 36 | docker buildx imagetools create ghcr.io/${{ github.repository_owner }}/galaxy-operator:${{ github.sha }} --tag quay.io/ansible/galaxy-operator:main 37 | -------------------------------------------------------------------------------- /roles/redis/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: redis persistent volume claim 3 | k8s: 4 | state: "{{ deployment_state }}" 5 | definition: "{{ lookup('template', 'templates/' + item + '.pvc.yaml.j2') | from_yaml }}" 6 | with_items: 7 | - redis 8 | when: redis_data_persistence | bool 9 | 10 | - name: redis service 11 | k8s: 12 | state: "{{ deployment_state }}" 13 | definition: "{{ lookup('template', 'templates/' + item + '.service.yaml.j2') | from_yaml }}" 14 | with_items: 15 | - redis 16 | 17 | - name: Set user provided redis image 18 | set_fact: 19 | _custom_redis_image: "{{ redis_image }}" 20 | when: 21 | - redis_image is defined and redis_image != '' 22 | 23 | - name: Set Redis image URL 24 | set_fact: 25 | _redis_image: "{{ _custom_redis_image | default(lookup('env', 'RELATED_IMAGE_GALAXY_REDIS')) | default(_redis_image, true) }}" 26 | 27 | - name: Set node affinity 28 | set_fact: 29 | _node_affinity: "{{ raw_spec['affinity']['node_affinity'] | default({}) }}" 30 | when: affinity is defined and affinity.node_affinity is defined 31 | 32 | - name: redis deployment 33 | k8s: 34 | state: "{{ deployment_state }}" 35 | definition: "{{ lookup('template', 'templates/' + item + '.deployment.yaml.j2') | from_yaml }}" 36 | with_items: 37 | - redis 38 | -------------------------------------------------------------------------------- /roles/backup/tasks/custom_resource.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: "Set {{ deployment_type }} object" 4 | set_fact: 5 | _galaxy: "{{ cr_spec }}" 6 | _db_secret_value: "{{ status['databaseConfigurationSecret'] }}" 7 | _admin_password_secret: "{{ status['adminPasswordSecret'] }}" 8 | _db_fields_encryption_secret: "{{ status['dbFieldsEncryptionSecret'] }}" 9 | _container_token_secret: "{{ status['containerTokenSecret'] }}" 10 | 11 | - name: Set names of backed up secrets in the CR spec 12 | set_fact: 13 | _galaxy: "{{ _galaxy | combine ({ item.key : item.value }) }}" 14 | with_items: 15 | - {"key": "db_fields_encryption_secret", "value": "{{ _db_fields_encryption_secret }}"} 16 | - {"key": "admin_password_secret", "value": "{{ _admin_password_secret }}"} 17 | - {"key": "postgres_configuration", "value": "{{ _db_secret_value }}" } 18 | - {"key": "container_token_secret", "value": "{{ _container_token_secret }}"} 19 | 20 | - name: "Set {{ deployment_type }} object" 21 | set_fact: 22 | galaxy_spec: 23 | spec: "{{ _galaxy }}" 24 | 25 | - name: "Write {{ deployment_type }} object to pvc" 26 | k8s_cp: 27 | namespace: "{{ backup_pvc_namespace }}" 28 | pod: "{{ ansible_operator_meta.name }}-db-management" 29 | remote_path: "{{ _backup_dir }}/cr_object" 30 | content: "{{ galaxy_spec | to_yaml }}" 31 | -------------------------------------------------------------------------------- /roles/common/tasks/check_k8s_or_openshift.yml: -------------------------------------------------------------------------------- 1 | - name: Get information about the cluster 2 | set_fact: 3 | api_groups: "{{ lookup('k8s', cluster_info='api_groups') }}" 4 | when: 5 | - not is_openshift 6 | - not is_k8s 7 | 8 | - name: Determine the cluster type 9 | set_fact: 10 | is_openshift: "{{ True if 'route.openshift.io' in api_groups else False }}" 11 | is_k8s: "{{ False if 'route.openshift.io' in api_groups else True }}" 12 | when: 13 | - not is_openshift 14 | - not is_k8s 15 | 16 | # Indicate what kind of cluster we are in (OpenShift or Kubernetes). 17 | - debug: 18 | msg: "CLUSTER TYPE: is_openshift={{ is_openshift }}; is_k8s={{ is_k8s }}" 19 | 20 | - block: 21 | - k8s_status: 22 | api_version: "{{ api_version }}" 23 | kind: "{{ kind }}" 24 | name: "{{ ansible_operator_meta.name }}" 25 | namespace: "{{ ansible_operator_meta.namespace }}" 26 | conditions: 27 | - type: "{{ deployment_type|capitalize }}-API-Ready" 28 | message: Cannot determine what type of cluster we are in 29 | reason: FailedToIdentifyClusterType 30 | status: "False" 31 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 32 | 33 | - fail: 34 | msg: "Cannot determine what type of cluster we are in" 35 | 36 | when: 37 | - not is_openshift 38 | - not is_k8s 39 | -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | # Development Guide 2 | 3 | There are development scripts and yaml examples in the [`dev/`](../dev) directory that, along with the ansible-up.sh and ansible-down.sh scripts in the root of the repo, can be used to build, deploy and test changes made to the gateway operator. 4 | 5 | 6 | ## Build and Deploy 7 | 8 | 9 | If you clone the repo, and make sure you are logged in at the CLI with oc and your cluster, you can run: 10 | 11 | ``` 12 | export QUAY_USER=username 13 | export NAMESPACE=galaxy 14 | export TAG=test 15 | ./ansible-up.sh 16 | ``` 17 | 18 | You can add those variables to your .bashrc file so that you can just run `./ansible-up.sh` in the future. 19 | 20 | > Note: the first time you run this, it will create quay.io repos on your fork. You will need to either make those public, or create a global pull secret on your Openshift cluster. 21 | 22 | About 5 minutes after the script completes, you will have a fully functional Galaxy deployment. To get the URL: 23 | 24 | ```bash 25 | oc get route 26 | ``` 27 | 28 | To get the admin password: 29 | 30 | ```bash 31 | oc get secret admin-password-secret -o jsonpath={.data.password} | base64 --decode 32 | ``` 33 | 34 | 35 | ## Clean up 36 | 37 | 38 | Same thing for cleanup, just run ./ansible-down.sh and it will clean up your namespace on that cluster 39 | 40 | 41 | ``` 42 | ./ansible-down.sh 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /roles/backup/README.md: -------------------------------------------------------------------------------- 1 | Backup 2 | ======== 3 | 4 | The purpose of this role is to create a backup of your Galaxy deployment. This includes: 5 | - backup of the PostgreSQL database 6 | - custom user config file 7 | 8 | Role Variables 9 | -------------- 10 | 11 | * `deployment_name`: The name of the galaxy custom resource to backup 12 | * `backup_pvc`: The name of the PVC to uses for backup 13 | * `backup_storage_requirements`: The size of storage for the PVC created by operator if one is not supplied 14 | * `backup_resource_requirements`: The size of storage for the PVC created by operator if one is not supplied 15 | * `backup_storage_class`: The storage class to be used for the backup PVC 16 | 17 | 18 | Defining resources limits and request for backup CR 19 | 20 | ``` 21 | backup_resource_requirements: 22 | limits: 23 | cpu: "1000m" 24 | memory: "4096Mi" 25 | requests: 26 | cpu: "25m" 27 | memory: "32Mi" 28 | ``` 29 | 30 | 31 | Requirements 32 | ------------ 33 | 34 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 35 | 36 | Dependencies 37 | ------------ 38 | 39 | collections: 40 | 41 | - kubernetes.core 42 | - operator_sdk.util 43 | 44 | License 45 | ------- 46 | 47 | GPLv2+ 48 | 49 | Author Information 50 | ------------------ 51 | 52 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 53 | -------------------------------------------------------------------------------- /dev/cr-examples/galaxy-db-configuration.cr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: galaxy.ansible.com/v1beta1 3 | kind: Galaxy 4 | metadata: 5 | name: galaxy 6 | spec: 7 | ingress_type: Route 8 | 9 | image_pull_policy: Always 10 | no_log: false 11 | 12 | # signing configuration 13 | signing_secret: "signing-galaxy" 14 | signing_scripts_configmap: "signing-scripts" 15 | 16 | # content storage 17 | storage_type: File 18 | file_storage_access_mode: "ReadWriteMany" 19 | file_storage_size: "10Gi" 20 | file_storage_storage_class: nfs-local-rwx 21 | 22 | pulp_settings: 23 | api_root: "/api/galaxy/pulp/" 24 | allowed_export_paths: 25 | - /tmp 26 | allowed_import_paths: 27 | - /tmp 28 | telemetry: false 29 | 30 | postgres_extra_settings: 31 | - setting: max_connections 32 | value: "999" 33 | - setting: ssl_ciphers 34 | value: "HIGH:!aNULL:!MD5" 35 | 36 | database: 37 | # postgres_storage_class: standard 38 | 39 | # requires galaxy-postgres-configuration secret to be pre-created 40 | postgres_configuration_secret: galaxy-postgres-configuration 41 | postgres_resource_requirements: 42 | requests: 43 | cpu: 100m 44 | memory: 256Mi 45 | limits: 46 | cpu: 800m 47 | memory: 1Gi 48 | postgres_storage_requirements: 49 | requests: 50 | storage: 20Gi 51 | limits: 52 | storage: 100Gi 53 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.s6.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | spec: 6 | image: quay.io/ansible/galaxy-ng 7 | image_version: latest 8 | image_web: quay.io/ansible/galaxy-ui 9 | image_web_version: latest 10 | no_log: false 11 | admin_password_secret: "example-galaxy-admin-password" 12 | storage_type: File 13 | ingress_type: nodeport 14 | # k3s local-path requires this 15 | file_storage_access_mode: "ReadWriteMany" 16 | # We have a little over 10GB free on GHA VMs/instances 17 | file_storage_size: "10Gi" 18 | container_token_secret: "container-auth" 19 | pulp_settings: 20 | allowed_export_paths: 21 | - /tmp 22 | allowed_import_paths: 23 | - /tmp 24 | telemetry: false 25 | content: 26 | replicas: 1 27 | resource_requirements: 28 | requests: 29 | cpu: 150m 30 | memory: 256Mi 31 | limits: 32 | cpu: 800m 33 | memory: 1Gi 34 | worker: 35 | replicas: 1 36 | resource_requirements: 37 | requests: 38 | cpu: 150m 39 | memory: 256Mi 40 | limits: 41 | cpu: 800m 42 | memory: 1Gi 43 | web: 44 | replicas: 1 45 | resource_requirements: 46 | requests: 47 | cpu: 100m 48 | memory: 256Mi 49 | limits: 50 | cpu: 800m 51 | memory: 1Gi 52 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.galaxy.ocp.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: ocp-example 5 | spec: 6 | image: quay.io/ansible/galaxy-ng 7 | image_version: latest 8 | image_web: quay.io/ansible/galaxy-ui 9 | image_web_version: latest 10 | no_log: false 11 | admin_password_secret: "example-galaxy-admin-password" 12 | storage_type: File 13 | ingress_type: route 14 | route_host: route_host_placeholder 15 | route_tls_termination_mechanism: edge 16 | # k3s local-path requires this 17 | file_storage_access_mode: "ReadWriteOnce" 18 | # We have a little over 10GB free on GHA VMs/instances 19 | file_storage_size: "10Gi" 20 | pulp_settings: 21 | allowed_export_paths: 22 | - /tmp 23 | allowed_import_paths: 24 | - /tmp 25 | telemetry: false 26 | content: 27 | replicas: 1 28 | resource_requirements: 29 | requests: 30 | cpu: 150m 31 | memory: 256Mi 32 | limits: 33 | cpu: 800m 34 | memory: 1Gi 35 | worker: 36 | replicas: 1 37 | resource_requirements: 38 | requests: 39 | cpu: 150m 40 | memory: 256Mi 41 | limits: 42 | cpu: 800m 43 | memory: 1Gi 44 | web: 45 | replicas: 1 46 | resource_requirements: 47 | requests: 48 | cpu: 100m 49 | memory: 256Mi 50 | limits: 51 | cpu: 800m 52 | memory: 1Gi 53 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.galaxy.s6.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | spec: 6 | image: quay.io/ansible/galaxy-ng 7 | image_version: latest 8 | image_web: quay.io/ansible/galaxy-ui 9 | image_web_version: latest 10 | no_log: false 11 | admin_password_secret: "example-galaxy-admin-password" 12 | signing_secret: "signing-galaxy" 13 | signing_scripts_configmap: "signing-scripts" 14 | storage_type: File 15 | ingress_type: nodeport 16 | # k3s local-path requires this 17 | file_storage_access_mode: "ReadWriteMany" 18 | # We have a little over 10GB free on GHA VMs/instances 19 | file_storage_size: "10Gi" 20 | pulp_settings: 21 | allowed_export_paths: 22 | - /tmp 23 | allowed_import_paths: 24 | - /tmp 25 | telemetry: false 26 | content: 27 | replicas: 1 28 | resource_requirements: 29 | requests: 30 | cpu: 150m 31 | memory: 256Mi 32 | limits: 33 | cpu: 800m 34 | memory: 1Gi 35 | worker: 36 | replicas: 1 37 | resource_requirements: 38 | requests: 39 | cpu: 150m 40 | memory: 256Mi 41 | limits: 42 | cpu: 800m 43 | memory: 1Gi 44 | web: 45 | replicas: 1 46 | resource_requirements: 47 | requests: 48 | cpu: 100m 49 | memory: 256Mi 50 | limits: 51 | cpu: 800m 52 | memory: 1Gi 53 | -------------------------------------------------------------------------------- /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.23.0 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.23.0 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.23.0 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.23.0 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.23.0 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /roles/common/README.md: -------------------------------------------------------------------------------- 1 | Common 2 | ======== 3 | 4 | A role to setup shared tasks in Galaxy. 5 | 6 | In order to pull the images from a private registry (in a disconnected installation, for example) it is 7 | possible to configure the `image_pull_secrets` with the names of the secrets that have the credentials to 8 | pull the images from the private registry. 9 | The secrets that will be used by `image_pull_secrets` need to be created manually: 10 | ~~ 11 | kubectl create secret docker-registry --docker-server= --docker-username= --docker-password= --docker-email= 12 | ~~ 13 | 14 | If you have a `~/.docker/config.json` already, you can create the secret through: 15 | ~~ 16 | kubectl create secret docker-registry --from-file=.dockerconfigjson=path/to/.docker/config.json 17 | ~~ 18 | 19 | Role Variables 20 | -------------- 21 | 22 | * `image_pull_secrets`: An array of secrets that will be used to pull image from private registries 23 | 24 | Requirements 25 | ------------ 26 | 27 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 28 | 29 | Dependencies 30 | ------------ 31 | 32 | collections: 33 | 34 | - kubernetes.core 35 | - operator_sdk.util 36 | 37 | License 38 | ------- 39 | 40 | GPLv2+ 41 | 42 | Author Information 43 | ------------------ 44 | 45 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 46 | -------------------------------------------------------------------------------- /ansible-down.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Galaxy Operator ansible-down.sh 3 | 4 | # -- Usage 5 | # NAMESPACE=galaxy ./ansible-down.sh 6 | # 7 | # -- User Environment Variables 8 | # export NAMESPACE=testme # Namespace to deploy to 9 | # export QUAY_USER=chadams # Quay.io username to push operator image to 10 | 11 | 12 | # -- Variables 13 | TAG=${TAG:-dev} 14 | IMG=quay.io/$QUAY_USER/galaxy-operator:$TAG 15 | 16 | if [ -z "$QUAY_USER" ]; then 17 | echo "Error: QUAY_USER env variable is not set." 18 | echo " export QUAY_USER=developer" 19 | 20 | exit 1 21 | fi 22 | if [ -z "$NAMESPACE" ]; then 23 | echo "Error: NAMESPACE env variable is not set. Run the following with your namespace:" 24 | echo " export NAMESPACE=developer" 25 | exit 1 26 | fi 27 | 28 | 29 | # Delete Custom Resources 30 | oc delete galaxyrestore --all -n $NAMESPACE 31 | oc delete galaxybackup --all -n $NAMESPACE 32 | oc delete galaxy --all -n $NAMESPACE 33 | 34 | # Delete Secrets 35 | oc delete secrets --all -n $NAMESPACE 36 | 37 | # Delete old operator deployment 38 | oc delete deployment galaxy-operator-controller-manager -n $NAMESPACE 39 | 40 | # Deploy Operator 41 | make undeploy IMG=$IMG NAMESPACE=$NAMESPACE 42 | 43 | # Delete Azurite Storage 44 | oc delete -f hacking/azurite/azurite.yaml 45 | 46 | # Delete tls and ca certs 47 | rm -rf ./hacking/ca-cert/* 48 | rm -rf ./hacking/tls-azurite/* 49 | rm -rf ./hacking/tls-galaxy/* 50 | 51 | # Remove PVCs 52 | oc delete pvc --all -n $NAMESPACE 53 | 54 | -------------------------------------------------------------------------------- /molecule/kind/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | options: 5 | requirements-file: requirements.yml 6 | driver: 7 | name: delegated 8 | lint: | 9 | set -e 10 | yamllint -d "{extends: relaxed, rules: {line-length: {max: 120}}}" . 11 | platforms: 12 | - name: cluster 13 | groups: 14 | - k8s 15 | provisioner: 16 | name: ansible 17 | config_options: 18 | defaults: 19 | stdout_callback: debug 20 | callback_enabled: profile_tasks 21 | playbooks: 22 | prepare: ../default/prepare.yml 23 | verify: ../default/verify.yml 24 | lint: | 25 | set -e 26 | ansible-lint 27 | inventory: 28 | group_vars: 29 | all: 30 | namespace: ${TEST_OPERATOR_NAMESPACE:-osdk-test} 31 | operator_service_account_name: osdk-sa 32 | host_vars: 33 | localhost: 34 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 35 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 36 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 37 | project_dir: ${MOLECULE_PROJECT_DIRECTORY} 38 | operator_image: testing-operator 39 | operator_pull_policy: "Never" 40 | kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" 41 | kustomize: ${KUSTOMIZE_PATH:-kustomize} 42 | env: 43 | K8S_AUTH_KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 44 | KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 45 | verifier: 46 | name: ansible 47 | lint: | 48 | set -e 49 | ansible-lint 50 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.galaxy.externaldb.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | spec: 6 | image: quay.io/ansible/galaxy-ng 7 | image_version: latest 8 | image_web: quay.io/ansible/galaxy-ui 9 | image_web_version: latest 10 | no_log: false 11 | admin_password_secret: "example-galaxy-admin-password" 12 | postgres_configuration_secret: "example-galaxy-external-database" 13 | signing_secret: "signing-galaxy" 14 | signing_scripts_configmap: "signing-scripts" 15 | storage_type: File 16 | ingress_type: nodeport 17 | # k3s local-path requires this 18 | file_storage_access_mode: "ReadWriteMany" 19 | # We have a little over 10GB free on GHA VMs/instances 20 | file_storage_size: "10Gi" 21 | pulp_settings: 22 | allowed_export_paths: 23 | - /tmp 24 | allowed_import_paths: 25 | - /tmp 26 | telemetry: false 27 | content: 28 | replicas: 1 29 | resource_requirements: 30 | requests: 31 | cpu: 150m 32 | memory: 256Mi 33 | limits: 34 | cpu: 800m 35 | memory: 1Gi 36 | worker: 37 | replicas: 1 38 | resource_requirements: 39 | requests: 40 | cpu: 150m 41 | memory: 256Mi 42 | limits: 43 | cpu: 800m 44 | memory: 1Gi 45 | web: 46 | replicas: 1 47 | resource_requirements: 48 | requests: 49 | cpu: 100m 50 | memory: 256Mi 51 | limits: 52 | cpu: 800m 53 | memory: 1Gi 54 | -------------------------------------------------------------------------------- /roles/backup/tasks/secrets.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Dump (generated) secret names from statuses and data into file 4 | include_tasks: dump_generated_secret.yml 5 | with_items: 6 | - dbFieldsEncryptionSecret 7 | - adminPasswordSecret 8 | - databaseConfigurationSecret 9 | - containerTokenSecret 10 | 11 | - name: "Dump secret names from {{ deployment_type }} spec and data into file" 12 | include_tasks: dump_secret.yml 13 | loop: 14 | - route_tls_secret 15 | - ingress_tls_secret 16 | - bundle_cacert_secret 17 | - signing_secret 18 | - sso_secret 19 | - object_storage_azure_secret 20 | - object_storage_s3_secret 21 | 22 | # image_pull_secret is deprecated in favor of image_pull_secrets 23 | - name: Dump image_pull_secret into file 24 | include_tasks: dump_secret.yml 25 | with_items: 26 | - image_pull_secret 27 | when: image_pull_secret is defined 28 | 29 | - name: Dump image_pull_secrets into file 30 | include_tasks: dump_secret.yml 31 | with_items: 32 | - image_pull_secrets 33 | when: image_pull_secrets | default([]) | length 34 | 35 | - name: Nest secrets under a single variable 36 | set_fact: 37 | secrets: {"secrets": '{{ secret_dict }}'} 38 | no_log: "{{ no_log }}" 39 | 40 | - name: Write postgres configuration to pvc 41 | k8s_cp: 42 | namespace: "{{ backup_pvc_namespace }}" 43 | pod: "{{ ansible_operator_meta.name }}-db-management" 44 | remote_path: "{{ _backup_dir }}/secrets.yml" 45 | content: "{{ secrets | to_yaml }}" 46 | no_log: "{{ no_log }}" 47 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.galaxy.azure.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | spec: 6 | image: quay.io/ansible/galaxy-ng 7 | image_version: latest 8 | image_web: quay.io/ansible/galaxy-ui 9 | image_web_version: latest 10 | no_log: false 11 | admin_password_secret: "example-galaxy-admin-password" 12 | storage_type: Azure 13 | ingress_type: nodeport 14 | ######## Enable Execution Environments ######## 15 | container_token_secret: "container-auth" 16 | ############################################### 17 | object_storage_azure_secret: example-galaxy-object-storage 18 | # k3s local-path requires this 19 | file_storage_access_mode: "ReadWriteMany" 20 | # We have a little over 10GB free on GHA VMs/instances 21 | file_storage_size: "10Gi" 22 | pulp_settings: 23 | allowed_export_paths: 24 | - /tmp 25 | allowed_import_paths: 26 | - /tmp 27 | telemetry: false 28 | content: 29 | replicas: 1 30 | resource_requirements: 31 | requests: 32 | cpu: 150m 33 | memory: 256Mi 34 | limits: 35 | cpu: 800m 36 | memory: 1Gi 37 | worker: 38 | replicas: 1 39 | resource_requirements: 40 | requests: 41 | cpu: 150m 42 | memory: 256Mi 43 | limits: 44 | cpu: 800m 45 | memory: 1Gi 46 | web: 47 | replicas: 1 48 | resource_requirements: 49 | requests: 50 | cpu: 100m 51 | memory: 256Mi 52 | limits: 53 | cpu: 800m 54 | memory: 1Gi 55 | -------------------------------------------------------------------------------- /.ci/scripts/prepare-object-storage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | #!/usr/bin/env bash 3 | 4 | if [[ "$CI_TEST_STORAGE" == "azure" ]]; then 5 | docker run -d -p 10000:10000 --name galaxy-azurite mcr.microsoft.com/azure-storage/azurite azurite-blob --blobHost 0.0.0.0 6 | sleep 5 7 | AZURE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://galaxy-azurite:10000/devstoreaccount1;" 8 | echo $(minikube ip) galaxy-azurite | sudo tee -a /etc/hosts 9 | az storage container create --name galaxy-test --connection-string $AZURE_CONNECTION_STRING 10 | elif [[ "$CI_TEST_STORAGE" == "s3" ]]; then 11 | export MINIO_ACCESS_KEY=AKIAIT2Z5TDYPX3ARJBA 12 | export MINIO_SECRET_KEY=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 13 | docker run -d -p 0.0.0.0:9000:9000 --name galaxy_minio -e MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY -e MINIO_SECRET_KEY=$MINIO_SECRET_KEY minio/minio server /data 14 | wget https://dl.min.io/client/mc/release/linux-amd64/mc 15 | sudo mv mc /usr/local/bin/ 16 | sudo chmod +x /usr/local/bin/mc 17 | while ! nc -z $(minikube ip) 9000; do echo 'Wait minio to startup...' && sleep 0.1; done; 18 | echo $(minikube ip) galaxy_minio | sudo tee -a /etc/hosts 19 | sed -i "s/galaxy_minio/$(minikube ip)/g" config/samples/galaxy_v1beta1_galaxy_cr.galaxy.s3.ci.yaml 20 | mc alias set s3 http://$(minikube ip):9000 AKIAIT2Z5TDYPX3ARJBA fqRvjWaPU5o0fCqQuUWbj9Fainj2pVZtBCiDiieS --api S3v4 21 | mc alias remove local 22 | mc mb s3/galaxy --region us-east-1 23 | fi 24 | -------------------------------------------------------------------------------- /roles/postgres/README.md: -------------------------------------------------------------------------------- 1 | Postgres 2 | ======== 3 | 4 | A role to setup postgres in Galaxy, yielding the following objects: 5 | 6 | * StatefulSet 7 | * Service 8 | * Secret 9 | * Stores the DB password 10 | 11 | Role Variables 12 | -------------- 13 | 14 | * `database_connection`: A dictionary of database configuration 15 | * `username`: User that owns and runs Postgres. 16 | * `password`: Database password. 17 | * `admin_password`: Initial password for the Pulp admin. 18 | * `sslmode` is valid for `external` databases only. The allowed values are: `prefer`, `disable`, `allow`, `require`, `verify-ca`, `verify-full`. 19 | 20 | * `postgres_extra_settings`: A list of PostgreSQL configuration parameters to override or add 21 | * Each item is a dictionary with `setting` and `value` keys 22 | * `setting`: The PostgreSQL configuration parameter name 23 | * `value`: The value for the parameter (must be a string) 24 | * Example: 25 | ```yaml 26 | postgres_extra_settings: 27 | - setting: max_connections 28 | value: "499" 29 | - setting: ssl_ciphers 30 | value: "HIGH:!aNULL:!MD5" 31 | ``` 32 | 33 | Requirements 34 | ------------ 35 | 36 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 37 | 38 | Dependencies 39 | ------------ 40 | 41 | collections: 42 | 43 | - kubernetes.core 44 | - operator_sdk.util 45 | 46 | License 47 | ------- 48 | 49 | GPLv2+ 50 | 51 | Author Information 52 | ------------------ 53 | 54 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 55 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.galaxy.s3.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | spec: 6 | image: quay.io/ansible/galaxy-ng 7 | image_version: latest 8 | image_web: quay.io/ansible/galaxy-ui 9 | image_web_version: latest 10 | no_log: false 11 | admin_password_secret: "example-galaxy-admin-password" 12 | storage_type: S3 13 | ingress_type: nodeport 14 | ######## Enable Execution Environments ######## 15 | container_token_secret: "container-auth" 16 | ############################################### 17 | object_storage_s3_secret: example-galaxy-object-storage 18 | # k3s local-path requires this 19 | file_storage_access_mode: "ReadWriteMany" 20 | # We have a little over 10GB free on GHA VMs/instances 21 | file_storage_size: "10Gi" 22 | pulp_settings: 23 | allowed_export_paths: 24 | - /tmp 25 | allowed_import_paths: 26 | - /tmp 27 | aws_s3_endpoint_url: http://galaxy_minio:9000 28 | telemetry: false 29 | content: 30 | replicas: 1 31 | resource_requirements: 32 | requests: 33 | cpu: 150m 34 | memory: 256Mi 35 | limits: 36 | cpu: 800m 37 | memory: 1Gi 38 | worker: 39 | replicas: 1 40 | resource_requirements: 41 | requests: 42 | cpu: 150m 43 | memory: 256Mi 44 | limits: 45 | cpu: 800m 46 | memory: 1Gi 47 | web: 48 | replicas: 1 49 | resource_requirements: 50 | requests: 51 | cpu: 100m 52 | memory: 256Mi 53 | limits: 54 | cpu: 800m 55 | memory: 1Gi 56 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.galaxy.ci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | spec: 6 | image: quay.io/ansible/galaxy-ng 7 | image_version: latest 8 | image_web: quay.io/ansible/galaxy-ui 9 | image_web_version: latest 10 | no_log: false 11 | admin_password_secret: "example-galaxy-admin-password" 12 | signing_secret: "signing-galaxy" 13 | signing_scripts_configmap: "signing-scripts" 14 | storage_type: File 15 | ingress_type: nodeport 16 | # k3s local-path requires this 17 | file_storage_access_mode: "ReadWriteMany" 18 | # We have a little over 10GB free on GHA VMs/instances 19 | file_storage_size: "10Gi" 20 | pulp_settings: 21 | allowed_export_paths: 22 | - /tmp 23 | allowed_import_paths: 24 | - /tmp 25 | telemetry: false 26 | content: 27 | replicas: 1 28 | resource_requirements: 29 | requests: 30 | cpu: 150m 31 | memory: 256Mi 32 | limits: 33 | cpu: 800m 34 | memory: 1Gi 35 | worker: 36 | replicas: 1 37 | resource_requirements: 38 | requests: 39 | cpu: 150m 40 | memory: 256Mi 41 | limits: 42 | cpu: 800m 43 | memory: 1Gi 44 | web: 45 | replicas: 1 46 | resource_requirements: 47 | requests: 48 | cpu: 100m 49 | memory: 256Mi 50 | limits: 51 | cpu: 800m 52 | memory: 1Gi 53 | 54 | # DB configuration 55 | postgres_extra_settings: 56 | - setting: max_connections 57 | value: "999" 58 | - setting: ssl_ciphers 59 | value: "HIGH:!aNULL:!MD5" 60 | -------------------------------------------------------------------------------- /roles/postgres/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | _postgres_image: quay.io/sclorg/postgresql-15-c9s:latest 3 | 4 | postgres_storage_requirements: 5 | requests: 6 | storage: 8Gi 7 | postgres_resource_requirements: {} 8 | 9 | postgres_initdb_args: '--auth-host=scram-sha-256' 10 | postgres_host_auth_method: 'scram-sha-256' 11 | 12 | # Add a nodeSelector for the Postgres pods. 13 | # It must match a node's labels for the pod to be scheduled on that node. 14 | # Specify as literal block. E.g.: 15 | # postgres_selector: | 16 | # disktype: ssd 17 | # kubernetes.io/arch: amd64 18 | # kubernetes.io/os: linux 19 | postgres_selector: '' 20 | 21 | # Specify whether or not to keep the old PVC after PostgreSQL upgrades 22 | postgres_keep_pvc_after_upgrade: false 23 | 24 | # Add node tolerations for the Postgres pods. 25 | # Specify as literal block. E.g.: 26 | # postgres_tolerations: | 27 | # - key: "dedicated" 28 | # operator: "Equal" 29 | # value: "Galaxy" 30 | # effect: "NoSchedule" 31 | postgres_tolerations: '' 32 | 33 | # Define postgres configuration arguments to use (Deprecated) 34 | postgres_extra_args: '' 35 | # 36 | # Define postgresql.conf configurations 37 | postgres_extra_settings: [] 38 | 39 | postgres_data_volume_init: false 40 | postgres_init_container_commands: | 41 | chown 26:0 /var/lib/pgsql/data 42 | chmod 700 /var/lib/pgsql/data 43 | 44 | custom_resource_key: '_galaxy_ansible_com_galaxy' 45 | database_status_present: false 46 | 47 | # Here we use _galaxy_ansible_com_galaxy to get un-modified cr 48 | # see: https://github.com/operator-framework/operator-sdk/issues/1770 49 | raw_spec: "{{ vars['_galaxy_ansible_com_galaxy']['spec'] }}" 50 | -------------------------------------------------------------------------------- /.ci/assets/kubernetes/signing_scripts.configmap.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: signing-scripts 6 | data: 7 | collection_sign.sh: |- 8 | #!/usr/bin/env bash 9 | 10 | FILE_PATH=$1 11 | SIGNATURE_PATH=$1.asc 12 | 13 | ADMIN_ID="$PULP_SIGNING_KEY_FINGERPRINT" 14 | PASSWORD="password" 15 | 16 | # Create a detached signature 17 | gpg --quiet --batch --pinentry-mode loopback --yes --passphrase \ 18 | $PASSWORD --homedir /var/lib/pulp/.gnupg --detach-sign --default-key $ADMIN_ID \ 19 | --armor --output $SIGNATURE_PATH $FILE_PATH 20 | 21 | # Check the exit status 22 | STATUS=$? 23 | if [ $STATUS -eq 0 ]; then 24 | echo {\"file\": \"$FILE_PATH\", \"signature\": \"$SIGNATURE_PATH\"} 25 | else 26 | exit $STATUS 27 | fi 28 | container_sign.sh: |- 29 | #!/usr/bin/env bash 30 | 31 | # galaxy_container SigningService will pass the next 4 variables to the script. 32 | MANIFEST_PATH=$1 33 | FINGERPRINT="$PULP_SIGNING_KEY_FINGERPRINT" 34 | IMAGE_REFERENCE="$REFERENCE" 35 | SIGNATURE_PATH="$SIG_PATH" 36 | 37 | # Create container signature using skopeo 38 | skopeo standalone-sign \ 39 | $MANIFEST_PATH \ 40 | $IMAGE_REFERENCE \ 41 | $FINGERPRINT \ 42 | --output $SIGNATURE_PATH 43 | 44 | # Optionally pass the passphrase to the key if password protected. 45 | # --passphrase-file /path/to/key_password.txt 46 | 47 | # Check the exit status 48 | STATUS=$? 49 | if [ $STATUS -eq 0 ]; then 50 | echo {\"signature_path\": \"$SIGNATURE_PATH\"} 51 | else 52 | exit $STATUS 53 | fi 54 | -------------------------------------------------------------------------------- /roles/restore/templates/management-pod.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: {{ ansible_operator_meta.name }}-db-management 6 | namespace: "{{ backup_pvc_namespace }}" 7 | annotations: 8 | labels: 9 | app.kubernetes.io/name: '{{ deployment_type }}-backup-manager' 10 | app.kubernetes.io/instance: '{{ deployment_type }}-backup-manager-{{ ansible_operator_meta.name }}' 11 | app.kubernetes.io/component: backup-manager 12 | app.kubernetes.io/part-of: '{{ deployment_type }}' 13 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 14 | spec: 15 | {% if is_k8s %} 16 | securityContext: 17 | runAsUser: 1000 18 | {% endif %} 19 | containers: 20 | - name: {{ ansible_operator_meta.name }}-db-management 21 | image: "{{ _postgres_image }}" 22 | command: 23 | - /bin/bash 24 | - -c 25 | - | 26 | sleep infinity 27 | {% if restore_resource_requirements is defined %} 28 | resources: 29 | {{ restore_resource_requirements | to_nice_yaml(indent=2) | indent(width=6, first=False) }} 30 | {%- endif %} 31 | volumeMounts: 32 | - name: {{ ansible_operator_meta.name }}-backup 33 | mountPath: /backups 34 | readOnly: false 35 | {% if storage_claim is defined %} 36 | - name: file-storage 37 | readOnly: false 38 | mountPath: "/var/lib/pulp" 39 | {% endif %} 40 | volumes: 41 | - name: {{ ansible_operator_meta.name }}-backup 42 | persistentVolumeClaim: 43 | claimName: {{ backup_pvc }} 44 | readOnly: false 45 | {% if storage_claim is defined %} 46 | - name: file-storage 47 | persistentVolumeClaim: 48 | claimName: {{ storage_claim }} 49 | {% endif %} 50 | restartPolicy: Never 51 | -------------------------------------------------------------------------------- /roles/galaxy-route/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Host to create the root with. 3 | # If not specific will default to -- 4 | # 5 | 6 | # common default values 7 | client_request_timeout: 30 8 | route_host: '' 9 | hostname: '{{ deployment_type }}.example.com' 10 | 11 | # https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size 12 | nginx_client_max_body_size: 10m 13 | 14 | nginx_proxy_read_timeout: '{{ (([(client_request_timeout | int), 10] | max) / 2) | int }}' 15 | nginx_proxy_connect_timeout: '{{ (([(client_request_timeout | int), 10] | max) / 2) | int }}' 16 | nginx_proxy_send_timeout: '{{ (([(client_request_timeout | int), 10] | max) / 2) | int }}' 17 | 18 | haproxy_timeout: '{{ (([(client_request_timeout | int), 10] | max) / 2) | int }}' 19 | 20 | # https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/ 21 | # https://docs.nginx.com/nginx-ingress-controller/configuration/ingress-resources/advanced-configuration-with-annotations/ 22 | # Add annotations to the ingress. Specify as literal block. E.g.: 23 | # ingress_annotations: | 24 | # kubernetes.io/ingress.class: nginx 25 | # nginx.ingress.kubernetes.io/proxy-connect-timeout: 60s 26 | ingress_annotations: | 27 | nginx.ingress.kubernetes.io/proxy-body-size: {{ nginx_client_max_body_size }} 28 | nginx.org/client-max-body-size: {{ nginx_client_max_body_size }} 29 | nginx.ingress.kubernetes.io/proxy-read-timeout: {{ nginx_proxy_read_timeout }} 30 | nginx.ingress.kubernetes.io/proxy-connect-timeout: {{ nginx_proxy_connect_timeout }} 31 | nginx.ingress.kubernetes.io/proxy-send-timeout: {{ nginx_proxy_send_timeout }} 32 | haproxy.router.openshift.io/timeout: {{ haproxy_timeout }} 33 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | annotations: # About kube-linter checks: https://docs.kubelinter.io/#/generated/checks 9 | ignore-check.kube-linter.io/no-liveness-probe: "Not linting kubebuilder" 10 | ignore-check.kube-linter.io/no-readiness-probe: "Not linting kubebuilder" 11 | ignore-check.kube-linter.io/run-as-non-root: "Not linting kubebuilder" 12 | spec: 13 | template: 14 | spec: 15 | containers: 16 | - name: kube-rbac-proxy 17 | securityContext: 18 | allowPrivilegeEscalation: false 19 | # TODO(user): uncomment for common cases that do not require escalating privileges 20 | # capabilities: 21 | # drop: 22 | # - "ALL" 23 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.12.0 24 | args: 25 | - "--secure-listen-address=0.0.0.0:8443" 26 | - "--upstream=http://127.0.0.1:8080/" 27 | - "--logtostderr=true" 28 | - "--v=0" 29 | ports: 30 | - containerPort: 8443 31 | protocol: TCP 32 | name: https 33 | resources: 34 | limits: 35 | cpu: 500m 36 | memory: 128Mi 37 | requests: 38 | cpu: 5m 39 | memory: 64Mi 40 | - name: galaxy-operator 41 | args: 42 | - "--health-probe-bind-address=:6789" 43 | - "--metrics-bind-address=127.0.0.1:8080" 44 | - "--leader-elect" 45 | - "--leader-election-id=galaxy-operator" 46 | -------------------------------------------------------------------------------- /roles/backup/templates/management-pod.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: {{ ansible_operator_meta.name }}-db-management 6 | namespace: "{{ backup_pvc_namespace }}" 7 | labels: 8 | app.kubernetes.io/name: '{{ deployment_type }}-backup-manager' 9 | app.kubernetes.io/instance: '{{ deployment_type }}-backup-manager-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: backup-manager 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | spec: 14 | {% if is_k8s %} 15 | securityContext: 16 | runAsUser: 1000 17 | {% endif %} 18 | containers: 19 | - name: {{ ansible_operator_meta.name }}-db-management 20 | image: "{{ _postgres_image }}" 21 | imagePullPolicy: Always 22 | command: 23 | - /bin/bash 24 | - -c 25 | - | 26 | mkdir -p {{ _backup_dir }}/ 27 | sleep infinity 28 | {% if backup_resource_requirements is defined %} 29 | resources: 30 | {{ backup_resource_requirements | to_nice_yaml(indent=2) | indent(width=6, first=False) }} 31 | {%- endif %} 32 | volumeMounts: 33 | - name: {{ ansible_operator_meta.name }}-backup 34 | mountPath: /backups 35 | readOnly: false 36 | {% if storage_claim is defined %} 37 | - name: file-storage 38 | readOnly: false 39 | mountPath: "/var/lib/pulp" 40 | {% endif %} 41 | volumes: 42 | - name: {{ ansible_operator_meta.name }}-backup 43 | persistentVolumeClaim: 44 | claimName: {{ backup_claim }} 45 | readOnly: false 46 | {% if storage_claim is defined %} 47 | - name: file-storage 48 | persistentVolumeClaim: 49 | claimName: {{ storage_claim }} 50 | {% endif %} 51 | restartPolicy: Never 52 | -------------------------------------------------------------------------------- /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: galaxy-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 | -------------------------------------------------------------------------------- /.ci/scripts/galaxy_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # coding=utf-8 3 | set -euo pipefail 4 | 5 | KUBE="k3s" 6 | SERVER=$(hostname) 7 | WEB_PORT="24817" 8 | if [[ "${1-}" == "--minikube" ]] || [[ "${1-}" == "-m" ]]; then 9 | KUBE="minikube" 10 | SERVER="localhost" 11 | if [[ "$CI_TEST" == "true" ]]; then 12 | SVC_NAME="example-galaxy-web-svc" 13 | WEB_PORT="24880" 14 | kubectl port-forward service/$SVC_NAME $WEB_PORT:$WEB_PORT & 15 | fi 16 | fi 17 | 18 | # From the galaxy-server/galaxy-api config-map 19 | echo "machine $SERVER 20 | login admin 21 | password password\ 22 | " > ~/.netrc 23 | 24 | export BASE_ADDR="http://$SERVER:$WEB_PORT" 25 | echo $BASE_ADDR 26 | 27 | python3 -m pip install pulp-cli==0.23.0 28 | 29 | if [ ! -f ~/.config/pulp/settings.toml ]; then 30 | echo "Configuring pulp-cli" 31 | mkdir -p ~/.config/pulp 32 | cat > ~/.config/pulp/cli.toml << EOF 33 | [cli] 34 | base_url = "$BASE_ADDR" 35 | verify_ssl = false 36 | format = "json" 37 | EOF 38 | fi 39 | 40 | cat ~/.config/pulp/cli.toml | tee ~/.config/pulp/settings.toml 41 | 42 | pulp status | jq 43 | 44 | pushd pulp_ansible/docs/_scripts 45 | timeout 5m bash -x quickstart.sh || { 46 | YLATEST=$(git ls-remote --heads https://github.com/pulp/pulp_ansible.git | grep -o "[[:digit:]]\.[[:digit:]]*" | sort -V | tail -1) 47 | git fetch --depth=1 origin heads/$YLATEST:$YLATEST 48 | git checkout $YLATEST 49 | timeout 5m bash -x quickstart.sh 50 | } 51 | popd 52 | 53 | pushd pulp_container/docs/_scripts 54 | timeout 5m bash -x docs_check.sh || { 55 | YLATEST=$(git ls-remote --heads https://github.com/pulp/pulp_container.git | grep -o "[[:digit:]]\.[[:digit:]]*" | sort -V | tail -1) 56 | git fetch --depth=1 origin heads/$YLATEST:$YLATEST 57 | git checkout $YLATEST 58 | timeout 5m bash -x docs_check.sh 59 | } 60 | popd 61 | -------------------------------------------------------------------------------- /roles/galaxy-api/tasks/container_auth_configuration.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - k8s_status: 3 | api_version: "{{ api_version }}" 4 | kind: "{{ kind }}" 5 | name: "{{ ansible_operator_meta.name }}" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | conditions: 8 | - type: "{{ deployment_type|capitalize }}-API-Ready" 9 | message: Creating token authentication Secret 10 | reason: CreatingTokenAuthSecret 11 | status: "False" 12 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 13 | 14 | - name: Generate token private authentication key 15 | community.crypto.openssl_privatekey: 16 | path: /tmp/private_key.pem 17 | type: ECC 18 | curve: secp256r1 19 | 20 | - name: Generate token public authentication key 21 | community.crypto.openssl_publickey: 22 | path: /tmp/public_key.pem 23 | privatekey_path: /tmp/private_key.pem 24 | 25 | - name: Create token authentication secret 26 | k8s: 27 | apply: true 28 | definition: "{{ lookup('template', 'galaxy-container-auth.secret.yaml.j2') }}" 29 | 30 | - name: Remove token authentication keys 31 | ansible.builtin.file: 32 | path: '/tmp/{{ item }}_key.pem' 33 | state: absent 34 | loop: 35 | - private 36 | - public 37 | 38 | - name: Remove ownerReferences from token authentication secret to avoid garbage collection 39 | k8s: 40 | definition: 41 | apiVersion: v1 42 | kind: Secret 43 | metadata: 44 | name: '{{ container_token_secret }}' 45 | namespace: '{{ ansible_operator_meta.namespace }}' 46 | ownerReferences: null 47 | no_log: "{{ no_log }}" 48 | 49 | - name: Get container token configuration 50 | k8s_info: 51 | kind: Secret 52 | namespace: '{{ ansible_operator_meta.namespace }}' 53 | name: '{{ container_token_secret }}' 54 | register: _container_token_configuration 55 | no_log: "{{ no_log }}" 56 | -------------------------------------------------------------------------------- /roles/restore/templates/restore-content-k8s-job.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: batch/v1 3 | kind: Job 4 | metadata: 5 | name: {{ ansible_operator_meta.name }}-restore-content 6 | namespace: "{{ backup_pvc_namespace }}" 7 | labels: 8 | app.kubernetes.io/name: '{{ deployment_type }}-restore-content' 9 | app.kubernetes.io/instance: '{{ deployment_type }}-restore-content-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: restore-content 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | spec: 14 | ttlSecondsAfterFinished: 60 15 | template: 16 | metadata: 17 | annotations: 18 | spec: 19 | {% if is_k8s %} 20 | securityContext: 21 | runAsUser: 1000 22 | {% endif %} 23 | containers: 24 | - name: restore-content 25 | image: {{ _postgres_image }} 26 | imagePullPolicy: Always 27 | command: 28 | - /bin/bash 29 | - -c 30 | - | 31 | stat {{ backup_dir }}/pulp 32 | cp -fr {{ backup_dir }}/pulp /var/lib/pulp 33 | {% if restore_resource_requirements is defined %} 34 | resources: 35 | {{ restore_resource_requirements | to_nice_yaml(indent=2) | indent(width=10, first=False) }} 36 | {%- endif %} 37 | volumeMounts: 38 | - name: {{ ansible_operator_meta.name }}-backup 39 | mountPath: /backups 40 | {% if storage_claim is defined %} 41 | - name: file-storage 42 | mountPath: "/var/lib/pulp" 43 | {% endif %} 44 | volumes: 45 | - name: {{ ansible_operator_meta.name }}-backup 46 | persistentVolumeClaim: 47 | claimName: {{ backup_pvc }} 48 | {% if storage_claim is defined %} 49 | - name: file-storage 50 | persistentVolumeClaim: 51 | claimName: {{ storage_claim }} 52 | {% endif %} 53 | restartPolicy: Never 54 | backoffLimit: 0 55 | -------------------------------------------------------------------------------- /roles/common/templates/service_account.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: "{{ ansible_operator_meta.name }}" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | labels: 8 | {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }} 9 | imagePullSecrets: 10 | {% if image_pull_secret|length > 0 and image_pull_secrets|length == 0 %} 11 | # image_pull_secret is deprecated in favor of image_pull_secrets 12 | - name: {{ image_pull_secret }} 13 | {% else %} 14 | {% for secret in image_pull_secrets %} 15 | - name: {{ secret }} 16 | {% endfor %} 17 | {% endif %} 18 | {% if __dockercfg_secret is defined and __dockercfg_secret | length > 0%} 19 | - name: {{ __dockercfg_secret }} 20 | {% endif %} 21 | 22 | --- 23 | apiVersion: rbac.authorization.k8s.io/v1 24 | kind: Role 25 | metadata: 26 | name: "{{ ansible_operator_meta.name }}" 27 | namespace: "{{ ansible_operator_meta.namespace }}" 28 | labels: 29 | {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }} 30 | rules: 31 | - apiGroups: [""] # "" indicates the core API group 32 | resources: ["pods"] 33 | verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] 34 | - apiGroups: [""] 35 | resources: ["pods/log"] 36 | verbs: ["get"] 37 | - apiGroups: [""] 38 | resources: ["secrets"] 39 | verbs: ["get", "create", "delete"] 40 | 41 | --- 42 | kind: RoleBinding 43 | apiVersion: rbac.authorization.k8s.io/v1 44 | metadata: 45 | name: "{{ ansible_operator_meta.name }}" 46 | namespace: "{{ ansible_operator_meta.namespace }}" 47 | labels: 48 | {{ lookup("template", "../common/templates/labels/common.yaml.j2") | indent(width=4) | trim }} 49 | subjects: 50 | - kind: ServiceAccount 51 | name: "{{ ansible_operator_meta.name }}" 52 | roleRef: 53 | apiGroup: rbac.authorization.k8s.io 54 | kind: Role 55 | name: "{{ ansible_operator_meta.name }}" 56 | -------------------------------------------------------------------------------- /roles/galaxy-content/tasks/create-content-pvc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: galaxy-file-storage 3 | block: 4 | - name: "Creating {{ deployment_type|capitalize }}-api PVC resource" 5 | k8s_status: 6 | api_version: "{{ api_version }}" 7 | kind: "{{ kind }}" 8 | name: "{{ ansible_operator_meta.name }}" 9 | namespace: "{{ ansible_operator_meta.namespace }}" 10 | conditions: 11 | - type: "{{ deployment_type|capitalize }}-API-Ready" 12 | message: "Creating {{ deployment_type|capitalize }}-api PVC resource" 13 | reason: CreatingPVC 14 | status: "False" 15 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 16 | 17 | - name: galaxy-file-storage persistent volume claim 18 | k8s: 19 | state: "{{ deployment_state }}" 20 | definition: "{{ lookup('template', 'templates/' + item + '.pvc.yaml.j2') | from_yaml }}" 21 | with_items: 22 | - galaxy-file-storage 23 | 24 | - name: "Removing ownerReferences from {{ ansible_operator_meta.name}}-file-storage PVC" 25 | k8s_status: 26 | api_version: "{{ api_version }}" 27 | kind: "{{ kind }}" 28 | name: "{{ ansible_operator_meta.name }}" 29 | namespace: "{{ ansible_operator_meta.namespace }}" 30 | conditions: 31 | - type: "{{ deployment_type|capitalize }}-API-Ready" 32 | message: "Removing ownerReferences from {{ ansible_operator_meta.name}}-file-storage PVC" 33 | reason: RemovingPVCOwnerReferences 34 | status: "False" 35 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 36 | 37 | - name: Remove ownerReferences from galaxy-file-storage pvc to avoid garbage collection 38 | k8s: 39 | definition: 40 | apiVersion: v1 41 | kind: PersistentVolumeClaim 42 | metadata: 43 | name: '{{ ansible_operator_meta.name }}-file-storage' 44 | namespace: '{{ ansible_operator_meta.namespace }}' 45 | ownerReferences: null 46 | 47 | when: is_file_storage 48 | -------------------------------------------------------------------------------- /roles/galaxy-route/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Load Route TLS certificate 3 | ansible.builtin.include_tasks: load_route_tls_secret.yml 4 | when: 5 | - ingress_type | lower == 'route' 6 | - route_tls_secret != '' 7 | 8 | - name: Galaxy Route 9 | kubernetes.core.k8s: 10 | state: "{{ deployment_state }}" 11 | definition: "{{ lookup('template', 'templates/' + item + '.ingress.yaml.j2') }}" 12 | with_items: 13 | - galaxy 14 | when: 15 | - ingress_type is defined 16 | - ('route' == ingress_type|lower) or ('ingress' == ingress_type|lower) 17 | 18 | - name: "Remove deprecated {{ deployment_type }} Routes when present for {{ ansible_operator_meta.name }} from {{ ansible_operator_meta.namespace }} namespace" 19 | kubernetes.core.k8s: 20 | state: absent 21 | api_version: route.openshift.io/v1 22 | kind: Route 23 | namespace: "{{ ansible_operator_meta.namespace }}" 24 | name: "{{ item }}" 25 | loop: 26 | - "{{ ansible_operator_meta.name }}-ansible-pulp-ansible-galaxy" 27 | - "{{ ansible_operator_meta.name }}-api-v3" 28 | - "{{ ansible_operator_meta.name }}-auth" 29 | - "{{ ansible_operator_meta.name }}-container-extensions-v2" 30 | - "{{ ansible_operator_meta.name }}-container-pulp-container" 31 | - "{{ ansible_operator_meta.name }}-container-token" 32 | - "{{ ansible_operator_meta.name }}-container-v2" 33 | - "{{ ansible_operator_meta.name }}-content" 34 | - "{{ ansible_operator_meta.name }}-galaxy-api" 35 | - "{{ ansible_operator_meta.name }}-galaxy-ui" 36 | - "{{ ansible_operator_meta.name }}-galaxy-ui2" 37 | when: ingress_type | lower == 'route' 38 | 39 | - k8s_status: 40 | api_version: "{{ api_version }}" 41 | kind: "{{ kind }}" 42 | name: "{{ ansible_operator_meta.name }}" 43 | namespace: "{{ ansible_operator_meta.namespace }}" 44 | conditions: 45 | - type: "{{ deployment_type|capitalize }}-Routes-Ready" 46 | message: All routes were created successfully 47 | reason: RouteComplete 48 | status: "True" 49 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 50 | -------------------------------------------------------------------------------- /roles/galaxy-web/templates/galaxy-web.service.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "{{ ansible_operator_meta.name }}-web-svc" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | labels: 8 | app.kubernetes.io/name: 'nginx' 9 | app.kubernetes.io/instance: 'nginx-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: webserver 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | {% if service_annotations %} 14 | annotations: 15 | {{ service_annotations | indent(width=4) }} 16 | {% endif %} 17 | spec: 18 | selector: 19 | app.kubernetes.io/name: 'nginx' 20 | app.kubernetes.io/instance: 'nginx-{{ ansible_operator_meta.name }}' 21 | app.kubernetes.io/component: webserver 22 | app.kubernetes.io/part-of: '{{ deployment_type }}' 23 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 24 | ports: 25 | {% if ingress_type | lower != 'loadbalancer' and loadbalancer_protocol | lower != 'https' %} 26 | - port: 24880 27 | protocol: TCP 28 | targetPort: 8080 29 | name: web-8080 30 | {% endif %} 31 | {% if ingress_type | lower == 'route' and route_tls_termination_mechanism | lower == 'passthrough' %} 32 | - port: 443 33 | protocol: TCP 34 | targetPort: 8443 35 | name: web-8443 36 | {% endif %} 37 | {% if ingress_type | lower == 'loadbalancer' and loadbalancer_protocol | lower == 'https' %} 38 | - port: {{ loadbalancer_port }} 39 | protocol: TCP 40 | targetPort: 8080 41 | name: web-8443 42 | {% elif ingress_type | lower == 'loadbalancer' and loadbalancer_protocol | lower != 'https' %} 43 | - port: {{ loadbalancer_port }} 44 | protocol: TCP 45 | targetPort: 8080 46 | name: web-8080 47 | {% endif %} 48 | {% if ingress_type | lower == "loadbalancer" %} 49 | type: LoadBalancer 50 | {% elif ingress_type|lower == "nodeport" %} 51 | {% if nodeport_port is defined %} 52 | nodePort: {{ nodeport_port }} 53 | {% endif %} 54 | type: NodePort 55 | {% else %} 56 | type: ClusterIP 57 | {% endif %} 58 | -------------------------------------------------------------------------------- /dev/cr-examples/galaxy.cr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: galaxy.ansible.com/v1beta1 3 | kind: Galaxy 4 | metadata: 5 | name: galaxy 6 | spec: 7 | 8 | # ingress_type: nodeport 9 | # nodeport_port: 32002 10 | ingress_type: Route 11 | 12 | image_pull_policy: Always 13 | no_log: false 14 | 15 | # Test CA Bundle 16 | # bundle_cacert_secret: my-custom-certs 17 | # route_tls_secret: galaxy-tls 18 | 19 | # signing configuration 20 | signing_secret: "signing-galaxy" 21 | signing_scripts_configmap: "signing-scripts" 22 | 23 | # admin_password_secret: admin-password-secret 24 | 25 | #object_storage_azure_secret: automation-hub-azurite 26 | #storage_type: Azure 27 | 28 | storage_type: File 29 | 30 | # k3s local-path requires this 31 | file_storage_access_mode: "ReadWriteMany" 32 | # # We have a little over 10GB free on GHA VMs/instances 33 | file_storage_size: "10Gi" 34 | file_storage_storage_class: nfs-local-rwx 35 | 36 | # set redis_data_persistence to true in case you need a redis pvc 37 | # redis_data_persistence: false 38 | 39 | pulp_settings: 40 | api_root: "/api/galaxy/pulp/" 41 | allowed_export_paths: 42 | - /tmp 43 | allowed_import_paths: 44 | - /tmp 45 | telemetry: false 46 | 47 | postgres_extra_settings: 48 | - setting: max_connections 49 | value: "999" 50 | - setting: ssl_ciphers 51 | value: "HIGH:!aNULL:!MD5" 52 | 53 | # database: 54 | # postgres_storage_class: standard 55 | # api: 56 | # replicas: 1 57 | # content: 58 | # replicas: 1 59 | # resource_requirements: 60 | # requests: 61 | # cpu: 150m 62 | # memory: 256Mi 63 | # limits: 64 | # cpu: 800m 65 | # memory: 1Gi 66 | # worker: 67 | # replicas: 1 68 | # resource_requirements: 69 | # requests: 70 | # cpu: 150m 71 | # memory: 256Mi 72 | # limits: 73 | # cpu: 800m 74 | # memory: 1Gi 75 | # web: 76 | # replicas: 1 77 | # resource_requirements: 78 | # requests: 79 | # cpu: 100m 80 | # memory: 256Mi 81 | # limits: 82 | # cpu: 800m 83 | # memory: 1Gi 84 | 85 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 90 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 30 9 | 10 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - security 16 | - planned 17 | 18 | # Set to true to ignore issues in a project (defaults to false) 19 | exemptProjects: false 20 | 21 | # Set to true to ignore issues in a milestone (defaults to false) 22 | exemptMilestones: false 23 | 24 | # Set to true to ignore issues with an assignee (defaults to false) 25 | exemptAssignees: false 26 | 27 | # Label to use when marking as stale 28 | staleLabel: stale 29 | 30 | # Limit the number of actions per hour, from 1-30. Default is 30 31 | limitPerRun: 30 32 | 33 | pulls: 34 | markComment: |- 35 | This pull request has been marked 'stale' due to lack of recent activity. If there is no further activity, the PR will be closed in another 30 days. Thank you for your contribution! 36 | 37 | unmarkComment: >- 38 | This pull request is no longer marked for closure. 39 | 40 | closeComment: >- 41 | This pull request has been closed due to inactivity. If you feel this is in error, please reopen the pull request or file a new PR with the relevant details. 42 | 43 | issues: 44 | markComment: |- 45 | This issue has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! 46 | 47 | unmarkComment: >- 48 | This issue is no longer marked for closure. 49 | 50 | closeComment: >- 51 | This issue has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. 52 | -------------------------------------------------------------------------------- /roles/galaxy-api/README.md: -------------------------------------------------------------------------------- 1 | Galaxy API 2 | ======== 3 | 4 | A role to setup the Galaxy API, yielding the following objects: 5 | 6 | * Deployment 7 | * Service 8 | * PersistentVolumeClaim 9 | * This is created only when choosing filesystem storage e.g. `storage_type=File` 10 | * Secret 11 | * For the admin password 12 | * For encrypting sensitive DB fields 13 | 14 | Role Variables 15 | -------------- 16 | 17 | * `api`: A dictionary of galaxy-api configuration 18 | * `replicas`: Number of pod replicas. 19 | * `log_level`: The desired log level. 20 | * `default_settings`: A nested dictionary that will be combined with custom values from the user's 21 | *setting.py*. The keys of this dictionary are variable names, and the values should be expressed using the 22 | [Dynaconf syntax](https://dynaconf.readthedocs.io/en/latest/guides/environment_variables.html#precedence-and-type-casting) 23 | Please see [pulpcore configuration docs](https://docs.pulpproject.org/en/master/nightly/installation/configuration.html#id2) 24 | for documentation on the possible variable names and their values. 25 | * `debug`: Wether to run galaxy in debug mode. 26 | * `image`: The image name. Default: quay.io/ansible/galaxy-ng 27 | * `image_version`: The image tag. Default: main 28 | * `gunicorn_timeout`: The timeout for the gunicorn process. Default: computed from `client_request_timeout` 29 | * `storage_type`: A string for specifying storage configuration type. 30 | * `file_storage_access_mode`: The access mode for the volume. 31 | * `file_storage_size`: The storage size. 32 | * `object_storage_s3_secret`:The kubernetes secret with s3 storage configuration information. 33 | * `object_storage_azure_secret`:The kubernetes secret with Azure blob storage configuration information. 34 | 35 | Requirements 36 | ------------ 37 | 38 | Requires the `kubernetes` Python library to interact with Kubernetes: `pip install kubernetes`. 39 | 40 | Dependencies 41 | ------------ 42 | 43 | collections: 44 | 45 | - kubernetes.core 46 | - operator_sdk.util 47 | 48 | License 49 | ------- 50 | 51 | GPLv2+ 52 | 53 | Author Information 54 | ------------------ 55 | 56 | [Galaxy-Operator Team](https://github.com/ansible/galaxy-operator) 57 | -------------------------------------------------------------------------------- /roles/backup/templates/backup-content-k8s-job.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: batch/v1 3 | kind: Job 4 | metadata: 5 | name: {{ ansible_operator_meta.name }}-backup-content 6 | namespace: "{{ backup_pvc_namespace }}" 7 | labels: 8 | app.kubernetes.io/name: '{{ deployment_type }}-backup-content' 9 | app.kubernetes.io/instance: '{{ deployment_type }}-backup-content-{{ ansible_operator_meta.name }}' 10 | app.kubernetes.io/component: backup-content 11 | app.kubernetes.io/part-of: '{{ deployment_type }}' 12 | app.kubernetes.io/managed-by: '{{ deployment_type }}-operator' 13 | spec: 14 | ttlSecondsAfterFinished: 60 15 | template: 16 | metadata: 17 | annotations: 18 | spec: 19 | {% if is_k8s %} 20 | securityContext: 21 | runAsUser: 1000 22 | {% endif %} 23 | containers: 24 | - name: backup-content 25 | image: {{ _galaxy_image }} 26 | imagePullPolicy: Always 27 | command: 28 | - /bin/bash 29 | - -c 30 | - | 31 | mkdir -p {{ _backup_dir }}/pulp 32 | if [ -d "/var/lib/pulp/media" ]; then cp -fr /var/lib/pulp/media {{ _backup_dir }}/pulp/; fi 33 | if [ -d "/var/lib/pulp/media/artifact" ]; then cp -fr /var/lib/pulp/media/artifact {{ _backup_dir }}/pulp/; fi 34 | if [ -d "/var/lib/pulp/scripts" ]; then cp -fr /var/lib/pulp/scripts {{ _backup_dir }}/pulp/; fi 35 | {% if backup_resource_requirements is defined %} 36 | resources: 37 | {{ backup_resource_requirements | to_nice_yaml(indent=2) | indent(width=10, first=False) }} 38 | {%- endif %} 39 | volumeMounts: 40 | - name: {{ ansible_operator_meta.name }}-backup 41 | mountPath: /backups 42 | {% if storage_claim is defined %} 43 | - name: file-storage 44 | mountPath: "/var/lib/pulp" 45 | {% endif %} 46 | volumes: 47 | - name: {{ ansible_operator_meta.name }}-backup 48 | persistentVolumeClaim: 49 | claimName: {{ backup_claim }} 50 | {% if storage_claim is defined %} 51 | - name: file-storage 52 | persistentVolumeClaim: 53 | claimName: {{ storage_claim }} 54 | {% endif %} 55 | restartPolicy: Never 56 | backoffLimit: 0 57 | -------------------------------------------------------------------------------- /roles/galaxy-api/tasks/resource_configuration.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Check for API Pod 4 | k8s_info: 5 | kind: Pod 6 | api_version: v1 7 | namespace: "{{ ansible_operator_meta.namespace }}" 8 | label_selectors: 9 | - "app.kubernetes.io/name={{ deployment_type }}-api" 10 | - "app.kubernetes.io/managed-by={{ deployment_type }}-operator" 11 | - "app.kubernetes.io/instance={{ deployment_type }}-api-{{ ansible_operator_meta.name }}" 12 | - "app.kubernetes.io/component=api" 13 | field_selectors: 14 | - status.phase=Running 15 | register: _api_pod 16 | until: 17 | - "_api_pod['resources'] | length" 18 | - "_api_pod['resources'][0]['status']['phase'] == 'Running'" 19 | - "_api_pod['resources'][0]['status']['containerStatuses'][0]['ready'] == true" 20 | retries: 60 21 | delay: 5 22 | 23 | - name: Set the resource pod name as a variable 24 | set_fact: 25 | _api_pod_name: "{{ _api_pod['resources'][0]['metadata']['name'] | default('') }}" 26 | 27 | - name: Check if the admin user is defined 28 | kubernetes.core.k8s_exec: 29 | namespace: "{{ ansible_operator_meta.namespace }}" 30 | pod: "{{ _api_pod_name }}" 31 | container: "api" 32 | command: >- 33 | bash -c "echo 'from django.contrib.auth import get_user_model; 34 | User = get_user_model(); 35 | nsu = User.objects.filter(is_superuser=True, username=\"admin\").count(); 36 | exit(0 if nsu > 0 else 1)' 37 | | pulpcore-manager shell" 38 | ignore_errors: true 39 | register: users_result 40 | changed_when: users_result.return_code > 0 41 | 42 | - name: Create admin user via Django if it doesn't exist. 43 | kubernetes.core.k8s_exec: 44 | namespace: "{{ ansible_operator_meta.namespace }}" 45 | pod: "{{ _api_pod_name }}" 46 | container: "api" 47 | command: bash -c "ANSIBLE_REVERSE_RESOURCE_SYNC=false pulpcore-manager reset-admin-password --password '{{ admin_password }}'" 48 | register: result 49 | changed_when: "'That username is already taken' not in result.stderr" 50 | failed_when: "'That username is already taken' not in result.stderr and 'Successfully set password' not in result.stdout" 51 | no_log: "{{ no_log }}" 52 | when: users_result.return_code > 0 53 | -------------------------------------------------------------------------------- /docs/container.md: -------------------------------------------------------------------------------- 1 | # Containers 2 | 3 | ## Galaxy-NG 4 | 5 | An all-in-one [galaxy](https://github.com/ansible/galaxy_ng) image that can assume each of the following types of service: 6 | 7 | - **pulpcore-api** - serves the Galaxy (v3) API. The number of instances of this service should be scaled as demand requires. _Administrators and users of all of the APIs put demand on this service_. 8 | 9 | - **pulpcore-content** - serves content to clients. pulpcore-api redirects clients to pulpcore-content to download content. When content is being mirrored from a remote source, this service can download that content and stream it to the client the first time the content is requested. The number of instances of this service should be scaled as demand requires. _Content consumers put demands on this service_. 10 | 11 | - **pulpcore-worker** - performs syncing, importing of content, and other asynchronous operations that require resource locking. The number of instances of this service should be scaled as demand requires. _Administrators and content importers put demands on this service_. 12 | 13 | [https://quay.io/repository/ansible/galaxy-ng?tab=tags](https://quay.io/repository/ansible/galaxy-ng?tab=tags) 14 | 15 | 16 | ## Galaxy-UI 17 | 18 | An Nginx image with galaxy specific configuration. 19 | 20 | ### Tags 21 | 22 | 23 | [https://quay.io/repository/ansible/galaxy-ui?tab=tags](https://quay.io/repository/ansible/galaxy-ui?tab=tags) 24 | 25 | 26 | ## Galaxy-Operator 27 | 28 | [Ansible Operator](https://www.ansible.com/blog/ansible-operator) image, with the following ansible roles: 29 | 30 | * [Galaxy API](roles/galaxy-api.md) 31 | * [Galaxy Content](roles/galaxy-content.md) 32 | * [Galaxy Route](roles/galaxy-route.md) 33 | * [Galaxy Worker](roles/galaxy-worker.md) 34 | * [Galaxy Web](roles/galaxy-web.md) 35 | * [Galaxy Status](roles/galaxy-status.md) 36 | * [Galaxy Backup](roles/galaxybackup.md) 37 | * [Galaxy Restore](roles/galaxyrestore.md) 38 | * [Postgres](roles/postgres.md) 39 | * [Redis](roles/redis.md) 40 | 41 | ### Tags 42 | 43 | * `latest`: Latest released version [galaxy-operator](https://github.com/ansible/galaxy-operator). 44 | * `main`: Built from main (development) branch. 45 | 46 | [https://quay.io/repository/ansible/galaxy-operator?tab=tags](https://quay.io/repository/ansible/galaxy-operator?tab=tags) -------------------------------------------------------------------------------- /docs/maintainers/release.md: -------------------------------------------------------------------------------- 1 | # Galaxy Operator Release Guide 2 | 3 | This document provides step-by-step instructions for releasing a new version of the Galaxy Operator. It includes tagging a new release, building and pushing images, and updating release artifacts. 4 | 5 | ## Release Workflow 6 | 7 | ### 1. Trigger the Release GitHub Action 8 | 9 | The release process is automated through a GitHub Action (GHA) workflow. You can trigger this workflow manually via the GitHub UI. 10 | 11 | - Navigate to the 'Actions' tab in the GitHub repository. 12 | - Select the 'Stage Release' workflow. 13 | - Click on 'Run workflow' dropdown. 14 | - Enter the new version number (e.g., `1.2.3`) in the 'Release Version' input box. 15 | - Click 'Run workflow'. 16 | 17 | ### 2. Monitor the Workflow 18 | 19 | - Monitor the workflow for completion. 20 | - The workflow will handle: 21 | - Tagging the release. 22 | - Building and pushing operator image for multiple platforms. 23 | - Generating the `operator.yaml` file. 24 | - Creating a draft release and attaching the `operator.yaml` as an artifact. 25 | 26 | ### 3. Publish the Release 27 | 28 | Once the draft release is created, you need to publish it: 29 | 30 | - Go to the 'Releases' section in the GitHub repository. 31 | - Open the draft release created by the GitHub Action. 32 | - Review and edit the release notes as necessary. Add notable changes, deprecation warnings, and useful upgrade information for users. 33 | - Once satisfied, publish the release. This will trigger the 'Promote Operator Release' GHA, which will publish the operator image to quay.io as well as build and push the bundle and catalog images. 34 | 35 | ### 4. Post-Release Checks 36 | 37 | - Ensure that the images are correctly tagged on Quay. 38 | - Verify that the `operator.yaml` file is attached to the release and is correct. 39 | 40 | ## Troubleshooting 41 | 42 | If you encounter issues during the release process: 43 | 44 | - Check the GitHub Action logs for any errors or warnings. 45 | - Verify that all prerequisites are met. 46 | - For more specific issues, refer to the workflow file `.github/workflows/stage.yml` for insights. 47 | 48 | ## Notes 49 | 50 | - Do not manually tag or create releases; always use the automated workflow. 51 | - Ensure that you're familiar with the semantic versioning guidelines when assigning a version number. -------------------------------------------------------------------------------- /roles/common/tasks/check_existing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check for presence of API Deployment 3 | kubernetes.core.k8s_info: 4 | api_version: apps/v1 5 | kind: Deployment 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | label_selectors: 8 | - 'app.kubernetes.io/instance={{ deployment_type }}-api-{{ ansible_operator_meta.name }}' 9 | - 'app.kubernetes.io/part-of={{ deployment_type }}' 10 | - 'app.kubernetes.io/managed-by={{ deployment_type }}-operator' 11 | register: _api_deployment 12 | 13 | - name: Set previous_version if deployment exists 14 | when: _api_deployment.resources | length > 0 15 | block: 16 | - name: Check if {{ kind }} CR exists by name 17 | kubernetes.core.k8s_info: 18 | kind: "{{ kind }}" 19 | api_version: '{{ api_version }}' 20 | name: "{{ ansible_operator_meta.name }}" 21 | namespace: '{{ ansible_operator_meta.namespace }}' 22 | register: existing_cr 23 | 24 | - name: Update previous existing deployment without version 25 | when: existing_cr.resources[0].status.deployedVersion is not defined 26 | block: 27 | - name: Get galaxy settings 28 | ansible.builtin.include_role: 29 | name: galaxy-config 30 | tasks_from: combine_galaxy_settings.yml 31 | 32 | - name: Set deployedVersion if not present 33 | ansible.builtin.include_role: 34 | name: galaxy-status 35 | tasks_from: version.yml 36 | 37 | - name: Check the updated {{ kind }} CR 38 | kubernetes.core.k8s_info: 39 | kind: "{{ kind }}" 40 | api_version: '{{ api_version }}' 41 | name: "{{ ansible_operator_meta.name }}" 42 | namespace: '{{ ansible_operator_meta.namespace }}' 43 | register: existing_cr 44 | 45 | - name: Set previous_version version based on {{ deployment_type }} CR version status 46 | ansible.builtin.set_fact: 47 | previous_version: "{{ existing_cr.resources[0].status.deployedVersion }}" 48 | 49 | - name: Set upgraded_from to previous_version ({{ previous_version }}) if older than gating_version ({{ gating_version }}) 50 | ansible.builtin.set_fact: 51 | upgraded_from: "{{ previous_version }}" 52 | when: 53 | - previous_version is defined 54 | - previous_version is version(gating_version, '<') 55 | ... 56 | -------------------------------------------------------------------------------- /roles/galaxy-route/templates/galaxy.ingress.yaml.j2: -------------------------------------------------------------------------------- 1 | {% if 'ingress' == ingress_type|lower %} 2 | --- 3 | apiVersion: networking.k8s.io/v1 4 | kind: Ingress 5 | metadata: 6 | name: '{{ ansible_operator_meta.name }}-ingress' 7 | namespace: '{{ ansible_operator_meta.namespace }}' 8 | {% if ingress_annotations %} 9 | annotations: 10 | {{ ingress_annotations | indent(width=4) }} 11 | {% endif %} 12 | spec: 13 | {% if ingress_class_name is defined %} 14 | ingressClassName: '{{ ingress_class_name }}' 15 | {% endif %} 16 | rules: 17 | - host: '{{ hostname }}' 18 | http: 19 | paths: 20 | - path: / 21 | pathType: Prefix 22 | backend: 23 | service: 24 | name: '{{ ansible_operator_meta.name }}-web-svc' 25 | port: 26 | number: 24880 27 | {% if ingress_tls_secret %} 28 | tls: 29 | - hosts: 30 | - {{ hostname }} 31 | secretName: {{ ingress_tls_secret }} 32 | {% endif %} 33 | {% endif %} 34 | 35 | {% if 'route' == ingress_type|lower %} 36 | --- 37 | apiVersion: route.openshift.io/v1 38 | kind: Route 39 | metadata: 40 | name: '{{ ansible_operator_meta.name }}' 41 | namespace: '{{ ansible_operator_meta.namespace }}' 42 | {% if ingress_annotations %} 43 | annotations: 44 | {{ ingress_annotations | indent(width=4) }} 45 | {% if plugin.rewrite is defined %} 46 | haproxy.router.openshift.io/rewrite-target: '{{ plugin.rewrite }}' 47 | {% endif %} 48 | {% endif %} 49 | spec: 50 | {% if route_host != '' %} 51 | host: {{ route_host }} 52 | {% endif %} 53 | path: '/' 54 | port: 55 | {% if ingress_type | lower == 'route' and route_tls_termination_mechanism | lower == 'passthrough' %} 56 | targetPort: 8443 57 | {% else %} 58 | targetPort: 8080 59 | {% endif %} 60 | tls: 61 | insecureEdgeTerminationPolicy: Redirect 62 | termination: {{ route_tls_termination_mechanism | lower }} 63 | {% if route_tls_secret != '' %} 64 | key: |- 65 | {{ route_tls_key | indent(width=6, first=True) }} 66 | certificate: |- 67 | {{ route_tls_crt | indent(width=6, first=True) }} 68 | {% if route_ca_crt is defined %} 69 | caCertificate: |- 70 | {{ route_ca_crt | indent(width=6, first=True) }} 71 | {% endif %} 72 | {% endif %} 73 | to: 74 | kind: Service 75 | name: '{{ ansible_operator_meta.name }}-web-svc' 76 | weight: 100 77 | wildcardPolicy: None 78 | {% endif %} 79 | -------------------------------------------------------------------------------- /roles/common/tasks/signing_service.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - k8s_status: 3 | api_version: "{{ api_version }}" 4 | kind: "{{ kind }}" 5 | name: "{{ ansible_operator_meta.name }}" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | conditions: 8 | - type: "{{ deployment_type|capitalize }}-API-Ready" 9 | message: Copying public GPG key for signing service 10 | reason: CopyingPublicGPG 11 | status: "False" 12 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 13 | 14 | - name: Check for signing keys 15 | k8s_info: 16 | kind: Secret 17 | namespace: '{{ ansible_operator_meta.namespace }}' 18 | name: '{{ signing_secret }}' 19 | register: _signing_keys 20 | no_log: "{{ no_log }}" 21 | 22 | - name: Get GPG key ID 23 | block: 24 | - k8s_status: 25 | api_version: "{{ api_version }}" 26 | kind: "{{ kind }}" 27 | name: "{{ ansible_operator_meta.name }}" 28 | namespace: "{{ ansible_operator_meta.namespace }}" 29 | conditions: 30 | - type: "{{ deployment_type|capitalize }}-API-Ready" 31 | message: Importing galaxy signing service GPG key file 32 | reason: ImportingGalaxyGPGKey 33 | status: "False" 34 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 35 | 36 | - k8s_status: 37 | api_version: "{{ api_version }}" 38 | kind: "{{ kind }}" 39 | name: "{{ ansible_operator_meta.name }}" 40 | namespace: "{{ ansible_operator_meta.namespace }}" 41 | conditions: 42 | - type: "{{ deployment_type|capitalize }}-API-Ready" 43 | message: Updating Ownertrust trust level 44 | reason: UpdatingOwnertrustTrustLevel 45 | status: "False" 46 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 47 | 48 | - name: Ensure gnupg directory exists 49 | ansible.builtin.file: 50 | path: '{{ lookup("env", "HOME") }}/.gnupg' 51 | state: directory 52 | mode: '0700' 53 | 54 | - name: Set environment variable for the GPG key ID 55 | set_fact: 56 | signing_key_fingerprint: "{{ signing_service_gpg_content | community.crypto.gpg_fingerprint }}" 57 | cacheable: yes 58 | vars: 59 | signing_service_gpg_content: "{{ _signing_keys['resources'][0]['data']['signing_service.gpg'] | b64decode }}" 60 | 61 | when: signing_key_fingerprint is undefined 62 | -------------------------------------------------------------------------------- /config/samples/galaxy_v1beta1_galaxy_cr.default.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: galaxy.ansible.com/v1beta1 2 | kind: Galaxy 3 | metadata: 4 | name: example-galaxy 5 | #spec: 6 | # The registry to grab the galaxy image from. 7 | # image: quay.io/ansible/galaxy-ng 8 | # image_version: main 9 | 10 | # Pulp settings. 11 | # pulp_settings: 12 | # debug: "True" 13 | # AUTHENTICATION_BACKEND_PRESET: ldap 14 | # AUTH_LDAP_SERVER_URI: "ldap://ldap:10389" 15 | # AUTH_LDAP_BIND_DN: "cn=admin,dc=planetexpress,dc=com" 16 | # AUTH_LDAP_BIND_PASSWORD: "GoodNewsEveryone" 17 | # AUTH_LDAP_USER_SEARCH_BASE_DN: "ou=people,dc=planetexpress,dc=com" 18 | # AUTH_LDAP_USER_SEARCH_SCOPE: "SUBTREE" 19 | # AUTH_LDAP_USER_SEARCH_FILTER: "(uid=%(user)s)" 20 | # AUTH_LDAP_GROUP_SEARCH_BASE_DN: "ou=people,dc=planetexpress,dc=com" 21 | # AUTH_LDAP_GROUP_SEARCH_SCOPE: "SUBTREE" 22 | # AUTH_LDAP_GROUP_SEARCH_FILTER : "(objectClass=Group)" 23 | # AUTH_LDAP_GROUP_TYPE_CLASS: "django_auth_ldap.config:GroupOfNamesType" 24 | # The galaxy adminstrator password secret. 25 | # admin_password_secret: 26 | # PostgreSQL container settings secret. 27 | # postgres_configuration_secret: pg_secret_name 28 | # Configuration for the persistentVolumeClaim for /var/lib/pulp 29 | # storage_type: File 30 | # Select storage type: File, S3, Azure 31 | # If your K8s cluster is only 1 node, and its StorageClass / 32 | # provisioner does not support ReadWriteMany, then you must change 33 | # this to "ReadWriteOnce". 34 | # 35 | # If your K8s cluster is multiple nodes, and does not support 36 | # ReadWriteMany, then galaxy-operator is currently incompatible. 37 | # 38 | # Reference on which support ReadWriteMany: 39 | # https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes 40 | # file_storage_access_mode: "ReadWriteMany" 41 | 42 | # How much space do you want to give Pulp for storing content under 43 | # /var/lib/pulp ? 44 | # https://docs.pulpproject.org/en/3.0/nightly/installation/configuration.html#media-root 45 | 46 | # For reference, epel7 x86_64 is currently (2019-07) 30G. So 100G 47 | # should be sufficient for a test deployment with only the RPM 48 | # content plugin. 49 | # file_storage_size: "100Gi" 50 | # object_storage_s3_secret: example-galaxy-object-storage 51 | # Configuration for S3 credentals 52 | # object_storage_azure_secret: example-galaxy-object-storage 53 | # Configuration for Azure credentals 54 | # Values below are set in roles rather than in playbook.yaml 55 | # api: 56 | # replicas: 1 57 | # log_level: INFO 58 | # content: 59 | # replicas: 2 60 | # log_level: INFO 61 | # worker: 62 | # replicas: 2 63 | # resource_manager: 64 | # replicas: 1 65 | -------------------------------------------------------------------------------- /roles/galaxy-status/tasks/version.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set the status protocol and port 3 | set_fact: 4 | status_protocol: "https" 5 | status_port: "8443" 6 | when: 7 | - ingress_type | lower == 'route' 8 | - route_tls_termination_mechanism | lower == 'passthrough' 9 | 10 | - name: Check, through galaxy-web, if the app is ready to serve requests 11 | uri: 12 | url: '{{ status_protocol }}://{{ ansible_operator_meta.name }}-web-svc.{{ ansible_operator_meta.namespace }}.svc.cluster.local:{{ status_port }}/{{ pulp_combined_settings.galaxy_api_path_prefix }}/pulp/api/v3/status/' 13 | register: result_not_route 14 | until: > 15 | result_not_route.json is defined and 16 | result_not_route.json.versions | length and 17 | result_not_route.json.online_content_apps | length and 18 | result_not_route.json.database_connection.connected and 19 | (result_not_route.json.redis_connection.connected or not pulp_combined_settings.cache_enabled) and 20 | result_not_route.json.online_workers | map(attribute='name') | select('match', '^[0-9]+@.*$') | list | count > 0 21 | delay: 5 22 | retries: 10 23 | when: 24 | - ingress_type | lower != 'route' 25 | 26 | - name: Check, through galaxy-route, if the app is ready to serve requests 27 | uri: 28 | url: 'https://{{ route_host }}/{{ pulp_combined_settings.galaxy_api_path_prefix }}/pulp/api/v3/status/' 29 | validate_certs: no 30 | register: result_route 31 | until: > 32 | result_route.json is defined and 33 | result_route.json.versions | length and 34 | result_route.json.online_content_apps | length and 35 | result_route.json.database_connection.connected and 36 | (result_route.json.redis_connection.connected or not pulp_combined_settings.cache_enabled) and 37 | result_route.json.online_workers | map(attribute='name') | select('match', '^[0-9]+@.*$') | list | count > 0 38 | delay: 5 39 | retries: 60 40 | when: 41 | - ingress_type | lower == 'route' 42 | 43 | # Workaround to issue https://github.com/ansible/ansible/issues/76176 44 | - set_fact: 45 | result: "{{ result_route.json is defined | ternary(result_route, result_not_route) }}" 46 | 47 | - name: Get installed plugins 48 | set_fact: 49 | installed_plugins: "{{ result.json.versions | items2dict(key_name='component', value_name='version') }}" 50 | 51 | - name: Show installed plugins 52 | debug: 53 | var: installed_plugins 54 | 55 | - name: Update version status 56 | operator_sdk.util.k8s_status: 57 | api_version: '{{ api_version }}' 58 | kind: "{{ kind }}" 59 | name: "{{ ansible_operator_meta.name }}" 60 | namespace: "{{ ansible_operator_meta.namespace }}" 61 | status: 62 | deployedVersion: "{{ installed_plugins.galaxy }}" 63 | ... 64 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: galaxy-operator-role 6 | rules: 7 | ## 8 | ## Base operator rules 9 | ## 10 | - apiGroups: 11 | - route.openshift.io 12 | resources: 13 | - routes 14 | - routes/custom-host 15 | verbs: 16 | - create 17 | - delete 18 | - get 19 | - list 20 | - patch 21 | - update 22 | - watch 23 | - apiGroups: 24 | - "" 25 | - rbac.authorization.k8s.io 26 | resources: 27 | - secrets 28 | - pods 29 | - pods/exec 30 | - pods/log 31 | - services 32 | - services/finalizers 33 | - endpoints 34 | - persistentvolumeclaims 35 | - events 36 | - configmaps 37 | - roles 38 | - rolebindings 39 | - serviceaccounts 40 | verbs: 41 | - create 42 | - delete 43 | - get 44 | - list 45 | - patch 46 | - update 47 | - watch 48 | - apiGroups: 49 | - apps 50 | - networking.k8s.io 51 | resources: 52 | - deployments 53 | - daemonsets 54 | - replicasets 55 | - statefulsets 56 | - ingresses 57 | verbs: 58 | - create 59 | - delete 60 | - get 61 | - list 62 | - patch 63 | - update 64 | - watch 65 | - apiGroups: 66 | - "" 67 | resources: 68 | - nodes 69 | verbs: 70 | - get 71 | - list 72 | - apiGroups: 73 | - apps 74 | resources: 75 | - deployments/scale 76 | verbs: 77 | - patch 78 | - apiGroups: 79 | - "" 80 | resources: 81 | - serviceaccounts 82 | verbs: 83 | - list 84 | - watch 85 | - apiGroups: 86 | - "" 87 | resourceNames: 88 | - galaxy-operator-sa 89 | resources: 90 | - serviceaccounts 91 | verbs: 92 | - patch 93 | - get 94 | - apiGroups: 95 | - batch 96 | resources: 97 | - jobs 98 | verbs: 99 | - get 100 | - list 101 | - create 102 | - patch 103 | - update 104 | - watch 105 | 106 | ## 107 | ## Rules for galaxy.ansible.com/v1beta1, Kind: Galaxy 108 | ## 109 | - apiGroups: 110 | - galaxy.ansible.com 111 | resources: 112 | - galaxies 113 | - galaxies/status 114 | - galaxies/finalizers 115 | - galaxybackups 116 | - galaxybackups/status 117 | - galaxybackups/finalizers 118 | - galaxyrestores 119 | - galaxyrestores/status 120 | - galaxyrestores/finalizers 121 | verbs: 122 | - create 123 | - delete 124 | - get 125 | - list 126 | - patch 127 | - update 128 | - watch 129 | #+kubebuilder:scaffold:rules 130 | -------------------------------------------------------------------------------- /roles/galaxy-worker/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - k8s_status: 3 | api_version: "{{ api_version }}" 4 | kind: "{{ kind }}" 5 | name: "{{ ansible_operator_meta.name }}" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | conditions: 8 | - type: "{{ deployment_type|capitalize }}-Worker-Ready" 9 | message: "Starting {{ deployment_type|capitalize }}-worker tasks" 10 | reason: StartingTasksExecution 11 | status: "False" 12 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 13 | 14 | - name: Set default galaxy-worker image 15 | set_fact: 16 | _default_image: "{{ _image }}:{{ _image_version }}" 17 | 18 | - name: Set user provided galaxy-worker image 19 | set_fact: 20 | _custom_image: "{{ image }}:{{ image_version }}" 21 | when: 22 | - image is defined and image != '' 23 | - image_version is defined and image_version != '' 24 | 25 | - name: Set galaxy-worker image URL 26 | set_fact: 27 | _image: "{{ _custom_image | default(lookup('env', 'RELATED_IMAGE_GALAXY')) | default(_default_image, true) }}" 28 | 29 | - name: Set node affinity 30 | set_fact: 31 | _node_affinity: "{{ raw_spec['affinity']['node_affinity'] | default({}) }}" 32 | when: affinity is defined and affinity.node_affinity is defined 33 | 34 | - k8s_status: 35 | api_version: "{{ api_version }}" 36 | kind: "{{ kind }}" 37 | name: "{{ ansible_operator_meta.name }}" 38 | namespace: "{{ ansible_operator_meta.namespace }}" 39 | conditions: 40 | - type: "{{ deployment_type|capitalize }}-Worker-Ready" 41 | message: "Creating {{ deployment_type|capitalize }}-worker Deployment resource" 42 | reason: CreatingDeployment 43 | status: "False" 44 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 45 | 46 | - name: Wait for {{ ansible_operator_meta.name }}-server secret to be created 47 | k8s: 48 | definition: 49 | apiVersion: v1 50 | kind: Secret 51 | metadata: 52 | name: "{{ ansible_operator_meta.name }}-server" 53 | namespace: "{{ ansible_operator_meta.namespace }}" 54 | wait: yes 55 | wait_timeout: 300 56 | 57 | - name: galaxy-worker deployment 58 | k8s: 59 | state: "{{ deployment_state }}" 60 | definition: "{{ lookup('template', 'templates/' + item + '.deployment.yaml.j2') | from_yaml }}" 61 | with_items: 62 | - galaxy-worker 63 | 64 | - k8s_status: 65 | api_version: "{{ api_version }}" 66 | kind: "{{ kind }}" 67 | name: "{{ ansible_operator_meta.name }}" 68 | namespace: "{{ ansible_operator_meta.namespace }}" 69 | conditions: 70 | - type: "{{ deployment_type|capitalize }}-Worker-Ready" 71 | message: "All {{ deployment_type|capitalize }}-worker tasks ran successfully" 72 | reason: WorkerTasksFinished 73 | status: "True" 74 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 75 | -------------------------------------------------------------------------------- /.github/workflows/scripts/show_logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | #!/usr/bin/env bash 3 | 4 | sudo -E docker images 5 | 6 | KUBE="minikube" 7 | if [[ "$1" == "--kind" ]] || [[ "$1" == "-k" ]]; then 8 | KUBE="kind" 9 | echo "Running $KUBE" 10 | kind export kubeconfig --name osdk-test 11 | sudo -E kubectl config set-context --current --namespace=osdk-test 12 | sudo -E kubectl port-forward service/example-galaxy-web-svc 24880:24880 & 13 | echo ::group::KIND_LOGS 14 | mkdir -p logs 15 | kind export logs --name osdk-test ./logs 16 | cd logs 17 | cat docker-info.txt 18 | cat kind-version.txt 19 | cd osdk-test-control-plane 20 | cat alternatives.log 21 | cat containerd.log 22 | cat inspect.json | jq 23 | cat journal.log 24 | cat kubelet.log 25 | cat kubernetes-version.txt 26 | echo ::endgroup:: 27 | fi 28 | 29 | sudo -E kubectl get pods -o wide 30 | sudo -E kubectl get pods -o go-template='{{range .items}} {{.metadata.name}} {{range .status.containerStatuses}} {{.lastState.terminated.exitCode}} {{end}}{{"\n"}} {{end}}' 31 | sudo -E kubectl get deployments 32 | 33 | if [[ "$KUBE" == "minikube" ]]; then 34 | echo ::group::METRICS 35 | sudo -E kubectl top pods || true 36 | sudo -E kubectl describe node minikube || true 37 | echo ::endgroup:: 38 | echo ::group::MINIKUBE_LOGS 39 | minikube logs -n 10000 40 | echo ::endgroup:: 41 | fi 42 | 43 | echo ::group::OPERATOR_LOGS 44 | sudo -E kubectl logs -l app.kubernetes.io/name=galaxy-operator -c galaxy-operator --tail=10000 45 | echo ::endgroup:: 46 | 47 | echo ::group::GALAXY_API_LOGS 48 | sudo -E kubectl logs -l app.kubernetes.io/name=galaxy-api --tail=10000 || true 49 | echo ::endgroup:: 50 | 51 | echo ::group::GALAXY_CONTENT_LOGS 52 | sudo -E kubectl logs -l app.kubernetes.io/name=galaxy-content --tail=10000 || true 53 | echo ::endgroup:: 54 | 55 | echo ::group::GALAXY_WORKER_LOGS 56 | sudo -E kubectl logs -l app.kubernetes.io/name=galaxy-worker --tail=10000 || true 57 | echo ::endgroup:: 58 | 59 | echo ::group::GALAXY_WEB_LOGS 60 | sudo -E kubectl logs -l app.kubernetes.io/name=nginx --tail=10000 || true 61 | echo ::endgroup:: 62 | 63 | echo ::group::POSTGRES 64 | sudo -E kubectl logs -l app.kubernetes.io/name=postgres --tail=10000 || true 65 | echo ::endgroup:: 66 | 67 | echo ::group::EVENTS 68 | sudo -E kubectl get events --sort-by='.metadata.creationTimestamp' || true 69 | echo ::endgroup:: 70 | 71 | echo ::group::OBJECTS 72 | sudo -E kubectl get galaxy,galaxybackup,galaxyrestore,pvc,configmap,serviceaccount,secret,networkpolicy,ingress,service,deployment,statefulset,hpa,job,cronjob -o yaml 73 | echo ::endgroup:: 74 | 75 | echo "Content endpoint" 76 | http HEAD http://localhost:24880/galaxy/content/ || true 77 | echo "Status endpoint" 78 | http --follow --timeout 30 --check-status --pretty format --print hb http://localhost:24880/api/galaxy/pulp/api/v3/status/ || true 79 | http --follow --timeout 30 --check-status --pretty format --print hb http://localhost:24880/api/galaxy/ || true 80 | -------------------------------------------------------------------------------- /roles/common/tasks/db_fields_encryption_configuration.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - k8s_status: 3 | api_version: "{{ api_version }}" 4 | kind: "{{ kind }}" 5 | name: "{{ ansible_operator_meta.name }}" 6 | namespace: "{{ ansible_operator_meta.namespace }}" 7 | conditions: 8 | - type: "{{ deployment_type|capitalize }}-API-Ready" 9 | message: Configuring DB fields encryption Secret name 10 | reason: ConfiguringDBFieldsEncryption 11 | status: "False" 12 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 13 | 14 | - name: Set default DB fields encryption secret name 15 | set_fact: 16 | db_fields_encryption_secret: '{{ ansible_operator_meta.name }}-db-fields-encryption' 17 | cacheable: yes 18 | when: db_fields_encryption_secret is undefined 19 | 20 | - name: Check for specified DB fields encryption configuration 21 | k8s_info: 22 | kind: Secret 23 | namespace: '{{ ansible_operator_meta.namespace }}' 24 | name: '{{ db_fields_encryption_secret }}' 25 | register: _db_fields_encryption_secret 26 | no_log: "{{ no_log }}" 27 | 28 | - block: 29 | - k8s_status: 30 | api_version: "{{ api_version }}" 31 | kind: "{{ kind }}" 32 | name: "{{ ansible_operator_meta.name }}" 33 | namespace: "{{ ansible_operator_meta.namespace }}" 34 | conditions: 35 | - type: "{{ deployment_type|capitalize }}-API-Ready" 36 | message: Creating DB fields encryption Secret 37 | reason: CreatingDBSecret 38 | status: "False" 39 | lastTransitionTime: "{{ lookup('pipe', 'date --iso-8601=seconds') }}" 40 | 41 | - name: Generate Galaxy Database Encryption Key 42 | shell: | 43 | from cryptography.fernet import Fernet 44 | with open("/tmp/fernet", "wb") as key_file: 45 | key_file.write(Fernet.generate_key()) 46 | args: 47 | executable: /usr/local/bin/python3 48 | no_log: "{{ no_log }}" 49 | 50 | - name: Store DB fields encryption key 51 | set_fact: 52 | db_fields_encryption_key: "{{ lookup('file', '/tmp/fernet') }}" 53 | no_log: "{{ no_log }}" 54 | 55 | - name: Create DB fields encryption secret 56 | k8s: 57 | apply: true 58 | definition: "{{ lookup('template', 'galaxy-db-fields-encryption.secret.yaml.j2') }}" 59 | no_log: "{{ no_log }}" 60 | 61 | - name: Remove ownerReferences from DB fields encryption secret to avoid garbage collection 62 | k8s: 63 | definition: 64 | apiVersion: v1 65 | kind: Secret 66 | metadata: 67 | name: '{{ ansible_operator_meta.name }}-db-fields-encryption' 68 | namespace: '{{ ansible_operator_meta.namespace }}' 69 | ownerReferences: null 70 | no_log: "{{ no_log }}" 71 | 72 | when: not _db_fields_encryption_secret['resources'] | default([]) | length 73 | 74 | - name: Retrieve db_fields_encryption_key Secret 75 | k8s_info: 76 | kind: Secret 77 | namespace: '{{ ansible_operator_meta.namespace }}' 78 | name: '{{ db_fields_encryption_secret }}' 79 | register: db_fields_encryption_secret_contents 80 | no_log: "{{ no_log }}" 81 | --------------------------------------------------------------------------------