├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── CONTRIBUTING.adoc ├── Dockerfile ├── LIFECYCLE.adoc ├── Makefile ├── PROJECT ├── README.adoc ├── config ├── console-samples │ ├── kustomization.yaml │ └── manifests │ │ ├── 1-jenkins-platform.yaml │ │ ├── 1-typical-pipeline-java.yaml │ │ ├── 2-tekton-platform.yaml │ │ ├── 2-typical-pipeline-nodejs.yaml │ │ └── 3-private-platform.yaml ├── crd │ ├── bases │ │ ├── redhatgov.io_ploigospipelines.yaml │ │ └── redhatgov.io_ploigosplatforms.yaml │ └── kustomization.yaml ├── default │ └── kustomization.yaml ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── manifests │ ├── bases │ │ └── ploigos-software-factory-operator.clusterserviceversion.yaml │ └── kustomization.yaml ├── rbac │ ├── cluster_role.yaml │ ├── cluster_role_binding.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── ploigospipeline_editor_role.yaml │ ├── ploigospipeline_viewer_role.yaml │ ├── ploigosplatform_editor_role.yaml │ ├── ploigosplatform_viewer_role.yaml │ └── service_account.yaml ├── samples │ ├── kustomization.yaml │ ├── redhatgov_v1alpha1_ploigospipeline_molecule.yaml │ └── redhatgov_v1alpha1_ploigosplatform_molecule.yaml ├── scorecard │ ├── bases │ │ └── config.yaml │ ├── kustomization.yaml │ └── patches │ │ ├── basic.config.yaml │ │ └── olm.config.yaml └── testing │ ├── cluster_scope │ └── kustomization.yaml │ ├── debug_logs_patch.yaml │ ├── manager_image.yaml │ └── pull_policy │ ├── Always.yaml │ ├── IfNotPresent.yaml │ └── Never.yaml ├── hack ├── ci-setup.sh ├── operate.conf ├── operate.sh └── requirements.txt ├── library ├── LICENSE ├── README.md └── k8s_json_patch.py ├── molecule ├── default │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── kustomize.yml │ ├── molecule.yml │ ├── prepare.yml │ ├── tasks │ │ └── ploigos_software_factory_test.yml │ └── verify.yml └── ocp-cluster │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ └── molecule.yml ├── playbooks ├── ploigos.yml └── roles ├── requirements.yml ├── roles ├── ploigos-pipeline │ ├── defaults │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── pipeline-run.yml.j2 │ │ └── ploigos-pipeline-values.yaml.j2 └── ploigos-platform │ ├── deployment │ ├── defaults │ │ └── main.yml │ ├── files │ │ ├── gitlab-system.yml │ │ ├── gitlab.yml │ │ └── gitlab_ci_role.yml │ ├── tasks │ │ ├── cleanup-ploigos-platform.yml │ │ ├── deploy_services.yml │ │ ├── generate_gpg_key.yml │ │ ├── gitlab_ci.yaml │ │ ├── install_operatorgroup.yml │ │ ├── main.yml │ │ └── service_access_init.yml │ └── templates │ │ ├── catalog-source.yml.j2 │ │ ├── container-builder.yml.j2 │ │ ├── custom-resources │ │ ├── argocd.yml.j2 │ │ ├── gitea.yml.j2 │ │ ├── gitlab.yml.j2 │ │ ├── nexus_artifacts.yml.j2 │ │ ├── nexus_containers.yml.j2 │ │ ├── quay.yml.j2 │ │ ├── rhsso.yml.j2 │ │ ├── selenium.yml.j2 │ │ └── sonarqube.yml.j2 │ │ ├── gitlab-resources │ │ ├── gitlab_ci.yml.j2 │ │ ├── gitlab_ci_binding.yml.j2 │ │ ├── gitlab_ci_configmap.yml.j2 │ │ ├── gitlab_ci_pvc.yml.j2 │ │ ├── gitlab_ci_serviceaccount.yml.j2 │ │ └── gitlab_route.yml.j2 │ │ ├── jenkins-rolebinding.yml.j2 │ │ ├── jenkins.yml.j2 │ │ ├── operator-group.yml.j2 │ │ ├── ploigos-platform-config-mvn.yml.j2 │ │ ├── ploigos-platform-config-npm.yml.j2 │ │ ├── ploigos-platform-config-secrets-mvn.yml.j2 │ │ ├── ploigos-platform-config-secrets-npm.yml.j2 │ │ ├── ploigos-service-account-secret.yml.j2 │ │ ├── subscriptions │ │ ├── argocd.yml.j2 │ │ ├── cert_manager.yml.j2 │ │ ├── gitea.yml.j2 │ │ ├── gitlab_ci.yml.j2 │ │ ├── nexus_artifacts.yml.j2 │ │ ├── nexus_containers.yml.j2 │ │ ├── quay.yml.j2 │ │ ├── rhsso.yml.j2 │ │ ├── selenium.yml.j2 │ │ ├── sonarqube.yml.j2 │ │ └── tekton.yml.j2 │ │ └── trustedcabundle.yml.j2 │ └── sso-integration │ ├── defaults │ └── main.yml │ ├── tasks │ ├── argocd.yml │ ├── gitea.yml │ ├── main.yml │ ├── quay.yml │ └── sonarqube.yml │ └── templates │ ├── argocd-rhsso-patch.yml.j2 │ ├── quay-config-rhsso-block.yml.j2 │ ├── realm.yml.j2 │ ├── rhsso-argocd-client.yml.j2 │ ├── rhsso-gitea-client.yml.j2 │ ├── rhsso-quay-client.yml.j2 │ └── rhsso-sonar4-client.yml.j2 ├── vars └── ploigos-platform │ └── common.yml └── watches.yaml /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build/Release Images 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - '**.adoc' 9 | 10 | jobs: 11 | release: 12 | if: > 13 | github.event_name != 'pull_request' && github.ref == 'refs/heads/main' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | with: 19 | submodules: recursive 20 | - name: Login to Quay 21 | uses: docker/login-action@v1 22 | with: 23 | registry: quay.io 24 | username: ${{ secrets.QUAY_USERNAME }} 25 | password: ${{ secrets.QUAY_PASSWORD }} 26 | - name: Build, bundle, and push 27 | run: | 28 | export PATH="$HOME/.local/bin:$PATH" 29 | pip3 install --user --upgrade setuptools wheel pip 30 | hack/operate.sh --push-images --bundle --extra-tag=latest --verbose --formatter 31 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run Operator SDK tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - '**.adoc' 9 | 10 | jobs: 11 | test-cluster: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | with: 17 | submodules: recursive 18 | - name: Prereq setup 19 | run: hack/ci-setup.sh 20 | - name: Test 21 | run: | 22 | python -m molecule test -s ocp-cluster 23 | env: 24 | OCP_USERNAME: ${{ secrets.OCP_USERNAME }} 25 | OCP_PASSWORD: ${{ secrets.OCP_PASSWORD }} 26 | OCP_SERVER: ${{ secrets.OCP_SERVER }} 27 | QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }} 28 | QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }} 29 | release: 30 | needs: [test-cluster] 31 | if: > 32 | github.event_name != 'pull_request' && github.ref == 'refs/heads/main' 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v2 37 | with: 38 | submodules: recursive 39 | - name: Login to Quay 40 | uses: docker/login-action@v1 41 | with: 42 | registry: quay.io 43 | username: ${{ secrets.QUAY_USERNAME }} 44 | password: ${{ secrets.QUAY_PASSWORD }} 45 | - name: Build, bundle, and push 46 | run: | 47 | export PATH="$HOME/.local/bin:$PATH" 48 | pip3 install --user --upgrade setuptools wheel pip 49 | hack/operate.sh --push-images --bundle --extra-tag=latest --verbose --formatter 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | *.log 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.adoc: -------------------------------------------------------------------------------- 1 | 2 | == Resources and References 3 | 4 | * https://learn.openshift.com/operatorframework/ 5 | 6 | == Development Environment 7 | 8 | . `$ git clone https://github.com/ploigos/ploigos-software-factory-operator.git` 9 | . install docker, podman will not work with the molecule tests right now 10 | . install make 11 | . install python3 12 | . `pip3 install docker==4.2.2 molecule ansible-lint yamllint flake8 openshift jmespath` 13 | . `ansible-galaxy collection install -r requirements.yml` 14 | 15 | ==== Fedora 32 16 | 17 | . `sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=0"`` 18 | . `sudo dnf remove podman-docker` <- if this is installed on your machine 19 | . `sudo dnf install moby-engine` 20 | . `sudo systemctl enable docker.service --now` 21 | . sorry now you need to reboot 22 | 23 | 24 | == Development Workflow 25 | 26 | . `$ oc login` <- as cluster admin 27 | . `$ hack/operate.sh` <- install the operator. It will be deployed into the ploigos-operator-system namespace by default. 28 | . Create a CR for whatever you're testing. You can check the config/samples directory. For example: `oc apply -f config/samples/redhatgov_v1alpha1_ploigosplatform_jenkins.yaml -n ploigos-operator-system`. This will provision a PloigosPlatform in the ploigos-operator-system namespace. 29 | . If you make some Ansible changes and need to test them, you need to rebuild the operator image. 30 | .. Carefully cleanup the ploigos-operator-system project (so as not to orphan resources and leave the project stuck in Terminating) by deleting the PloigosPlatform (e.g. `oc delete ploigosplatform --all`), and then removing the operator installation by running `hack/operate.sh -r` 31 | .. Override the default operator image in a shell variable: `OPERATOR_IMAGE=quay.io//ploigos-operator` 32 | .. Build and push the image (requires a container runtime on your local): `./hack/operate.sh --image=$OPERATOR_IMAGE --version=latest --push-only`. If this is the first time you're doing this, you'll need to login to quay.io and change your repo from Private to Public so OCP can pull it 33 | .. Deploy the operator from your new image: `hack/operate.sh --image=$OPERATOR_IMAGE --version=latest` 34 | .. Recreate the CR: `oc apply -f config/samples/redhatgov_v1alpha1_ploigosplatform_jenkins.yaml -n ploigos-operator-system` 35 | 36 | === Testing 37 | 38 | In the example below are all the env vars that you need to set in your terminal. Note that you need to replace all the 'replace's, one in each env var. 39 | 40 | ``` 41 | export OCP_SERVER=replace 42 | export OCP_USERNAME=replace 43 | export OCP_PASSWORD='replace' 44 | export QUAY_USERNAME=replace 45 | export QUAY_PASSWORD='replace' 46 | export OPERATOR_IMAGE=quay.io/replace/ploigos-operator 47 | ``` 48 | 49 | Make sure the repo provided is public for the env var OPERATOR_IMAGE 50 | 51 | Now you can execute the molecule tests repeatedly as much as you like with this command: 52 | 53 | `molecule test -s ocp-cluster` 54 | 55 | == PR Testing 56 | 57 | describe current TravisCI integration 58 | 59 | == Process for Publishing a Release of the Operator 60 | 61 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/operator-framework/ansible-operator:v1.21.0 2 | 3 | COPY requirements.yml ${HOME}/requirements.yml 4 | USER root 5 | RUN dnf -y install git httpd-tools && \ 6 | dnf -y clean all --enablerepo='*' 7 | RUN pip3 install --upgrade --no-cache-dir jmespath git+https://github.com/RedHatGov/devsecops-api-collection.git 8 | RUN curl https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/openshift-client-linux.tar.gz -o /root/oc.tar.gz && \ 9 | tar xvzf /root/oc.tar.gz -C /usr/local/bin oc && \ 10 | curl -Lo ${HOME}/helm.tgz https://get.helm.sh/helm-v3.3.0-linux-amd64.tar.gz && \ 11 | tar xvzf ${HOME}/helm.tgz -C /usr/local/bin --strip-components 1 linux-amd64/helm 12 | USER ansible 13 | RUN ansible-galaxy collection install -r ${HOME}/requirements.yml \ 14 | && chmod -R ug+rwx ${HOME}/.ansible 15 | 16 | COPY watches.yaml ${HOME}/watches.yaml 17 | COPY library/ /usr/share/ansible/openshift/ 18 | COPY roles/ ${HOME}/roles/ 19 | COPY playbooks/ ${HOME}/playbooks/ 20 | COPY vars/ ${HOME}/vars/ 21 | -------------------------------------------------------------------------------- /LIFECYCLE.adoc: -------------------------------------------------------------------------------- 1 | = Lifecycle of this Operator 2 | 3 | == Configuration 4 | CI uses the following link:https://github.com/ploigos/ploigos-software-factory-operator/settings/secrets/actions[Actions Secrets] in this repository: 5 | 6 | * `OCP_SERVER` 7 | * `OCP_USERNAME` 8 | * `OCP_PASSWORD` 9 | * `QUAY_USERNAME` 10 | * `QUAY_PASSWORD` 11 | 12 | == Testing 13 | Changes to this operator should be accompanied by a version increment in link:hack/operate.conf[]. To request a change, open a PR against the `main` branch. This will trigger a unit test (GH Actions) that performs the following: 14 | 15 | . Ansible Lint 16 | . Build the operator image from link:Dockerfile[this Dockerfile], and push it to quay.io with the tag `quay.io/redhatgov/ploigos-operator:test`. 17 | . Deploy CRDs, RBAC, and operator deployment from the link:config[config] directory into the cluster in the `ploigos-operator-system` namespace using `kustomize`. 18 | . Create a link:config/samples/redhatgov_v1alpha1_ploigosplatform_molecule.yaml[PloigosPlatform] with default configuration (Jenkins for Continuous Integration, Nexus for a Container Registry, and SSO disabled), and ensure that the object reconciles successfully. 19 | . Delete the `PloigosPlatform`, operator objects, and associated namespace. 20 | 21 | In future, it would be great to expand this to do things like: 22 | 23 | * Create a `PloigosPipeline`, and verify that the pipeline completes successfully 24 | * Test an SSO-enabled environment 25 | * Test alternative tool chains (using Quay or Tekton) in different combinations 26 | * Test link:README.adoc#external-services[external services] 27 | 28 | == Release 29 | Once PRs are merged into `main`, the link:.github/workflows/release.yml[release workflow] is initiated, which does the following: 30 | 31 | . Build the operator image with the name and tag indicated by `hack/operate.conf` (e.g. `quay.io/redhatgov/ploigos-operator:0.21.0`), and push it to quay 32 | . Construct the operator bundle image (`quay.io/redhatgov/ploigos-operator-bundle:0.21.0`), and push it to quay 33 | 34 | == Packaging 35 | Once the release workflow has completed, open a PR against the link:https://github.com/redhatgov/operator-catalog[RedHatGov Operator Catalog] so that the release bundle can be installed through OLM as indicated in the link:README.adoc#quick-start[Quick Start Guide]. 36 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Current Operator version 2 | VERSION ?= 0.0.1 3 | # Default bundle image tag 4 | BUNDLE_IMG ?= controller-bundle:$(VERSION) 5 | # Options for 'bundle-build' 6 | ifneq ($(origin CHANNELS), undefined) 7 | BUNDLE_CHANNELS := --channels=$(CHANNELS) 8 | endif 9 | ifneq ($(origin DEFAULT_CHANNEL), undefined) 10 | BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) 11 | endif 12 | BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) 13 | 14 | # Image URL to use all building/pushing image targets 15 | IMG ?= controller:latest 16 | 17 | OVERLAY ?= default 18 | 19 | all: docker-build 20 | 21 | # Run against the configured Kubernetes cluster in ~/.kube/config 22 | run: ansible-operator 23 | $(ANSIBLE_OPERATOR) run 24 | 25 | # Install CRDs into a cluster 26 | install: kustomize 27 | $(KUSTOMIZE) build config/crd | kubectl apply -f - 28 | 29 | # Uninstall CRDs from a cluster 30 | uninstall: kustomize 31 | $(KUSTOMIZE) build config/crd | kubectl delete -f - 32 | 33 | # Deploy controller in the configured Kubernetes cluster in ~/.kube/config 34 | deploy: kustomize 35 | cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} 36 | $(KUSTOMIZE) build --load-restrictor LoadRestrictionsNone config/${OVERLAY} | kubectl apply -f - 37 | 38 | # Undeploy controller in the configured Kubernetes cluster in ~/.kube/config 39 | undeploy: kustomize 40 | $(KUSTOMIZE) build --load-restrictor LoadRestrictionsNone config/${OVERLAY} | kubectl delete -f - 41 | 42 | # Build the docker image 43 | docker-build: 44 | docker build . -t ${IMG} 45 | 46 | # Push the docker image 47 | docker-push: 48 | docker push ${IMG} 49 | 50 | PATH := $(PATH):$(PWD)/bin 51 | SHELL := env PATH=$(PATH) /bin/sh 52 | OS = $(shell uname -s | tr '[:upper:]' '[:lower:]') 53 | ARCH = $(shell uname -m | sed 's/x86_64/amd64/') 54 | OSOPER = $(shell uname -s | tr '[:upper:]' '[:lower:]' | sed 's/darwin/apple-darwin/' | sed 's/linux/linux-gnu/') 55 | ARCHOPER = $(shell uname -m ) 56 | 57 | kustomize: 58 | ifeq (, $(shell which kustomize 2>/dev/null)) 59 | @{ \ 60 | set -e ;\ 61 | mkdir -p bin ;\ 62 | curl -sSLo - https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.5.4/kustomize_v3.5.4_$(OS)_$(ARCH).tar.gz | tar xzf - -C bin/ ;\ 63 | } 64 | KUSTOMIZE=$(realpath ./bin/kustomize) 65 | else 66 | KUSTOMIZE=$(shell which kustomize) 67 | endif 68 | 69 | ansible-operator: 70 | ifeq (, $(shell which ansible-operator 2>/dev/null)) 71 | @{ \ 72 | set -e ;\ 73 | mkdir -p bin ;\ 74 | curl -LO https://github.com/operator-framework/operator-sdk/releases/download/v1.0.0/ansible-operator-v1.0.0-$(ARCHOPER)-$(OSOPER) ;\ 75 | mv ansible-operator-v1.0.0-$(ARCHOPER)-$(OSOPER) ./bin/ansible-operator ;\ 76 | chmod +x ./bin/ansible-operator ;\ 77 | } 78 | ANSIBLE_OPERATOR=$(realpath ./bin/ansible-operator) 79 | else 80 | ANSIBLE_OPERATOR=$(shell which ansible-operator) 81 | endif 82 | 83 | # Generate bundle manifests and metadata, then validate generated files. 84 | .PHONY: bundle 85 | bundle: kustomize 86 | operator-sdk generate kustomize manifests -q 87 | cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) 88 | $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) 89 | operator-sdk bundle validate ./bundle 90 | 91 | # Build the bundle image. 92 | .PHONY: bundle-build 93 | bundle-build: 94 | docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . 95 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: io 2 | layout: ansible.sdk.operatorframework.io/v1 3 | projectName: ploigos-software-factory-operator 4 | resources: 5 | - 6 | # controller: true 7 | domain: io 8 | group: redhatgov 9 | kind: PloigosPlatform 10 | version: v1alpha1 11 | - 12 | # controller: true 13 | domain: io 14 | group: redhatgov 15 | kind: PloigosPipeline 16 | version: v1alpha1 17 | version: "3" 18 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Ploigos Software Factory Operator 2 | 3 | This operator powers OpenShift-based deployments of https://ploigos.github.io/ploigos-docs/[Ploigos] - a DevSecOps ecosystem modeled after the US DoD's https://dodcio.defense.gov/Portals/0/Documents/DoD%20Enterprise%20DevSecOps%20Reference%20Design%20v1.0_Public%20Release.pdf?ver=2019-09-26-115824-583[DoD Enterprise DevSecOps Reference Design(DEDSORD)]. Pipeline steps are implemented using the https://github.com/ploigos/ploigos-step-runner[ploigos-step-runner], a python-based abstraction layer equipped with _step implementers_ that make your pipeline agnostic to underlying tools and services. 4 | 5 | Two APIs are offered: 6 | 7 | * *PloigosPlatform*: an all-in-one resource for provisioning pre-wired infrastructure like a CI tool, static code analysis server, artifact repository, and other services that support a DevSecOps pipeline. 8 | * *PloigosPipeline*: a resource for creating an end-to-end pipeline for your application's source code. 9 | 10 | == Quick Start 11 | 12 | . Create a `CatalogSource` to import the RedHatGov operator catalog. 13 | 14 | oc apply -f - << EOF 15 | apiVersion: operators.coreos.com/v1alpha1 16 | kind: CatalogSource 17 | metadata: 18 | name: redhatgov-operators 19 | namespace: openshift-marketplace 20 | spec: 21 | sourceType: grpc 22 | image: quay.io/redhatgov/operator-catalog:latest 23 | displayName: Red Hat NAPS Community Operators 24 | publisher: RedHatGov 25 | EOF 26 | 27 | . Create a project for your pipeline tooling to live. 28 | 29 | export PLOIGOS_PROJECT=devsecops 30 | oc new-project $PLOIGOS_PROJECT 31 | 32 | . Ploigos is hungry - delete any `LimitRange` that might have been created from project templates: 33 | 34 | oc delete limitrange --all -n $PLOIGOS_PROJECT 35 | 36 | . Create a new `OperatorGroup` to support installation into the `$PLOIGOS_PROJECT` namespace: 37 | 38 | oc apply -f - << EOF 39 | apiVersion: operators.coreos.com/v1 40 | kind: OperatorGroup 41 | metadata: 42 | namespace: $PLOIGOS_PROJECT 43 | name: $PLOIGOS_PROJECT-og 44 | spec: 45 | targetNamespaces: 46 | - $PLOIGOS_PROJECT 47 | EOF 48 | 49 | . Install this operator into your namespace: 50 | 51 | oc apply -f - << EOF 52 | apiVersion: operators.coreos.com/v1alpha1 53 | kind: Subscription 54 | metadata: 55 | name: ploigos-software-factory-operator 56 | namespace: $PLOIGOS_PROJECT 57 | spec: 58 | channel: alpha 59 | installPlanApproval: Automatic 60 | name: ploigos-software-factory-operator 61 | source: redhatgov-operators 62 | sourceNamespace: openshift-marketplace 63 | EOF 64 | 65 | . Create a `PloigosPlatform` to spin up your infrastructure: 66 | 67 | oc apply -f - << EOF 68 | apiVersion: redhatgov.io/v1alpha1 69 | kind: PloigosPlatform 70 | metadata: 71 | name: ploigosplatform 72 | spec: 73 | ploigosPlatform: 74 | services: 75 | continuousIntegration: 76 | jenkins: 77 | enabled: true 78 | sourceControl: 79 | gitea: 80 | enabled: true 81 | artifactRepository: 82 | nexusArtifacts: 83 | enabled: true 84 | staticCodeAnalysis: 85 | sonarqube: 86 | enabled: true 87 | continuousDeployment: 88 | argocd: 89 | enabled: true 90 | uat: 91 | selenium: 92 | enabled: true 93 | containerRegistry: 94 | nexusContainers: 95 | enabled: true 96 | EOF 97 | 98 | . Then create a `PloigosPipeline` instance for our reference application. If you want to use your _own_ application here, take a look at the https://github.com/andykrohg/ploigos-onboarding-demo[ploigos-onboarding-demo] to see how to wire it up. 99 | 100 | oc apply -f - << EOF 101 | apiVersion: redhatgov.io/v1alpha1 102 | kind: PloigosPipeline 103 | metadata: 104 | name: ploigospipeline-reference-app 105 | spec: 106 | appName: ref-quarkus-mvn 107 | appRepo: 108 | destinationRepoName: reference-quarkus-mvn 109 | sourceUrl: >- 110 | https://github.com/ploigos-reference-apps/reference-quarkus-mvn.git 111 | autoStartPipeline: true 112 | helmRepo: 113 | destinationRepoName: reference-quarkus-mvn-cloud-resources_workflow-typical 114 | sourceUrl: >- 115 | https://github.com/ploigos-reference-apps/reference-cloud-resources_operator.git 116 | serviceName: fruit 117 | EOF 118 | 119 | . Watch the magic happen - pop into Jenkins and check out the pipeline: 120 | 121 | oc get route jenkins --template "https://{{.spec.host}}" 122 | 123 | == Optional PloigosPlatform Configuration 124 | 125 | === Services 126 | 127 | You can specify a select number of services in the `ploigosPlatform.services` property of your `PloigosPlatform` object, leveraging either *Fully Managed* or *External Services*. 128 | 129 | ==== Fully Managed Services 130 | Fully Managed Services are deployed and configured by the Ploigos Software Factory Operator. To use a fully managed implementation for a given workflow function, add it to your `PloigosPlatform` `CustomResource` like this: 131 | 132 | ploigosPlatform: 133 | services: 134 | continuousIntegration: 135 | jenkins: 136 | enabled: true 137 | 138 | ==== External Services 139 | To use a service which already exists, you must supply connection properties so the operator can configure it. This can be done by adding the required options in the `externalProperties` sub-object. For example: 140 | 141 | ploigosPlatform: 142 | services: 143 | continuousIntegration: 144 | jenkins: 145 | enabled: true 146 | externalProperties: 147 | url: http://jenkins.example.com 148 | token: 12345678 149 | 150 | Note that applicable `externalProperties` differ depending on the service you're configuring. External Services can also be configured without the use of this operator by using the https://galaxy.ansible.com/ploigos/service_configs[Ploigos Service Configs Collection] directly. 151 | 152 | ==== Service Specification 153 | See below for a list of supported implementations for each service along with applicable External Properties: 154 | 155 | |=========================== 156 | |Service|Required?|Supported Implementations|External Properties 157 | |Single Sign-On (SSO)|| *rhsso* (Red Hat Single Sign-On)|(Not Supported) 158 | .3+|Continuous Integration .3+|✅ 159 | |*jenkins* a| 160 | * *url* - the URL to access Jenkins 161 | * *token* - an Oauth token to access the Jenkins API 162 | |*gitlabCi* a| 163 | * *tokenSecret* - Name of the secret containing the token secret for registration, optional (a secret is created by default) 164 | * *buildImage* - build image name for GitLab Runner 165 | * *caSecret* - Name of TLS secret containing custom CA certs 166 | |*tekton*|(Not Supported) 167 | .2+|Source Control .2+|✅ 168 | |*gitea* a| 169 | * *url* - the URL to access Gitea 170 | * *username* - the username of an existing Gitea user 171 | * *password* - the password of an existing Gitea user 172 | |*gitlab* a| 173 | * *url* - the URL to access GitLab 174 | * *apiToken* - a token to access GitLab API 175 | * *installCertManager* - Whether to install cert-manager or use existing 176 | |Artifact Repository|✅ 177 | |*nexusArtifacts* a| 178 | * *url* - the URL to access Nexus 179 | * *username* - the username of an existing Nexus user 180 | * *password* - the password of an existing Nexus user 181 | |Static Code Analysis|✅ 182 | |*sonarqube* a| 183 | * *url* - the URL to access Sonarqube 184 | * *username* - the username of an existing Sonarqube user 185 | * *password* - the password of an existing Sonarqube user 186 | .2+|Container Registry .2+|✅ 187 | |*nexusContainers* a| 188 | * *serverUrl* - the URL to access the Nexus Server 189 | * *dockerUrl* - the URL to access containers in Nexus. (e.g. quay.apps.example.com) 190 | * *username* - the username of an existing Nexus user 191 | * *password* - the password of an existing Nexus user 192 | |*quay* a| 193 | * *url* - the URL to access Quay (e.g. quay.apps.example.com) 194 | * *username* - the username of an existing Quay user 195 | * *password* - the password of an existing Quay user 196 | |Continuous Deployment|✅ 197 | |*argocd* a| 198 | * *kubernetesApi* - the Kuberenetes API Server where ArgoCD is hosted 199 | * *kubernetesToken* - a token to access the Kubernetes API where ArgoCD is hosted 200 | * *kubernetesCrName* - the name of the ArgoCD Custom Resource 201 | * *kubernetesNamespace* - the namespace in which ArgoCD is deployed 202 | |User Acceptance Testing|✅ 203 | |*selenium* a| 204 | * *url* - the URL to access Selenium Grid 205 | |=========================== 206 | 207 | === TLS 208 | The default `PloigosPlatform` deployment assumes that your OpenShift Router is equipped with a certificate signed by a well-known certificate authority. If your certificates are signed using a private CA instead, you can provide the name of a `ConfigMap` which holds your trusted CA Bundle. The ConfigMap should have a single key named ca-bundle.crt. This key has a collection of CA certificates as its value. If the provided ConfigMap exists, it will be used as-is. Otherwise, it will be generated using a label of `config.openshift.io/inject-trusted-cabundle=true` and populated with the *Cluster Network Operator*. For example: 209 | 210 | apiVersion: redhatgov.io/v1alpha1 211 | kind: PloigosPlatform 212 | metadata: 213 | name: ploigosplatform 214 | spec: 215 | ploigosPlatform: 216 | tls: 217 | trustBundleConfigMap: trustedcabundle 218 | 219 | If you are using self-signed certs, but configuring your own private CA is for some reason not an option, you can instead disable TLS verification. This is not recommended because it is less secure. To disable TLS verification, update your `PloigosPlatform` CR like this: 220 | 221 | apiVersion: redhatgov.io/v1alpha1 222 | kind: PloigosPlatform 223 | metadata: 224 | name: ploigosplatform 225 | spec: 226 | ploigosPlatform: 227 | tls: 228 | verify: false 229 | 230 | === Helm Repository 231 | 232 | When using `tekton` as a `continuousIntegration` service, cluster and `Pipeline` assets are deployed using helm charts served from the helm repository specified by `ploigosPlatform.helmRepository`. This is particularly useful to override when operating in disconnected environments. 233 | 234 | apiVersion: redhatgov.io/v1alpha1 235 | kind: PloigosPlatform 236 | metadata: 237 | name: ploigosplatform 238 | spec: 239 | ploigosPlatform: 240 | helmRepository: https://my.private.repo/charts 241 | 242 | == Building the Operator 243 | 244 | There is a script `hack/operate.sh` which will download the prerequisites (operator-sdk etc.), build the operator artifacts from operator-sdk defaults, package and push the operator container image, deploy the artifacts to a Kubernetes cluster, and create a `kind: PloigosPlatform` CR to deploy an instance. You should use the help page to look at what the various options do, but for the most part if you want to deploy a Ploigos Platform to a cluster directly from this repo you could run `hack/operate.sh -d`. 245 | 246 | Before running the script make sure to update the location of the container image to a repository you have access to. If you decide to build your own container image for the operator, make sure to update `hack/operate.conf` with an updated container image location and add the `-p` flag to `operate.sh`. 247 | 248 | == Developer Installation Steps 249 | 250 | The installation of the Custom Resource Definition and Cluster Role requires *cluster-admin* privileges. After that regular users with `admin` privileges on their projects (which is automatically granted to the user who creates a project) can provision the Ploigos Software Factory Operator in their projects and deploy PloigosPlatforms using the ploigosplatform.redhatgov.io Custom Resource. If you've installed the operator from the https://github.com/RedHatGov/operator-catalog[RedHatGov Operator Catalog Index] on an OLM-enabled cluster, the Ploigos Software Factory Operator can be installed from the OperatorHub interface of the console. 251 | 252 | Perform the following tasks as *cluster-admin*: 253 | 254 | . Deploy the CustomResourceDefinition, ClusterRole, ClusterRoleBinding, ServiceAccount, and Operator Deployment: 255 | + 256 | [source,sh] 257 | ---- 258 | hack/operate.sh 259 | ---- 260 | 261 | . Once the Operator pod is running the Operator is ready to start creating Ploigos Platforms. 262 | . To deploy the above, and also one of the `config/samples/redhatgov_v1alpha1_ploigosplatform*.yaml` example CustomResources: 263 | + 264 | [source,sh] 265 | ---- 266 | hack/operate.sh --deploy-cr 267 | ---- 268 | 269 | . To install the operator with RBAC scoped to a specific namespace, deploying a Role and RoleBinding instead of a ClusterRole and ClusterRoleBinding: 270 | + 271 | [source,sh] 272 | ---- 273 | hack/operate.sh --overlay=namespaced --namespace=mynamespace 274 | ---- 275 | 276 | == Developer Uninstalling the Ploigos Software Factory Operator 277 | 278 | In case you wish to uninstall the Ploigos Software Factory Operator, simply delete the operator and its resources with: 279 | 280 | [source,sh] 281 | ---- 282 | hack/operate.sh -r 283 | ---- 284 | 285 | OLM uninstallation for OLM-based operators can be handled through the UI, or by deleting the `Subscription`. 286 | 287 | == Notes on Disconnected Installations 288 | 289 | The Operator SDK makes heavy use of Kustomize for development and installation, but intends bundles to be generated for use in an operator catalog. This enables the Operator Lifecycle Manager, deployed onto your cluster, to install and configure operators with a simple `kind: Subscription` object, instead of a large collection of manifests. 290 | 291 | If you are using a `registries.conf` change and/or ImageContentSourcePolicy mirror that covers quay.io/redhatgov images, you should not have to change anything. 292 | 293 | To change the image sources for all necessary images to deploy the operator without such a policy, you need to have the following images hosted in a container repository on your disconnected network: 294 | 295 | * quay.io/redhatgov/ploigos-operator:latest 296 | 297 | If you intend on using `hack/operate.sh` it expects you to be in a development environment. Operator installation from this script therefore expects access to the internet. This comes with one extra concern: If `kustomize` isn't in your path, it tries to download it from the internet and save it locally into a `.gitignore`d folder. If you intend on using `hack/operate.sh` to install the operator, you should also bring `kustomize` and place it in the `$PATH` of the user who will be running the script. Additionally, in order to install the operator with `hack/operate.sh` you'll need to make the following change: 298 | 299 | * `hack/operate.conf`: IMG should point to the ploigos-operator image in your environment 300 | 301 | == Contributing 302 | Please see the link:CONTRIBUTING.adoc[Contributing Documentation]. 303 | 304 | == Lifecycle 305 | Please see the link:LIFECYCLE.adoc[Lifecycle Documentation]. 306 | -------------------------------------------------------------------------------- /config/console-samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - manifests/1-jenkins-platform.yaml 6 | - manifests/2-tekton-platform.yaml 7 | - manifests/3-private-platform.yaml 8 | - manifests/1-typical-pipeline-java.yaml 9 | - manifests/2-typical-pipeline-nodejs.yaml 10 | -------------------------------------------------------------------------------- /config/console-samples/manifests/1-jenkins-platform.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: console.openshift.io/v1 2 | kind: ConsoleYAMLSample 3 | metadata: 4 | name: 1-jenkins-platform 5 | spec: 6 | description: > 7 | Default PloigosPlatform deployment, with Jenkins for Continuous Integration, 8 | Nexus for a Container Registry, and SSO disabled 9 | targetResource: 10 | apiVersion: redhatgov.io/v1alpha1 11 | kind: PloigosPlatform 12 | title: Default PloigosPlatform 13 | yaml: | 14 | apiVersion: redhatgov.io/v1alpha1 15 | kind: PloigosPlatform 16 | metadata: 17 | name: ploigosplatform-sample 18 | spec: 19 | ploigosPlatform: 20 | services: 21 | continuousIntegration: 22 | jenkins: 23 | enabled: true 24 | sourceControl: 25 | gitea: 26 | enabled: true 27 | artifactRepository: 28 | nexusArtifacts: 29 | enabled: true 30 | staticCodeAnalysis: 31 | sonarqube: 32 | enabled: true 33 | continuousDeployment: 34 | argocd: 35 | enabled: true 36 | uat: 37 | selenium: 38 | enabled: true 39 | containerRegistry: 40 | nexusContainers: 41 | enabled: true 42 | -------------------------------------------------------------------------------- /config/console-samples/manifests/1-typical-pipeline-java.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: console.openshift.io/v1 2 | kind: ConsoleYAMLSample 3 | metadata: 4 | name: 1-pipeline-java 5 | spec: 6 | description: > 7 | PloigosPipeline using a reference java application. This will migrate upstream app and helm 8 | repositories into your Gitea instance and start a new typical pipeline run against them. 9 | targetResource: 10 | apiVersion: redhatgov.io/v1alpha1 11 | kind: PloigosPipeline 12 | title: Typical Pipeline - Java App 13 | yaml: | 14 | apiVersion: redhatgov.io/v1alpha1 15 | kind: PloigosPipeline 16 | metadata: 17 | name: ploigospipeline 18 | spec: 19 | appName: ref-quarkus-mvn 20 | serviceName: fruit 21 | appRepo: 22 | sourceUrl: https://github.com/ploigos-reference-apps/reference-quarkus-mvn.git 23 | destinationRepoName: reference-quarkus-mvn 24 | helmRepo: 25 | sourceUrl: https://github.com/ploigos-reference-apps/reference-cloud-resources_operator.git 26 | destinationRepoName: reference-quarkus-mvn-cloud-resources_workflow-typical 27 | autoStartPipeline: true 28 | -------------------------------------------------------------------------------- /config/console-samples/manifests/2-tekton-platform.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: console.openshift.io/v1 2 | kind: ConsoleYAMLSample 3 | metadata: 4 | name: 2-tekton-platform 5 | spec: 6 | description: > 7 | PloigosPlatform deployment with Tekton for Continuous Integration, 8 | Nexus for a Container Registry, and SSO disabled 9 | targetResource: 10 | apiVersion: redhatgov.io/v1alpha1 11 | kind: PloigosPlatform 12 | title: PloigosPlatform - Tekton 13 | yaml: | 14 | apiVersion: redhatgov.io/v1alpha1 15 | kind: PloigosPlatform 16 | metadata: 17 | name: ploigosplatform-sample 18 | spec: 19 | ploigosPlatform: 20 | services: 21 | continuousIntegration: 22 | tekton: 23 | enabled: true 24 | sourceControl: 25 | gitea: 26 | enabled: true 27 | artifactRepository: 28 | nexusArtifacts: 29 | enabled: true 30 | staticCodeAnalysis: 31 | sonarqube: 32 | enabled: true 33 | continuousDeployment: 34 | argocd: 35 | enabled: true 36 | uat: 37 | selenium: 38 | enabled: true 39 | containerRegistry: 40 | nexusContainers: 41 | enabled: true 42 | -------------------------------------------------------------------------------- /config/console-samples/manifests/2-typical-pipeline-nodejs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: console.openshift.io/v1 2 | kind: ConsoleYAMLSample 3 | metadata: 4 | name: 2-pipeline-nodejs 5 | spec: 6 | description: > 7 | PloigosPipeline using a reference NodeJS application. This will migrate upstream app and helm 8 | repositories into your Gitea instance and start a new typical pipeline run against them. 9 | targetResource: 10 | apiVersion: redhatgov.io/v1alpha1 11 | kind: PloigosPipeline 12 | title: Typical Pipeline - NodeJS App 13 | yaml: | 14 | apiVersion: redhatgov.io/v1alpha1 15 | kind: PloigosPipeline 16 | metadata: 17 | name: ploigospipeline 18 | spec: 19 | appName: ref-nodejs-npm 20 | serviceName: hello 21 | appRepo: 22 | sourceUrl: https://github.com/ploigos-reference-apps/reference-nodejs-npm.git 23 | destinationRepoName: reference-nodejs-npm 24 | helmRepo: 25 | sourceUrl: https://github.com/ploigos-reference-apps/reference-cloud-resources_operator.git 26 | destinationRepoName: reference-nodejs-npm-cloud-resources_workflow-typical 27 | autoStartPipeline: true 28 | -------------------------------------------------------------------------------- /config/console-samples/manifests/3-private-platform.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: console.openshift.io/v1 2 | kind: ConsoleYAMLSample 3 | metadata: 4 | name: 3-private-platform 5 | spec: 6 | description: > 7 | The default PloigosPlatform deployment assumes that your OpenShift Router 8 | is equipped with a certificate signed by a well-known certificate authority. 9 | If your certificates are signed using a private CA instead, you can provide the 10 | name of a ConfigMap which holds your trusted CA Bundle. The ConfigMap should 11 | have a single key named ca-bundle.crt. This key has a collection of CA certificates 12 | as its value. If the provided ConfigMap exists, it will be used as-is. 13 | Otherwise, it will be generated using a label of config.openshift.io/inject-trusted-cabundle=true 14 | and populated with the Cluster Network Operator. 15 | targetResource: 16 | apiVersion: redhatgov.io/v1alpha1 17 | kind: PloigosPlatform 18 | title: PloigosPlatform with Privately Signed Certificates 19 | yaml: | 20 | apiVersion: redhatgov.io/v1alpha1 21 | kind: PloigosPlatform 22 | metadata: 23 | name: ploigosplatform-jenkins-private 24 | spec: 25 | ploigosPlatform: 26 | services: 27 | continuousIntegration: 28 | jenkins: 29 | enabled: true 30 | sourceControl: 31 | gitea: 32 | enabled: true 33 | artifactRepository: 34 | nexusArtifacts: 35 | enabled: true 36 | staticCodeAnalysis: 37 | sonarqube: 38 | enabled: true 39 | continuousDeployment: 40 | argocd: 41 | enabled: true 42 | uat: 43 | selenium: 44 | enabled: true 45 | containerRegistry: 46 | nexusContainers: 47 | enabled: true 48 | tls: 49 | verify: false 50 | trustBundleConfigMap: trustedcabundle 51 | -------------------------------------------------------------------------------- /config/crd/bases/redhatgov.io_ploigospipelines.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: ploigospipelines.redhatgov.io 6 | spec: 7 | conversion: 8 | strategy: None 9 | group: redhatgov.io 10 | names: 11 | kind: PloigosPipeline 12 | listKind: PloigosPipelineList 13 | plural: ploigospipelines 14 | singular: ploigospipeline 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: PloigosPipeline is the Schema for the ploigospipeline API 21 | properties: 22 | apiVersion: 23 | description: | 24 | APIVersion defines the versioned schema of this representation 25 | of an object. Servers should convert recognized schemas to the latest 26 | internal value, and may reject unrecognized values. More info: 27 | https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: | 31 | Kind is a string value representing the REST resource this 32 | object represents. Servers may infer this from the endpoint the client 33 | submits requests to. Cannot be updated. In CamelCase. More info: 34 | https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 35 | type: string 36 | metadata: 37 | type: object 38 | spec: 39 | description: Spec defines the desired state of PloigosPipeline 40 | properties: 41 | appName: 42 | description: Name of the application the artifact built and deployed by this workflow is part of. 43 | type: string 44 | default: ref-quarkus-mvn-jenkins-std 45 | serviceName: 46 | description: Name of the service the artifact built and deployed by this workflow implements as part of the application it is a part of. 47 | type: string 48 | default: fruit 49 | appRepo: 50 | description: Attributes of the application repository. 51 | type: object 52 | properties: 53 | sourceUrl: 54 | description: Upstream repository for migration into platform-managed source control. 55 | type: string 56 | default: https://github.com/ploigos-reference-apps/reference-quarkus-mvn.git 57 | destinationRepoName: 58 | description: Name given to the newly-created repository migration in platform-managed source control 59 | type: string 60 | default: reference-quarkus-mvn 61 | ciConfigPath: 62 | description: Path of the CI for GitLab CI, optional 63 | type: string 64 | default: cicd/ploigos-software-factory-operator/gitlab/.gitlab-ci.yml 65 | required: 66 | - sourceUrl 67 | - destinationRepoName 68 | helmRepo: 69 | description: Attributes of the application's helm repository. 70 | type: object 71 | properties: 72 | sourceUrl: 73 | description: Upstream repository for migration into platform-managed source control. 74 | type: string 75 | default: https://github.com/ploigos-reference-apps/reference-cloud-resources_operator.git 76 | destinationRepoName: 77 | description: Name given to the newly-created repository migration in platform-managed source control. 78 | type: string 79 | default: reference-quarkus-mvn-cloud-resources_workflow-typical 80 | ciConfigPath: 81 | description: Path of the CI for GitLab CI, optional 82 | type: string 83 | default: .gitlab-ci.yaml 84 | required: 85 | - sourceUrl 86 | - destinationRepoName 87 | autoStartPipeline: 88 | type: boolean 89 | default: true 90 | required: 91 | - appName 92 | - serviceName 93 | - appRepo 94 | - helmRepo 95 | type: object 96 | status: 97 | description: Status defines the observed state of PloigosPipeline 98 | properties: 99 | conditions: 100 | description: The conditions reported by the Ansible operator 101 | items: 102 | properties: 103 | ansibleResult: 104 | description: The result reported from the last playbook run 105 | properties: 106 | changed: 107 | description: The number of tasks that resulted in a change 108 | type: integer 109 | completion: 110 | description: The time of completion 111 | type: string 112 | failures: 113 | description: The number of tasks that failed 114 | type: integer 115 | ok: 116 | description: The number of tasks that completed without 117 | change 118 | type: integer 119 | skipped: 120 | description: The number of tasks that were skipped 121 | type: integer 122 | type: object 123 | lastTransitionTime: 124 | description: The last time the playbook was invoked following 125 | a watch 126 | format: date-time 127 | type: string 128 | message: 129 | description: The last message reported from the Ansible Operator 130 | SDK 131 | type: string 132 | reason: 133 | description: The reason the status was updated 134 | type: string 135 | status: 136 | description: The boolean return value of the playbook execution, 137 | as a string 138 | type: string 139 | type: 140 | description: The type of status change event 141 | type: string 142 | type: object 143 | type: array 144 | type: object 145 | type: object 146 | served: true 147 | storage: true 148 | subresources: 149 | status: {} 150 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | # This kustomization.yaml is not intended to be run by itself, 5 | # since it depends on service name and namespace that are out of this kustomize package. 6 | # It should be run by config/default 7 | resources: 8 | - bases/redhatgov.io_ploigosplatforms.yaml 9 | - bases/redhatgov.io_ploigospipelines.yaml 10 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: ploigos-operator-system 5 | namePrefix: ploigos-operator- 6 | 7 | bases: 8 | - ../crd 9 | - ../rbac 10 | - ../manager 11 | - ../console-samples 12 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - manager.yaml 6 | images: 7 | - name: controller 8 | newName: quay.io/redhatgov/ploigos-operator 9 | newTag: latest 10 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | labels: 23 | control-plane: controller-manager 24 | spec: 25 | containers: 26 | - name: manager 27 | args: 28 | - "--enable-leader-election" 29 | - "--leader-election-id=ploigos-operator" 30 | - "--inject-owner-ref=false" 31 | image: controller:latest 32 | env: 33 | - name: WATCH_NAMESPACE 34 | valueFrom: 35 | fieldRef: 36 | fieldPath: metadata.annotations['olm.targetNamespaces'] 37 | terminationGracePeriodSeconds: 10 38 | serviceAccountName: ploigos-operator-sa 39 | -------------------------------------------------------------------------------- /config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../default 3 | - ../samples 4 | - ../scorecard 5 | -------------------------------------------------------------------------------- /config/rbac/cluster_role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | - apiGroups: 8 | - '*' 9 | resources: 10 | - '*' 11 | verbs: 12 | - '*' 13 | -------------------------------------------------------------------------------- /config/rbac/cluster_role_binding.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: manager-rolebinding 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: manager-role 10 | subjects: 11 | - kind: ServiceAccount 12 | name: sa 13 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - cluster_role.yaml 6 | - cluster_role_binding.yaml 7 | - leader_election_role.yaml 8 | - leader_election_role_binding.yaml 9 | - ploigosplatform_editor_role.yaml 10 | - ploigosplatform_viewer_role.yaml 11 | - service_account.yaml 12 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 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 | - "" 21 | resources: 22 | - events 23 | verbs: 24 | - create 25 | - patch 26 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: sa 12 | -------------------------------------------------------------------------------- /config/rbac/ploigospipeline_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit ploigospipelines. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: ploigospipeline-editor-role 6 | rules: 7 | - apiGroups: 8 | - redhatgov.io 9 | resources: 10 | - ploigospipelines 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - redhatgov.io 21 | resources: 22 | - ploigospipelines/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/ploigospipeline_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view ploigospipelines. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: ploigospipelines-viewer-role 6 | rules: 7 | - apiGroups: 8 | - redhatgov.io 9 | resources: 10 | - ploigospipelines 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - redhatgov.io 17 | resources: 18 | - ploigospipelines/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/ploigosplatform_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit ploigosplatforms. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: ploigosplatform-editor-role 6 | rules: 7 | - apiGroups: 8 | - redhatgov.io 9 | resources: 10 | - ploigosplatforms 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - redhatgov.io 21 | resources: 22 | - ploigosplatforms/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/ploigosplatform_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view ploigosplatforms. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: ploigosplatforms-viewer-role 6 | rules: 7 | - apiGroups: 8 | - redhatgov.io 9 | resources: 10 | - ploigosplatforms 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - redhatgov.io 17 | resources: 18 | - ploigosplatforms/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: sa 6 | namespace: system 7 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - redhatgov_v1alpha1_ploigosplatform_molecule.yaml 6 | - redhatgov_v1alpha1_ploigospipeline_molecule.yaml 7 | -------------------------------------------------------------------------------- /config/samples/redhatgov_v1alpha1_ploigospipeline_molecule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: redhatgov.io/v1alpha1 2 | kind: PloigosPipeline 3 | metadata: 4 | name: ploigospipeline-typical 5 | spec: 6 | appName: ref-quarkus-mvn 7 | serviceName: fruit 8 | appRepo: 9 | sourceUrl: https://github.com/ploigos-reference-apps/reference-quarkus-mvn.git 10 | destinationRepoName: reference-quarkus-mvn 11 | helmRepo: 12 | sourceUrl: https://github.com/ploigos-reference-apps/reference-quarkus-mvn-cloud-resources_operator.git 13 | destinationRepoName: reference-quarkus-mvn-cloud-resources_workflow-typical 14 | autoStartPipeline: true 15 | -------------------------------------------------------------------------------- /config/samples/redhatgov_v1alpha1_ploigosplatform_molecule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: redhatgov.io/v1alpha1 2 | kind: PloigosPlatform 3 | metadata: 4 | name: ploigosplatform-typical 5 | spec: 6 | ploigosPlatform: 7 | services: 8 | continuousIntegration: 9 | jenkins: 10 | enabled: true 11 | sourceControl: 12 | gitea: 13 | enabled: true 14 | artifactRepository: 15 | nexusArtifacts: 16 | enabled: true 17 | staticCodeAnalysis: 18 | sonarqube: 19 | enabled: true 20 | continuousDeployment: 21 | argocd: 22 | enabled: true 23 | uat: 24 | selenium: 25 | enabled: true 26 | containerRegistry: 27 | nexusContainers: 28 | enabled: true 29 | -------------------------------------------------------------------------------- /config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: [] 8 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - bases/config.yaml 6 | patchesJson6902: 7 | - path: patches/basic.config.yaml 8 | target: 9 | group: scorecard.operatorframework.io 10 | version: v1alpha3 11 | kind: Configuration 12 | name: config 13 | - path: patches/olm.config.yaml 14 | target: 15 | group: scorecard.operatorframework.io 16 | version: v1alpha3 17 | kind: Configuration 18 | name: config 19 | # +kubebuilder:scaffold:patchesJson6902 20 | -------------------------------------------------------------------------------- /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:master 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:master 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:master 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:master 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:master 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:master 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /config/testing/cluster_scope/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../crd 5 | - ../../rbac 6 | - ../../manager 7 | 8 | namespace: ploigos-operator-system 9 | namePrefix: ploigos-operator- 10 | 11 | images: 12 | - name: testing 13 | newName: testing-operator 14 | 15 | patchesStrategicMerge: 16 | - ../manager_image.yaml 17 | - ../debug_logs_patch.yaml 18 | -------------------------------------------------------------------------------- /config/testing/debug_logs_patch.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | env: 13 | - name: ANSIBLE_DEBUG_LOGS 14 | value: "TRUE" 15 | -------------------------------------------------------------------------------- /config/testing/manager_image.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | image: testing 13 | -------------------------------------------------------------------------------- /config/testing/pull_policy/Always.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | imagePullPolicy: Always 13 | -------------------------------------------------------------------------------- /config/testing/pull_policy/IfNotPresent.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | imagePullPolicy: IfNotPresent 13 | -------------------------------------------------------------------------------- /config/testing/pull_policy/Never.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | - name: manager 12 | imagePullPolicy: Never 13 | -------------------------------------------------------------------------------- /hack/ci-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | # Installs dependencies needed to run the tests in CI environment 3 | 4 | 5 | mkdir -p $HOME/.local/bin 6 | export PATH="$HOME/.local/bin:$PATH" 7 | export KUBECONFIG=$HOME/.kube/config 8 | 9 | # Basic pip prereqs 10 | pip3 install --user --upgrade setuptools wheel pip 11 | 12 | # Dependencies for test environment 13 | # NOTE: explicitly downgrading openshift due to https://github.com/kubernetes-client/python/issues/1333 14 | pip3 install --user docker==4.2.2 ansible molecule ansible-lint yamllint flake8 openshift==0.11.2 jmespath 15 | 16 | # Ansible dependencies 17 | ansible-galaxy collection install -r requirements.yml 18 | 19 | # OC CLI 20 | curl -Lo $HOME/oc.tar.gz http://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/openshift-client-linux.tar.gz 21 | tar xvzf $HOME/oc.tar.gz -C $HOME/.local/bin oc 22 | 23 | # Helm CLI 24 | curl -Lo $HOME/helm.tgz https://get.helm.sh/helm-v3.5.2-linux-amd64.tar.gz 25 | tar xvzf $HOME/helm.tgz -C $HOME/.local/bin --strip-components 1 linux-amd64/helm 26 | 27 | # Kustomize CLI 28 | curl -Lo $HOME/kustomize.tar.gz https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v4.0.5/kustomize_v4.0.5_linux_amd64.tar.gz 29 | tar zxvf $HOME/kustomize.tar.gz -C $HOME/.local/bin kustomize 30 | -------------------------------------------------------------------------------- /hack/operate.conf: -------------------------------------------------------------------------------- 1 | IMG=quay.io/redhatgov/ploigos-operator 2 | KIND=PloigosPlatform 3 | CR_SAMPLE=redhatgov_v1alpha1_ploigosplatform_molecule.yaml 4 | VERSION=0.22.1 5 | CHANNELS=alpha 6 | -------------------------------------------------------------------------------- /hack/operate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_ROOT=$(dirname $(realpath $0)) 4 | if [ $(basename "$SCRIPT_ROOT") = 'hack' ]; then 5 | cd "$SCRIPT_ROOT/.." 6 | else 7 | cd "$SCRIPT_ROOT" 8 | fi 9 | 10 | if ! which docker &>/dev/null; then 11 | if which podman &>/dev/null; then 12 | function docker { podman "${@}" ; } 13 | else 14 | echo "We may not be able to do docker things..." >&2 15 | fi 16 | fi 17 | 18 | function now() { 19 | date '+%Y%m%dT%H%M%S' 20 | } 21 | # Error handler 22 | function on_error() { 23 | [ -n "$msg" ] && wrap "$msg" ||: 24 | echo 25 | now=$(now) 26 | mv $log error_$now.log 27 | chmod 644 error_$now.log 28 | sync 29 | wrap "Error on $0 line $1, logs available at error_$now.log" >&2 30 | [ $1 -eq 0 ] && : || exit $2 31 | } 32 | 33 | # Generic exit cleanup helper 34 | function on_exit() { 35 | unkustomize_namespace &>/dev/null 36 | rm -f $log 37 | } 38 | 39 | # Stage some logging 40 | log=$(mktemp) 41 | if echo "$*" | grep -qF -- '-v' || echo "$*" | grep -qF -- '--verbose'; then 42 | exec 7> >(tee -a "$log" |& sed 's/^/\n/' >&2) 43 | FORMATTER_PAD_RESULT=0 44 | else 45 | exec 7>$log 46 | fi 47 | echo "Logging initialized $(now)" >&7 48 | 49 | # Set some traps 50 | trap 'on_error $LINENO $?' ERR 51 | trap 'on_exit' EXIT 52 | 53 | # Get some output helpers to keep things clean-ish 54 | if which formatter &>/dev/null; then 55 | # I keep this on my system. If you want, you can install it yourself: 56 | # mkdir -p ~/.local/bin 57 | # curl -o ~/.local/bin/formatter https://raw.githubusercontent.com/solacelost/output-formatter/modern-only/formatter 58 | # chmod +x ~/.local/bin/formatter 59 | # echo "$PATH" | grep -qF "$(realpath ~/.local/bin)" || export PATH="$(realpath ~/.local/bin):$PATH" 60 | . $(which formatter) 61 | else 62 | if echo "$*" | grep -qF -- '--formatter'; then 63 | curl -o ~/.local/bin/formatter https://raw.githubusercontent.com/solacelost/output-formatter/modern-only/formatter 64 | chmod +x ~/.local/bin/formatter 65 | . ~/.local/bin/formatter 66 | else 67 | # These will work as a poor-man's approximation in just a few lines 68 | function error_run() { 69 | echo -n "$1" 70 | shift 71 | eval "$@" >&7 2>&1 && echo ' [ SUCCESS ]' || { ret=$? ; echo ' [ ERROR ]' ; return $ret ; } 72 | } 73 | function warn_run() { 74 | echo -n "$1" 75 | shift 76 | eval "$@" >&7 2>&1 && echo ' [ SUCCESS ]' || { ret=$? ; echo ' [ WARNING ]' ; return $ret ; } 77 | } 78 | function wrap() { 79 | if [ $# -gt 0 ]; then 80 | echo "${@}" | fold -s 81 | else 82 | fold -s 83 | fi 84 | } 85 | fi 86 | fi 87 | 88 | function print_usage() { 89 | wrap "usage: $(basename $0) [-h|--help] | " \ 90 | "[--formatter] " \ 91 | "[-v|--verbose] " \ 92 | "[(-i |--image=)IMG] " \ 93 | "[(-k |--kind=)KIND] " \ 94 | "[-r|--remove] " \ 95 | "[-b|--build-artifacts] " \ 96 | "[--build-only] " \ 97 | "[-p|--push-images] " \ 98 | "[--push-only] " \ 99 | "[(-o |--overlay=)DIR] " \ 100 | "[(-n |--namespace=)NS] " \ 101 | "[(-c | --custom-resource=)YAML] " \ 102 | "[-d|--deploy-cr] " \ 103 | "[-u|--undeploy-cr] " \ 104 | "[(-V |--version=)SEMVER] " \ 105 | "[(-C |--channels=)CHANNELS] " \ 106 | "[(-t |--extra-tag=)TAG] " \ 107 | "[--develop] " \ 108 | "[--bundle]" 109 | } 110 | 111 | function print_help() { 112 | print_usage 113 | cat << 'EOF' 114 | 115 | Build an ansible-based operator using only requirements.yml, watches.yml, and 116 | the requisite playbooks/ and roles/ files on the fly. Can complete any and all 117 | stages as part of building artifacts, pushing, installing, and deploying the 118 | application to a cluster directly. Additional bundling or kustomization is 119 | available as well. 120 | 121 | OPTIONS 122 | -h|--help Print this help page and exit. 123 | --formatter Download and use the pretty-printing 124 | formatter to execute task runs. 125 | -v|--verbose Output all command output directly to 126 | stderr, making it ugly but debuggable. 127 | -i |--image=IMG Set the image name for the operator to IMG 128 | -k |--kind=KIND Set the Kind of the CRD to KIND 129 | -r|--remove Remove any installed/built operator and 130 | artifacts of that build. Do not build, 131 | push, install, or deploy. 132 | -b|--build-artifacts Rebuild deployment artifacts, removing them 133 | and rebuilding as necessary. 134 | --build-only Build artifacts, but don't install them or 135 | do any follow-on actions. 136 | -p|--push-images Build and push new operator images to your 137 | tagged registry - you must already be 138 | logged in. 139 | --push-only Build and push new operator images to your 140 | tagged repository, but don't do any 141 | follow-on actions. 142 | -o |--overlay=DIR When installing, use the kustomize overlay 143 | present in DIR (as a subdirectory of 144 | config) instead of the one in default. 145 | -n |--namespace=NS Set the namespace to deploy the artifacts to 146 | -c |--custom-resource=YAML Specify the CR sample file to deploy from 147 | the config/samples directory. 148 | -d|--deploy-cr Deploy a CR for the operator to the cluster. 149 | -u|--undeploy-cr Undeploy the CR for the operator. 150 | -V |--version=SEMVER Set the operator version to SEMVER for the 151 | purpose of building an operator image and 152 | an OLM bundle. 153 | -C |--channels=CHANNELS Set the channels label for the bundle image. 154 | -t |--extra-tag=TAG As well as SEMVER, also publish a version 155 | tagged as TAG. 156 | --develop Don't push to the SEMVER tag, either from 157 | operate.conf or the --version option, and 158 | instead push only to the develop tag 159 | --bundle Build and publish an OLM bundle to the tag 160 | at ${IMG}-bundle:${VERSION} 161 | 162 | NOTE: If you run with `--push-images` and `--bundle`, no installation will be 163 | attempted as this combination is intended for CI. Other options will still 164 | be processed. 165 | 166 | EOF 167 | } 168 | 169 | function parse_arg() { 170 | # If the first arg = the second arg, output the third and fail, otherwise 171 | # split the second on the first `=` sign and succeed. 172 | # ex: 173 | # -i|--image=*) 174 | # IMG=$(parse_arg -i "$1" "$2") || shift 175 | if [ "$1" = "$2" ]; then 176 | echo "$3" 177 | return 1 178 | else 179 | echo "$2" | cut -d= -f2- 180 | return 0 181 | fi 182 | } 183 | 184 | # Un/set defaults 185 | REMOVE_OPERATOR= 186 | IMG= 187 | KIND= 188 | PUSH_IMAGES= 189 | PUSH_ONLY= 190 | BUILD_ARTIFACTS= 191 | BUILD_ONLY= 192 | NAMESPACE= 193 | CR_SAMPLE= 194 | DEPLOY_CR= 195 | UNDEPLOY_CR= 196 | OVERLAY=default 197 | VERSION= 198 | CHANNELS= 199 | DEVLEOP= 200 | BUNDLE= 201 | EXTRA_TAGS=() 202 | 203 | # Load the configuration 204 | config= 205 | if [ -f operate.conf ]; then 206 | config=operate.conf 207 | elif [ -f hack/operate.conf ]; then 208 | config=hack/operate.conf 209 | fi 210 | if [ "$config" ]; then 211 | # This uses some simple python to read the .conf file in true ini format, 212 | # outputting the variables in an exportable fashion so we can eval them 213 | # in the warn_run. 214 | warn_run "Loading configuration from operate.conf" source $config ||: 215 | fi 216 | 217 | while [ $# -gt 0 ]; do 218 | case "$1" in 219 | -h|--help) 220 | print_help 221 | exit 0 222 | ;; 223 | --formatter) 224 | true 225 | ;; 226 | -v|--verbose) 227 | true 228 | ;; 229 | -r|--remove) 230 | REMOVE_OPERATOR=true 231 | ;; 232 | -i|--image=*) 233 | IMG=$(parse_arg -i "$1" "$2") || shift 234 | ;; 235 | -k|--kind=*) 236 | KIND=$(parse_arg -k "$1" "$2") || shift 237 | ;; 238 | -b|--build-artifacts) 239 | BUILD_ARTIFACTS=true 240 | ;; 241 | --build-only) 242 | BUILD_ARTIFACTS=true 243 | BUILD_ONLY=true 244 | ;; 245 | -p|--push-images) 246 | PUSH_IMAGES=true 247 | ;; 248 | --push-only) 249 | PUSH_IMAGES=true 250 | PUSH_ONLY=true 251 | ;; 252 | -o|--overlay=*) 253 | OVERLAY=$(parse_arg -o "$1" "$2") || shift 254 | ;; 255 | -n|--namespace=*) 256 | NAMESPACE=$(parse_arg -n "$1" "$2") || shift 257 | ;; 258 | -c|--custom-resource=*) 259 | CR_SAMPLE=$(parse_arg -c "$1" "$2") || shift 260 | ;; 261 | -d|--deploy-cr) 262 | DEPLOY_CR=true 263 | UNDEPLOY_CR= 264 | ;; 265 | -u|--undeploy-cr) 266 | UNDEPLOY_CR=true 267 | DEPLOY_CR= 268 | ;; 269 | -V|--version=*) 270 | VERSION=$(parse_arg -V "$1" "$2") || shift 271 | ;; 272 | -C|--channels=*) 273 | CHANNELS=$(parse_arg -C "$1" "$2") || shift 274 | ;; 275 | -t|--extra-tag=*) 276 | EXTRA_TAGS+=($(parse_arg -t "$1" "$2")) || shift 277 | ;; 278 | --develop) 279 | DEVELOP=true 280 | ;; 281 | --bundle) 282 | BUNDLE=true 283 | ;; 284 | *) 285 | print_usage >&2 286 | exit 127 287 | ;; 288 | esac ; shift 289 | done 290 | 291 | if [ -n "$BUILD_ONLY" -a -n "$PUSH_ONLY" ]; then 292 | echo "Unable to build and push only" >&2 293 | print_usage >&2 294 | exit 1 295 | fi 296 | 297 | quay_logged_in= 298 | components_updated= 299 | artifacts_built= 300 | operator_installed= 301 | cluster_validated= 302 | kustomize_validated= 303 | old_namespace= 304 | 305 | function update_components() { 306 | # Ensure we have the things we need to work with the operator-sdk 307 | if [ -z "$components_updated" ]; then 308 | if [ "$VIRTUAL_ENV" ]; then 309 | error_run "Updating the Operator SDK manager" 'pip3 install --upgrade -r "$SCRIPT_ROOT/requirements.txt"' || return 1 310 | else 311 | error_run "Updating the Operator SDK manager" 'pip3 install --user --upgrade -r "$SCRIPT_ROOT/requirements.txt"' || return 1 312 | fi 313 | error_run "Updating the Operator SDK" 'sdk_version=$(osdk-manager osdk update --no-verify -vvvv | cut -d" " -f 3)' || return 1 314 | fi 315 | components_updated=true 316 | } 317 | 318 | function build_artifacts() { 319 | # Build the operator artifacts from the provided configuration 320 | if [ -z "$artifacts_built" ]; then 321 | if [ -d config ]; then 322 | remove_artifacts 323 | fi 324 | error_run "Initializing Ansible Operator with operator-sdk $sdk_version" operator-sdk init --plugins=ansible --domain=io || return 1 325 | error_run "Creating API config with operator-sdk $sdk_version" operator-sdk create api --group redhatgov --version v1alpha1 --kind $KIND || return 1 326 | fi 327 | artifacts_built=true 328 | } 329 | 330 | function quay_login() { 331 | if [ -z "$quay_logged_in" ]; then 332 | if [ -n "$QUAY_USER" -a -n "$QUAY_PASSWORD" ]; then 333 | error_run "Logging in to quay.io with provided credentials" "docker login -u '$QUAY_USER' -p '$QUAY_PASSWORD' quay.io" || return 1 334 | else 335 | warn_run "No credentials provided, assuming cached login..." false ||: 336 | fi 337 | fi 338 | quay_logged_in=true 339 | } 340 | 341 | function push_images() { 342 | quay_login || return 1 343 | # Push the images to the logged in repository 344 | if [ -z "$DEVELOP" ]; then 345 | for tag in $VERSION ${EXTRA_TAGS[@]}; do 346 | error_run "Building $IMG:$tag" make docker-build IMG=$IMG:$tag || return 1 347 | error_run "Pushing $IMG:$tag" make docker-push IMG=$IMG:$tag || return 1 348 | done 349 | else 350 | error_run "Building $IMG:develop" make docker-build IMG=$IMG:develop || return 1 351 | error_run "Pushing $IMG:develop" make docker-push IMG=$IMG:develop || return 1 352 | fi 353 | } 354 | 355 | function validate_cluster() { 356 | if [ -z "$cluster_validated" ]; then 357 | # Make sure we've got the tooling and cached logins to support application 358 | error_run "Checking for kubectl in path" which kubectl || return 1 359 | error_run "Checking for logged in status on cluster" kubectl get nodes || return 1 360 | fi 361 | cluster_validated=true 362 | } 363 | 364 | function install_operator() { 365 | # Installs the operator defined by built artifacts to the locally logged in 366 | # cluster 367 | if [ -z "$operator_installed" ]; then 368 | validate_cluster || return 1 369 | error_run "Installing operator resources" make install || return 1 370 | error_run "Deploying operator" make deploy IMG=$IMG:latest OVERLAY=$OVERLAY || return 1 371 | fi 372 | operator_installed=true 373 | } 374 | 375 | function uninstall_operator() { 376 | # Uninstalls the operator defined by the built artifacts from the locally 377 | # logged in cluster 378 | validate_cluster || return 1 379 | undeploy_cr 380 | warn_run "Undeploying operator" make undeploy IMG=$IMG:latest OVERLAY=$OVERLAY || : 381 | warn_run "Uninstalling operator resources" make uninstall || : 382 | operator_installed= 383 | } 384 | 385 | function remove_artifacts() { 386 | # Remove operator artifacts from the tree 387 | warn_run "Removing operator files" rm -rf PROJECT Makefile Dockerfile bin bundle bundle.Dockerfile config molecule roles/.placeholder playbooks/.placeholder || : 388 | artifacts_built= 389 | } 390 | 391 | function deploy_cr() { 392 | validate_cluster || return 1 393 | error_run "Deploying custom resource sample" kubectl apply -f "config/samples/${CR_SAMPLE}" || return 1 394 | } 395 | 396 | function undeploy_cr() { 397 | validate_cluster || return 1 398 | warn_run "Undeploying custom resource sample" kubectl delete -f "config/samples/${CR_SAMPLE}" ||: 399 | } 400 | 401 | function validate_kustomize() { 402 | if [ -z "$kustomize_validated" ]; then 403 | error_run "Validating kustomize is installed/downloaded" make kustomize || return 1 404 | project_root=$(pwd) 405 | which kustomize &>/dev/null || function kustomize() { "${project_root}/bin/kustomize" "${@}" ; } 406 | fi 407 | kustomize_validated=true 408 | } 409 | 410 | function kustomize_namespace() { 411 | validate_kustomize || return 1 412 | pushd config/$OVERLAY &>/dev/null 413 | old_namespace=$(awk '/^namespace:/{print $2}' kustomization.yaml) 414 | error_run "Kustomizing namespace" kustomize edit set namespace "$NAMESPACE" || return 1 415 | popd &>/dev/null 416 | } 417 | 418 | function unkustomize_namespace() { 419 | if [ -z "$old_namespace" ]; then 420 | return 0 421 | fi 422 | pushd config/$OVERLAY &>/dev/null 423 | warn_run "Removing namespace kustomization" kustomize edit set namespace "$old_namespace" ||: 424 | popd &>/dev/null 425 | } 426 | 427 | function publish_bundle() { 428 | update_components || return 1 429 | validate_kustomize || return 1 430 | quay_login || return 1 431 | rm -rf bundle bundle.Dockerfile 432 | pushd config/rbac &>/dev/null 433 | error_run "Adding namespaced Role to kustomization" 'kustomize edit add resource namespaced/role.yaml' || return 1 434 | error_run "Adding namespaced RoleBinding to kustomization" 'kustomize edit add resource namespaced/role_binding.yaml' || return 1 435 | popd &>/dev/null 436 | error_run "Building bundle manifests" 'kustomize build --load-restrictor LoadRestrictionsNone config/manifests | operator-sdk generate bundle --overwrite --version $VERSION --channels "$CHANNELS"' || return 1 437 | error_run "Validating bundle" operator-sdk bundle validate ./bundle || return 1 438 | error_run "Adding ConsoleYAMLSamples to bundle" 'cp config/console-samples/manifests/* bundle/manifests/' || return 1 439 | error_run "Building bundle image" docker build -f bundle.Dockerfile -t "$IMG-bundle:$VERSION" . || return 1 440 | if [ -z "$DEVELOP" ]; then 441 | error_run "Pushing image $IMG-bundle:$VERSION" docker push "$IMG-bundle:$VERSION" || return 1 442 | for tag in ${EXTRA_TAGS[@]}; do 443 | error_run "Tagging bundle image with $tag" docker tag "$IMG-bundle:$VERSION" "$IMG-bundle:$tag" || return 1 444 | error_run "Pushing image $IMG-bundle:$tag" docker push "$IMG-bundle:$tag" || return 1 445 | done 446 | else 447 | error_run "Tagging bundle image with develop" docker tag "$IMG-bundle:$VERSION" "$IMG-bundle:develop" || return 1 448 | error_run "Pushing image $IMG-bundle:develop" docker push "$IMG-bundle:develop" || return 1 449 | fi 450 | pushd config/rbac &>/dev/null 451 | warn_run "Removing namespaced Role from kustomization" 'kustomize edit remove resource namespaced/role.yaml' ||: 452 | warn_run "Removing namespaced RoleBinding from kustomization" 'kustomize edit remove resource namespaced/role.yaml' ||: 453 | popd &>/dev/null 454 | } 455 | 456 | if [ "$REMOVE_OPERATOR" ]; then 457 | # Try to remove everything from a cluster 458 | uninstall_operator || : 459 | else 460 | if [ "$BUILD_ARTIFACTS" ]; then 461 | # Build the artifacts necessary to deploy the operator from an image 462 | # NOTE: Removes all existing tweaks to built artifacts! 463 | update_components 464 | build_artifacts 465 | if [ "$BUILD_ONLY" ]; then 466 | exit 0 467 | fi 468 | fi 469 | if [ "$PUSH_IMAGES" ]; then 470 | # Push the images to a repository 471 | # NOTE: REQUIRES YOU TO ACTUALLY LOG IN FIRST 472 | push_images 473 | if [ "$PUSH_ONLY" ]; then 474 | exit 0 475 | fi 476 | fi 477 | 478 | if [ "$NAMESPACE" ]; then 479 | # Ensure kustomize is available and kustomize the namespace 480 | kustomize_namespace 481 | fi 482 | 483 | if [ -z "$PUSH_IMAGES" -o -z "$BUNDLE" ]; then 484 | # Install all of the necessary artifacts 485 | install_operator 486 | fi 487 | 488 | # Apply the artifacts to the currently logged in cluster 489 | if [ "$DEPLOY_CR" ]; then 490 | deploy_cr 491 | elif [ "$UNDEPLOY_CR" ]; then 492 | undeploy_cr 493 | fi 494 | 495 | if [ "$BUNDLE" ]; then 496 | publish_bundle 497 | fi 498 | fi 499 | -------------------------------------------------------------------------------- /hack/requirements.txt: -------------------------------------------------------------------------------- 1 | osdk-manager >= 0.3.1, == 0.3.* 2 | -------------------------------------------------------------------------------- /library/README.md: -------------------------------------------------------------------------------- 1 | This module was pulled from the [RedHat-COP](https://github.com/redhat-cop/k8s_config/blob/master/library/k8s_json_patch.py) repository, which is licensed under the GPL v3 by Johnathan Kupferer. It has been lightly modified here and retains its license. 2 | -------------------------------------------------------------------------------- /library/k8s_json_patch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # (c) 2018, Chris Houseknecht <@chouseknecht> 5 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 | 7 | from __future__ import absolute_import, division, print_function 8 | 9 | __metaclass__ = type 10 | 11 | 12 | ANSIBLE_METADATA = {'metadata_version': '1.1', 13 | 'status': ['preview'], 14 | 'supported_by': 'community'} 15 | 16 | DOCUMENTATION = ''' 17 | 18 | module: k8s_resource 19 | 20 | short_description: Manage Kubernetes (k8s) resources 21 | 22 | version_added: "2.9" 23 | 24 | author: 25 | - "Johnathan Kupferer" 26 | 27 | description: 28 | - Use the OpenShift Python client to patch K8s resources. 29 | - Authenticate using either a config file, certificates, password or token. 30 | - Supports check mode. 31 | 32 | extends_documentation_fragment: 33 | - k8s_auth_options 34 | - k8s_name_options 35 | 36 | options: 37 | patch: 38 | description: 39 | - The patch must be a valid JSON patch list with the following adjustments to support idempotent patching of kubernetes resources. 40 | - C(remove) operations are silently ignored when the path is not found in the resource definition. 41 | - C(add) operations are silently ignored when the path is found with the specified value. 42 | - C(add) operations may specify C(replace=false) to produce an error if the path is set and is different from value. 43 | - C(test) operations may specify C(state) to define how the test value should be evaluated. 44 | - Test C(state=equal) means the path value must equal the specified value, the default behavior. 45 | - Test C(state=unequal) means the path value must not equal the specified value. 46 | - Test C(state=present) means the path must be present with any value 47 | - Test C(state=absent) means the path must not be found in the resource. 48 | - C(test) operations may specify C(operations) as a list of operations to conditionally process if the test condition is true. 49 | - If a test specifies C(operations) then a failed test does not produce an error. 50 | - List indexes may be given with a simple key query of the form C([?KEY=='VALUE']) to support for various kubernetes use cases where lists have name keys. 51 | - The list index query resolves to C(-) (end of list) if it fails to match when adding a value to a list. 52 | type: list 53 | version_added: "2.9" 54 | 55 | requirements: 56 | - "python >= 2.7" 57 | - "openshift >= 0.6" 58 | - "PyYAML >= 3.11" 59 | ''' 60 | 61 | EXAMPLES = ''' 62 | - name: Set key in ConfigMap if not set 63 | k8s_json_patch: 64 | api_version: v1 65 | kind: ConfigMap 66 | name: myconf 67 | patch: 68 | - op: add 69 | path: /data/somekey 70 | value: somevalue 71 | 72 | - name: Set ENV_LEVEL environment variable in Deployment 73 | k8s_json_patch: 74 | api_version: apps/v1 75 | kind: Deployment 76 | name: myapp 77 | patch: 78 | - op: default 79 | path: /spec/template/spec/containers/[?name=='myapp']/env/[?name=='ENV_LEVEL'] 80 | value: 81 | name: ENV_LEVEL 82 | value: dev 83 | ''' 84 | 85 | RETURN = ''' 86 | result: 87 | description: 88 | - The resources created, deleted, patched, or found already in the configured state. 89 | returned: success 90 | type: complex 91 | contains: 92 | resource: 93 | description: The patched resource definition. 94 | returned: success 95 | type: complex 96 | ''' 97 | 98 | import copy 99 | import re 100 | from distutils.version import LooseVersion 101 | 102 | from ansible.module_utils.basic import missing_required_lib 103 | from ansible.module_utils.k8s.common import AUTH_ARG_SPEC, COMMON_ARG_SPEC 104 | from ansible.module_utils.k8s.common import KubernetesAnsibleModule 105 | 106 | try: 107 | import yaml 108 | from openshift.dynamic.exceptions import \ 109 | DynamicApiError, NotFoundError, ForbiddenError 110 | except ImportError: 111 | # Exceptions handled in common 112 | pass 113 | 114 | class JsonPatchFailException(Exception): 115 | pass 116 | 117 | match_list_search = re.compile(r'\[\?(.*)=\'(.*)\'\]$') 118 | match_num = re.compile(r'\d+$') 119 | 120 | def resolve_path(obj, path): 121 | value = obj 122 | context = None 123 | key = None 124 | matched_path = [] 125 | unmatched_path = copy.deepcopy(path) 126 | while unmatched_path: 127 | if not isinstance(value, (dict, list)): 128 | break 129 | key = unmatched_path.pop(0) 130 | context = value 131 | value = None 132 | if key == '-' and isinstance(context, list): 133 | unmatched_path.insert(0, str(len(context))) 134 | break 135 | 136 | if isinstance(context, dict): 137 | if key in context: 138 | matched_path.append(key) 139 | value = context[key] 140 | continue 141 | else: 142 | unmatched_path.insert(0, key) 143 | break 144 | 145 | if match_num.match(key): 146 | key = int(key) 147 | if key < len(context): 148 | matched_path.append(str(key)) 149 | value = context[key] 150 | continue 151 | else: 152 | unmatched_path.insert(0, str(key)) 153 | break 154 | 155 | list_search_match = match_list_search.match(key) 156 | if list_search_match: 157 | search_key = list_search_match.group(1) 158 | search_value = list_search_match.group(2) 159 | n = None 160 | for i, ci in enumerate(context): 161 | if isinstance(ci, dict) \ 162 | and ci.get(search_key) == search_value: 163 | n = i 164 | value = ci 165 | break 166 | if n != None: 167 | matched_path.append(str(n)) 168 | key = n 169 | continue 170 | else: 171 | unmatched_path.insert(0, key) 172 | break 173 | 174 | raise JsonPatchFailException('Unable to match path dict key in list {0}'.format(path_from_list(path))) 175 | return value, context, key, matched_path, unmatched_path 176 | 177 | def path_to_list(path): 178 | return [ 179 | item.replace('~1', '/').replace('~0', '~') for item in path.split('/')[1:] 180 | ] 181 | 182 | def path_from_list(path, last=None): 183 | if last: 184 | return '/' + '/'.join(path) + '/' + last 185 | else: 186 | return '/' + '/'.join(path) 187 | 188 | def _process_patch_add(patch_operation, patched_obj, value, context, matched_path, unmatched_path): 189 | for item in unmatched_path[:0:-1]: 190 | if match_num.match(item): 191 | value = [value] 192 | else: 193 | match = match_list_search.match(item) 194 | if match: 195 | if not isinstance(value, dict): 196 | raise JsonPatchFailException('Invalid placement of list query in path {0}'.format(patch_operation)) 197 | value[match.group(1)] = match.group(2) 198 | value = [value] 199 | else: 200 | value = {item: value} 201 | 202 | if isinstance(context, dict): 203 | context[unmatched_path[0]] = copy.deepcopy(value) 204 | processed_path = path_from_list(matched_path, unmatched_path[0]) 205 | else: 206 | if match_num.match(unmatched_path[0]): 207 | context.append(copy.deepcopy(value)) 208 | processed_path = path_from_list(matched_path, '-') 209 | else: 210 | match = match_list_search.match(unmatched_path[0]) 211 | if not match: 212 | raise JsonPatchFailException('Unable to add, invalid list index {0}'.format(patch_operation)) 213 | if not isinstance(value, dict): 214 | raise JsonPatchFailException('Invalid placement of list query in path {0}'.format(patch_operation)) 215 | value[match.group(1)] = match.group(2) 216 | context.append(copy.deepcopy(value)) 217 | processed_path = path_from_list(matched_path, '-') 218 | 219 | return dict(op='add', path=processed_path, value=value) 220 | 221 | def process_patch_add(patch_operation, patched_obj): 222 | path = path_to_list(patch_operation['path']) 223 | value = copy.deepcopy(patch_operation.get('value')) 224 | obj_value, context, key, matched_path, unmatched_path = resolve_path(patched_obj, path) 225 | if unmatched_path: 226 | return _process_patch_add(patch_operation, patched_obj, value, context, matched_path, unmatched_path) 227 | elif value == obj_value: 228 | # Already present with desired value, nothing to do 229 | return None 230 | elif patch_operation.get('replace', True): 231 | if isinstance(context, list) and key == len(context): 232 | context.append(value) 233 | else: 234 | context[key] = value 235 | processed_path = path_from_list(matched_path) 236 | return dict(op='replace', path=processed_path, value=value) 237 | else: 238 | raise JsonPatchFailException('Unable to add, path already exists {0}'.format(patch_operation)) 239 | 240 | def process_patch_copy(patch_operation, patched_obj): 241 | src_path = path_to_list(patch_operation['from']) 242 | dst_path = path_to_list(patch_operation['path']) 243 | src_value, src_context, src_key, src_matched_path, src_unmatched_path = resolve_path(patched_obj, src_path) 244 | dst_value, dst_context, dst_key, dst_matched_path, dst_unmatched_path = resolve_path(patched_obj, dst_path) 245 | if src_unmatched_path: 246 | raise JsonPatchFailException('Unable to copy, from path not found {0}'.format(patch_operation)) 247 | elif src_value == dst_value: 248 | # Already present with desired value, nothing to do 249 | return None 250 | elif dst_unmatched_path: 251 | # Handle as add to resolve any queries in the destination 252 | return _process_patch_add(patch_operation, patched_obj, src_value, dst_context, dst_matched_path, dst_unmatched_path) 253 | else: 254 | dst_context[dst_key] = src_value 255 | src_processed_path = path_from_list(src_matched_path) 256 | dst_processed_path = path_from_list(dst_matched_path) 257 | return {'op': 'copy', 'path': dst_processed_path, 'from': src_processed_path} 258 | 259 | def process_patch_move(patch_operation, patched_obj): 260 | src_path = path_to_list(patch_operation['from']) 261 | dst_path = path_to_list(patch_operation['path']) 262 | src_value, src_context, src_key, src_matched_path, src_unmatched_path = resolve_path(patched_obj, src_path) 263 | dst_value, dst_context, dst_key, dst_matched_path, dst_unmatched_path = resolve_path(patched_obj, dst_path) 264 | if src_unmatched_path: 265 | raise JsonPatchFailException('Unable to move, from path not found {0}'.format(patch_operation)) 266 | elif dst_unmatched_path: 267 | # Handle as remove then add resolve any queries in the destination 268 | src_processed_path = path_from_list(src_matched_path) 269 | del src_context[src_key] 270 | return [ 271 | dict(op='remove', path=src_processed_path), 272 | _process_patch_add(patch_operation, patched_obj, src_value, dst_context, dst_matched_path, dst_unmatched_path) 273 | ] 274 | else: 275 | dst_context[dst_key] = src_value 276 | del src_context[src_key] 277 | src_processed_path = path_from_list(src_matched_path) 278 | dst_processed_path = path_from_list(dst_matched_path) 279 | return {'op': 'copy', 'path': dst_processed_path, 'from': src_processed_path} 280 | 281 | def process_patch_remove(patch_operation, patched_obj): 282 | path = path_to_list(patch_operation['path']) 283 | obj_value, context, key, matched_path, unmatched_path = resolve_path(patched_obj, path) 284 | if unmatched_path: 285 | return None 286 | else: 287 | del context[key] 288 | processed_path = path_from_list(matched_path) 289 | return dict(op='remove', path=processed_path) 290 | 291 | def process_patch_replace(patch_operation, patched_obj): 292 | path = path_to_list(patch_operation['path']) 293 | value = copy.deepcopy(patch_operation.get('value')) 294 | obj_value, context, key, matched_path, unmatched_path = resolve_path(patched_obj, path) 295 | if unmatched_path: 296 | raise JsonPatchFailException('Unable to replace, path not found {0}'.format(patch_operation)) 297 | elif value == obj_value: 298 | # Already present with desired value, nothing to do 299 | return None 300 | else: 301 | context[key] = value 302 | processed_path = path_from_list(matched_path) 303 | return dict(op='replace', path=processed_path, value=value) 304 | 305 | def process_patch_test(patch_operation, patched_obj): 306 | path = path_to_list(patch_operation['path']) 307 | value = patch_operation.get('value') 308 | allowed_states = patch_operation.get('state', ['equal']) 309 | if not isinstance(allowed_states, list): 310 | allowed_states = [allowed_states] 311 | obj_value, context, key, matched_path, unmatched_path = resolve_path(patched_obj, path) 312 | if unmatched_path: 313 | if 'absent' in allowed_states: 314 | return None 315 | else: 316 | raise JsonPatchFailException('Test failed, path not found {0}'.format(patch_operation)) 317 | elif 'equal' in allowed_states and value == obj_value: 318 | return dict(op='test', path=path_from_list(matched_path), value=value) 319 | elif 'present' in allowed_states: 320 | return None 321 | elif 'unequal' in allowed_states and value != obj_value: 322 | return None 323 | else: 324 | raise JsonPatchFailException('Test failed {0}'.format(patch_operation)) 325 | 326 | def process_patch(json_patch, existing): 327 | processed_patch = [] 328 | patched_obj = copy.deepcopy(existing) 329 | patch_operations = copy.deepcopy(json_patch) 330 | 331 | while patch_operations: 332 | patch_operation = patch_operations.pop(0) 333 | 334 | op = patch_operation.get('op') 335 | if op == 'add': 336 | patch_operation = process_patch_add(patch_operation, patched_obj) 337 | elif op == 'copy': 338 | patch_operation = process_patch_copy(patch_operation, patched_obj) 339 | elif op == 'move': 340 | patch_operation = process_patch_move(patch_operation, patched_obj) 341 | elif op == 'remove': 342 | patch_operation = process_patch_remove(patch_operation, patched_obj) 343 | elif op == 'replace': 344 | patch_operation = process_patch_replace(patch_operation, patched_obj) 345 | elif op == 'test': 346 | test_operations = patch_operation.get('operations') 347 | test_path = patch_operation.get('path') 348 | try: 349 | patch_operation = process_patch_test(patch_operation, patched_obj) 350 | if test_operations: 351 | # Set path for operations if unset 352 | for operation in test_operations: 353 | if 'path' not in operation: 354 | operation['path'] = test_path 355 | # Add test operations to the beginning of patch operations list 356 | test_operations.extend(patch_operations) 357 | patch_operations = test_operations 358 | except JsonPatchFailException: 359 | if test_operations == None: 360 | raise 361 | else: 362 | patch_operation = None 363 | else: 364 | raise JsonPatchFailException('No op in {0}'.format(patch_operation)) 365 | 366 | if patch_operation: 367 | if isinstance(patch_operation, list): 368 | processed_patch.extend(patch_operation) 369 | else: 370 | processed_patch.append(patch_operation) 371 | 372 | return processed_patch, patched_obj 373 | 374 | class KubernetesJsonPatchModule(KubernetesAnsibleModule): 375 | @property 376 | def argspec(self): 377 | argument_spec = copy.deepcopy(COMMON_ARG_SPEC) 378 | argument_spec.update(copy.deepcopy(AUTH_ARG_SPEC)) 379 | argument_spec['patch'] = dict( 380 | type='list', 381 | default=[], 382 | ) 383 | return argument_spec 384 | 385 | def __init__(self, *args, **kwargs): 386 | self.client = None 387 | 388 | KubernetesAnsibleModule.__init__( 389 | self, *args, 390 | supports_check_mode=True, 391 | **kwargs 392 | ) 393 | 394 | self.kind = self.params.get('kind') 395 | self.api_version = self.params.get('api_version') 396 | self.name = self.params.get('name') 397 | self.namespace = self.params.get('namespace') 398 | self.patch = self.params.get('patch') 399 | 400 | if LooseVersion(self.openshift_version) < LooseVersion("0.6.2"): 401 | self.fail_json(msg=missing_required_lib("openshift >= 0.6.2")) 402 | 403 | def execute_module(self): 404 | self.client = self.get_api_client() 405 | resource = self.find_resource(self.kind, self.api_version, fail=True) 406 | params = dict(name = self.name) 407 | if self.namespace: 408 | params['namespace'] = self.namespace 409 | try: 410 | existing = resource.get(**params).to_dict() 411 | except (DynamicApiError, ForbiddenError, NotFoundError): 412 | self.fail_json( 413 | msg='Failed to retrieve requested object: {0}'.format(exc.body), 414 | error=exc.status, status=exc.status, reason=exc.reason 415 | ) 416 | 417 | try: 418 | processed_patch, patched_obj = process_patch(self.patch, existing) 419 | except JsonPatchFailException as e: 420 | self.fail_json( 421 | msg='Failed processing json_patch: {0}'.format(e) 422 | ) 423 | 424 | # If no changes in the processed patch, then just apply 425 | if not processed_patch: 426 | self.exit_json(changed=False, resource=existing) 427 | # For check mode, just return locally patched object 428 | if self.check_mode: 429 | self.exit_json(changed=True, resource=patched_obj, json_patch=processed_patch) 430 | try: 431 | k8s_obj = resource.patch(processed_patch, content_type='application/json-patch+json', **params).to_dict() 432 | self.exit_json(changed=True, resource=k8s_obj, json_patch=processed_patch) 433 | except DynamicApiError as exc: 434 | self.fail_json( 435 | msg="Failed to patch object: {0}".format(exc.body), 436 | error=exc.status, status=exc.status, reason=exc.reason 437 | ) 438 | 439 | 440 | def main(): 441 | KubernetesJsonPatchModule().execute_module() 442 | 443 | if __name__ == '__main__': 444 | main() 445 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | collections: 7 | - community.kubernetes 8 | 9 | tasks: 10 | - name: Create Namespace 11 | k8s: 12 | api_version: v1 13 | kind: Namespace 14 | name: '{{ item }}' 15 | loop: 16 | - '{{ namespace }}' 17 | 18 | - import_tasks: kustomize.yml 19 | vars: 20 | state: present 21 | -------------------------------------------------------------------------------- /molecule/default/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | tasks: [] 7 | -------------------------------------------------------------------------------- /molecule/default/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - community.kubernetes 8 | 9 | tasks: 10 | - import_tasks: kustomize.yml 11 | vars: 12 | state: absent 13 | 14 | - name: Destroy Namespace 15 | k8s: 16 | api_version: v1 17 | kind: Namespace 18 | name: '{{ namespace }}' 19 | state: absent 20 | 21 | - name: Unset pull policy 22 | command: '{{ kustomize }} edit remove patch pull_policy/{{ operator_pull_policy }}.yaml' 23 | args: 24 | chdir: '{{ config_dir }}/testing/{{ scope|default("cluster") }}_scope' 25 | -------------------------------------------------------------------------------- /molecule/default/kustomize.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Build kustomize testing overlay 3 | # load_restrictor must be set to none so we can load patch files from the default overlay 4 | command: '{{ kustomize }} build --load-restrictor LoadRestrictionsNone .' 5 | args: 6 | chdir: '{{ config_dir }}/testing/{{ scope|default("cluster") }}_scope' 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 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: delegated 6 | lint: | 7 | set -e 8 | yamllint -d "{extends: relaxed, rules: {line-length: {max: 120}}}" . 9 | platforms: 10 | - name: cluster 11 | groups: 12 | - k8s 13 | provisioner: 14 | name: ansible 15 | lint: | 16 | set -e 17 | ansible-lint 18 | inventory: 19 | group_vars: 20 | all: 21 | namespace: ${TEST_OPERATOR_NAMESPACE:-ploigos-operator-system} 22 | host_vars: 23 | localhost: 24 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 25 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 26 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 27 | operator_image: ${OPERATOR_IMAGE:-""} 28 | operator_pull_policy: ${OPERATOR_PULL_POLICY:-"Always"} 29 | kustomize: ${KUSTOMIZE:-kustomize} 30 | env: 31 | K8S_AUTH_KUBECONFIG: ${KUBECONFIG:-"~/.kube/config"} 32 | verifier: 33 | name: ansible 34 | lint: | 35 | set -e 36 | ansible-lint 37 | -------------------------------------------------------------------------------- /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/{{ scope|default("cluster") }}_scope' 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/{{ scope|default("cluster") }}_scope' 24 | 25 | - name: Set testing namespace 26 | command: '{{ kustomize }} edit set namespace {{ namespace }}' 27 | args: 28 | chdir: '{{ config_dir }}/testing/{{ scope|default("cluster") }}_scope' 29 | -------------------------------------------------------------------------------- /molecule/default/tasks/ploigos_software_factory_test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - set_fact: 3 | custom_resources: 4 | - PloigosPlatform 5 | 6 | - name: Create CR 7 | k8s: 8 | state: present 9 | namespace: '{{ namespace }}' 10 | definition: "{{ lookup('template', '/'.join([samples_dir, cr_file])) | from_yaml }}" 11 | vars: 12 | cr_file: 'redhatgov_v1alpha1_{{ item | lower }}_molecule.yaml' 13 | loop: '{{ custom_resources }}' 14 | 15 | # Ensure regular Ansible output to avoid Travis build timeouts 16 | - name: Wait for CR to be Reconciled 17 | k8s_info: 18 | api_version: v1alpha1 19 | kind: '{{ item }}' 20 | namespace: '{{ namespace }}' 21 | name: '{{ item | lower}}-typical' 22 | register: result 23 | until: result.resources[0].status | json_query("conditions[?(@.reason=='Successful')]") 24 | retries: 30 25 | delay: 60 26 | loop: '{{ custom_resources }}' 27 | -------------------------------------------------------------------------------- /molecule/default/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Verify 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | collections: 7 | - community.kubernetes 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 controller Pod logs 36 | k8s_log: 37 | name: '{{ item.metadata.name }}' 38 | namespace: '{{ namespace }}' 39 | container: manager 40 | loop: "{{ q('k8s', api_version='v1', kind='Pod', namespace=namespace, label_selector=ctrl_label) }}" 41 | register: debug_controller_logs 42 | 43 | - name: Retrieve application Pod logs 44 | k8s_log: 45 | name: '{{ item.metadata.name }}' 46 | namespace: '{{ namespace }}' 47 | loop: "{{ q('k8s', api_version='v1', kind='Pod', namespace=namespace, label_selector='app=ploigosplatform-typical') }}" 48 | register: debug_app_logs 49 | 50 | - name: Output gathered resources 51 | debug: 52 | var: debug_resources 53 | 54 | - name: Output gathered logs 55 | debug: 56 | var: item.log_lines 57 | loop: '{{ debug_controller_logs.results + debug_app_logs.results }}' 58 | 59 | - name: Re-emit failure 60 | vars: 61 | failed_task: 62 | result: '{{ ansible_failed_result }}' 63 | fail: 64 | msg: '{{ failed_task }}' 65 | -------------------------------------------------------------------------------- /molecule/ocp-cluster/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: localhost 4 | connection: local 5 | gather_facts: no 6 | 7 | tasks: 8 | - name: Login to quay 9 | docker_login: 10 | registry_url: quay.io 11 | username: '{{ quay_username }}' 12 | password: '{{ quay_password }}' 13 | 14 | - name: Build operator image 15 | docker_image: 16 | build: 17 | path: '{{ project_dir }}' 18 | pull: no 19 | name: '{{ operator_image }}' 20 | tag: latest 21 | push: yes 22 | source: build 23 | force_source: yes 24 | 25 | - import_playbook: ../default/converge.yml 26 | -------------------------------------------------------------------------------- /molecule/ocp-cluster/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | tasks: 7 | - name: Login to OCP 8 | command: oc login $OCP_SERVER -u $OCP_USERNAME -p $OCP_PASSWORD --insecure-skip-tls-verify 9 | -------------------------------------------------------------------------------- /molecule/ocp-cluster/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - community.kubernetes 8 | 9 | tasks: 10 | - name: Login to OCP 11 | command: oc login $OCP_SERVER -u $OCP_USERNAME -p $OCP_PASSWORD --insecure-skip-tls-verify 12 | 13 | - set_fact: 14 | custom_resources: 15 | - PloigosPlatform 16 | 17 | - name: Remove CR 18 | k8s: 19 | api_version: v1alpha1 20 | kind: '{{ item }}' 21 | state: absent 22 | name: '{{ item | lower }}-typical' 23 | namespace: '{{ namespace }}' 24 | ignore_errors: true 25 | loop: '{{ custom_resources }}' 26 | 27 | - name: Wait for CR to be deleted 28 | k8s_info: 29 | api_version: v1alpha1 30 | kind: '{{ item }}' 31 | name: '{{ item | lower }}-typical' 32 | namespace: '{{ namespace }}' 33 | register: result 34 | until: result.resources | length == 0 35 | ignore_errors: true 36 | retries: 30 37 | delay: 10 38 | loop: '{{ custom_resources }}' 39 | 40 | - name: Remove Namespaces 41 | k8s: 42 | api_version: v1 43 | kind: Namespace 44 | name: '{{ namespace }}' 45 | state: absent 46 | loop: '{{ custom_resources }}' 47 | -------------------------------------------------------------------------------- /molecule/ocp-cluster/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: delegated 6 | lint: | 7 | set -e 8 | yamllint -d "{extends: relaxed, rules: {line-length: {max: 120}}}" . 9 | platforms: 10 | - name: cluster 11 | groups: 12 | - k8s 13 | provisioner: 14 | name: ansible 15 | playbooks: 16 | prepare: ../default/prepare.yml 17 | verify: ../default/verify.yml 18 | lint: | 19 | set -e 20 | ansible-lint 21 | inventory: 22 | group_vars: 23 | all: 24 | namespace: ${TEST_OPERATOR_NAMESPACE:-ploigos-operator-system} 25 | host_vars: 26 | localhost: 27 | ansible_python_interpreter: '{{ ansible_playbook_python }}' 28 | config_dir: ${MOLECULE_PROJECT_DIRECTORY}/config 29 | samples_dir: ${MOLECULE_PROJECT_DIRECTORY}/config/samples 30 | project_dir: ${MOLECULE_PROJECT_DIRECTORY} 31 | quay_username: "{{ lookup('env', 'QUAY_USERNAME') }}" 32 | quay_password: "{{ lookup('env', 'QUAY_PASSWORD') }}" 33 | operator_image: ${OPERATOR_IMAGE:-"quay.io/redhatgov/ploigos-operator:test"} 34 | operator_pull_policy: "Always" 35 | kubeconfig: "{{ lookup('env', 'KUBECONFIG') }}" 36 | kustomize: ${KUSTOMIZE:-kustomize} 37 | scope: cluster 38 | env: 39 | K8S_AUTH_KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 40 | KUBECONFIG: ${MOLECULE_EPHEMERAL_DIRECTORY}/kubeconfig 41 | verifier: 42 | name: ansible 43 | lint: | 44 | set -e 45 | ansible-lint 46 | -------------------------------------------------------------------------------- /playbooks/ploigos.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | tasks: 4 | - name: Get cluster dns 5 | k8s_info: 6 | api_version: config.openshift.io/v1 7 | kind: DNS 8 | namespace: openshift-config 9 | name: cluster 10 | register: cluster_dns 11 | 12 | - name: Ensure Quay.io is in containerRuntimeSearchRegistries 13 | k8s: 14 | kind: Image 15 | definition: | 16 | apiVersion: config.openshift.io/v1 17 | kind: Image 18 | metadata: 19 | name: cluster 20 | spec: 21 | registrySources: 22 | containerRuntimeSearchRegistries: 23 | - quay.io 24 | - docker.io 25 | - registry.connect.redhat.com 26 | 27 | - set_fact: 28 | cluster_name: '{{ (cluster_dns.resources|first).spec.baseDomain.split(".")[0] }}' 29 | 30 | - set_fact: 31 | openshift_base_domain: "{{ (cluster_dns.resources|first).spec.baseDomain.replace(cluster_name + '.','') }}" 32 | ploigos_namespace: "{{ ansible_operator_meta.namespace }}" 33 | 34 | - name: Set project names 35 | set_fact: 36 | argocd_project_name: '{{ ploigos_namespace }}' 37 | gitea_project_name: '{{ ploigos_namespace }}' 38 | jenkins_project_name: '{{ ploigos_namespace }}' 39 | nexus_project_name: '{{ ploigos_namespace }}' 40 | quay_project_name: '{{ ploigos_namespace }}' 41 | rhsso_project_name: '{{ ploigos_namespace }}' 42 | selenium_grid_project_name: '{{ ploigos_namespace }}' 43 | sonarqube_project_name: '{{ ploigos_namespace }}' 44 | gitlab_project_name: '{{ploigos_namespace}}' 45 | 46 | - name: Set ploigos service account secret 47 | set_fact: 48 | ploigos_service_account_secret: ploigos-service-account-credentials 49 | 50 | - name: Look for default defined common variables 51 | include_vars: ../vars/ploigos-platform/common.yml 52 | 53 | - name: Ensure temporary directory exists 54 | file: 55 | path: "{{ tmp_dir }}" 56 | state: directory 57 | 58 | - include_role: 59 | name: ploigos-platform/deployment 60 | when: cr_type == 'platform' 61 | 62 | - include_role: 63 | name: ploigos-pipeline 64 | when: cr_type == 'pipeline' 65 | -------------------------------------------------------------------------------- /playbooks/roles: -------------------------------------------------------------------------------- 1 | ../roles -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - name: community.kubernetes 4 | version: 1.2.1 5 | - name: operator_sdk.util 6 | - name: ploigos.service_configs 7 | -------------------------------------------------------------------------------- /roles/ploigos-pipeline/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /roles/ploigos-pipeline/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure that a PloigosPlatform exists here 3 | k8s_info: 4 | api_version: redhatgov.io/v1alpha1 5 | kind: PloigosPlatform 6 | namespace: '{{ ploigos_namespace }}' 7 | register: ploigos_platform_result 8 | until: 9 | - ploigos_platform_result.resources[0].status is defined 10 | - ploigos_platform_result.resources[0].status | json_query("conditions[?(@.reason=='Successful')]") 11 | retries: 20 12 | delay: 60 13 | 14 | - set_fact: 15 | ploigos_platform: "{{ ploigos_platform_result.resources[0].spec.ploigosPlatform }}" 16 | 17 | - name: Fetch ploigos-service-account secret 18 | set_fact: 19 | secret_data: "{{ lookup('k8s', kind='Secret', namespace=ploigos_namespace, resource_name='ploigos-service-account-credentials') }}" 20 | 21 | - name: Set ploigos-service-account credential facts from secret 22 | set_fact: 23 | ploigos_service_account: 24 | username: "{{ secret_data.data.username | b64decode }}" 25 | password: "{{ secret_data.data.password | b64decode }}" 26 | email: "{{ secret_data.data.email | b64decode }}" 27 | first_name: "{{ secret_data.data.first_name | b64decode }}" 28 | last_name: "{{ secret_data.data.last_name | b64decode }}" 29 | 30 | - name: Create quay repository 31 | shell: >- 32 | devsecops-api quay add-repo https://quay-{{ ploigos_namespace }}.apps.{{ full_cluster_name }} 33 | --login-username '{{ ploigos_service_account.username }}' --login-password '{{ ploigos_service_account.password }}' 34 | --organization platform --repo-name {{ app_name}}-{{ service_name }} 35 | when: ploigos_platform.services.containerRegistry.quay.enabled | default(false) 36 | 37 | - name: Setup Gitea 38 | block: 39 | - name: Get gitea token secret 40 | k8s_info: 41 | kind: Secret 42 | api_version: v1 43 | namespace: '{{ ploigos_namespace }}' 44 | name: gitea-access-token 45 | register: gitea_token_secret 46 | 47 | - name: Get gitea token from secret 48 | set_fact: 49 | gitea_token: "{{ gitea_token_secret.resources[0].data.token | b64decode }}" 50 | 51 | - name: Get platform org uid 52 | uri: 53 | url: 'https://gitea-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}/api/v1/orgs/platform' 54 | validate_certs: false 55 | body_format: json 56 | headers: 57 | Authorization: 'token {{ gitea_token }}' 58 | register: gitea_org_response 59 | 60 | - name: Migrate app and helm repos From Upstream into Gitea, in platform org 61 | uri: 62 | url: 'https://gitea-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}/api/v1/repos/migrate' 63 | validate_certs: false 64 | body_format: json 65 | method: POST 66 | headers: 67 | Authorization: 'token {{ gitea_token }}' 68 | body: | 69 | { 70 | "clone_addr": "{{ repo.source_url }}", 71 | "uid": {{ gitea_org_response.json.id }}, 72 | "repo_name": "{{ repo.destination_repo_name }}" 73 | } 74 | status_code: 201,409 75 | register: gitea_repo_migration_task 76 | changed_when: gitea_repo_migration_task.status == 201 77 | loop: 78 | - '{{ app_repo }}' 79 | - '{{ helm_repo }}' 80 | loop_control: 81 | loop_var: repo 82 | label: repo.name 83 | when: ploigos_platform.services.sourceControl.gitea.enabled | default(false) 84 | 85 | - name: Setup GitLab 86 | block: 87 | - name: Get gitlab token secret 88 | k8s_info: 89 | kind: Secret 90 | api_version: v1 91 | namespace: '{{ ploigos_namespace }}' 92 | name: gitlab-access-token 93 | register: gitlab_token_secret 94 | 95 | - name: Get gitlab token from secret 96 | set_fact: 97 | gitlab_token: "{{ gitlab_token_secret.resources[0].data.token | b64decode }}" 98 | 99 | - name: Get GitLab namespace UID 100 | uri: 101 | url: 'https://gitlab.apps.{{ full_cluster_name }}/api/v4/namespaces?search=platform' 102 | validate_certs: false 103 | method: GET 104 | body_format: json 105 | headers: 106 | PRIVATE-TOKEN: "{{ gitlab_token }}" 107 | status_code: 200 108 | register: gitlab_ns_response 109 | 110 | - name: Check for existing projects 111 | uri: 112 | url: 'https://gitlab.apps.{{ full_cluster_name }}/api/v4/projects?search={{ app_repo.destination_repo_name }}' 113 | validate_certs: false 114 | method: GET 115 | return_content: true 116 | body_format: json 117 | headers: 118 | PRIVATE-TOKEN: "{{ gitlab_token }}" 119 | status_code: 200 120 | register: gitlab_project_check_response 121 | 122 | - name: Set GitLab Project ID 123 | set_fact: 124 | gitlab_project_id: "{{ gitlab_project_check_response.json[0].id }}" 125 | when: gitlab_project_check_response.json != [] 126 | 127 | - name: Migrate app repo From Upstream into GitLab, in platform group 128 | block: 129 | - name: Create new project 130 | uri: 131 | url: 'https://gitlab.apps.{{ full_cluster_name }}/api/v4/projects' 132 | validate_certs: false 133 | method: POST 134 | body_format: json 135 | headers: 136 | PRIVATE-TOKEN: "{{ gitlab_token }}" 137 | body: 138 | name: "{{ app_repo.destination_repo_name }}" 139 | import_url: "{{ app_repo.source_url }}" 140 | namespace_id: "{{ gitlab_ns_response.json[0].id }}" 141 | ci_config_path: "{{ app_repo.ci_config_path }}" 142 | visibility: "public" 143 | status_code: 201,409 144 | register: gitlab_repo_migration_task 145 | changed_when: gitlab_repo_migration_task.status == 201 146 | 147 | - name: Get project id 148 | set_fact: 149 | gitlab_project_id: "{{ gitlab_repo_migration_task.json.id }}" 150 | 151 | - name: Migrate helm repo From Upstream into GitLab, in platform group 152 | uri: 153 | url: 'https://gitlab.apps.{{ full_cluster_name }}/api/v4/projects' 154 | validate_certs: false 155 | method: POST 156 | body_format: json 157 | headers: 158 | PRIVATE-TOKEN: "{{ gitlab_token }}" 159 | body: 160 | name: "{{ helm_repo.destination_repo_name }}" 161 | import_url: "{{ helm_repo.source_url }}" 162 | namespace_id: "{{ gitlab_ns_response.json[0].id }}" 163 | ci_config_path: "{{ helm_repo.ci_config_path }}" 164 | visibility: "public" 165 | status_code: 201,409 166 | register: gitlab_repo_migration_task 167 | changed_when: gitlab_repo_migration_task.status == 201 168 | when: gitlab_project_check_response.json == [] 169 | 170 | when: ploigos_platform.services.sourceControl.gitlab.enabled | default(false) 171 | 172 | - name: Create pipeline in Jenkins 173 | block: 174 | - name: Get Service Account token 175 | set_fact: 176 | ocp_token: "{{ lookup('file', '/var/run/secrets/kubernetes.io/serviceaccount/token') }}" 177 | 178 | - name: Scan Gitea Org to create Jenkins job 179 | uri: 180 | url: 'https://jenkins-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}/job/platform/build?delay=0' 181 | method: POST 182 | validate_certs: false 183 | headers: 184 | Authorization: 'Bearer {{ ocp_token }}' 185 | status_code: "302" 186 | when: ploigos_platform.services.continuousIntegration.jenkins.enabled | default(false) 187 | 188 | - name: Create pipeline in Tekton 189 | block: 190 | - name: Add ploigos chart repo 191 | community.kubernetes.helm_repository: 192 | name: ploigos_charts 193 | repo_url: "{{ ploigos_platform.helmRepository }}" 194 | 195 | - name: Deploy pipeline resources chart 196 | community.kubernetes.helm: 197 | name: ploigos-workflow-typical-tekton-pipeline 198 | chart_ref: ploigos_charts/ploigos-workflow-tekton-pipeline-typical 199 | values: "{{ lookup('template', 'ploigos-pipeline-values.yaml.j2') | from_yaml }}" 200 | release_namespace: "{{ ploigos_namespace }}" 201 | 202 | - name: Create PipelineRun, if autoStartPipeline is true 203 | community.kubernetes.k8s: 204 | definition: "{{ lookup('template', 'pipeline-run.yml.j2') }}" 205 | when: auto_start_pipeline 206 | when: ploigos_platform.services.continuousIntegration.tekton.enabled | default(false) 207 | 208 | - name: Create pipeline in GitLab CI 209 | block: 210 | - name: Create a pipeline trigger 211 | uri: 212 | url: 'https://gitlab.apps.{{ full_cluster_name }}/api/v4/projects/{{ gitlab_project_id }}/triggers' 213 | validate_certs: false 214 | body_format: json 215 | method: POST 216 | headers: 217 | PRIVATE-TOKEN: "{{ gitlab_token }}" 218 | body: 219 | description: "Ploigos trigger" 220 | id: "{{ gitlab_project_id }}" 221 | status_code: 201,409 222 | register: gitlab_trigger_task 223 | changed_when: gitlab_trigger_task.status == 201 224 | 225 | - name: Trigger GitLab CI pipeline 226 | uri: 227 | url: 'https://gitlab.apps.{{ full_cluster_name }}/api/v4/projects/{{ gitlab_project_id }}/trigger/pipeline' 228 | validate_certs: false 229 | method: POST 230 | body_format: json 231 | headers: 232 | PRIVATE-TOKEN: "{{ gitlab_token }}" 233 | body: 234 | ref: main 235 | token: "{{ gitlab_trigger_task.json.token }}" 236 | status_code: 201,409 237 | register: gitlab_trigger_ci_task 238 | changed_when: gitlab_trigger_ci_task.status == 201 239 | when: ploigos_platform.services.continuousIntegration.gitlabCi.enabled | default(false) 240 | -------------------------------------------------------------------------------- /roles/ploigos-pipeline/templates/pipeline-run.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: PipelineRun 3 | metadata: 4 | name: {{ app_name }}-{{ service_name }} 5 | namespace: {{ ploigos_namespace }} 6 | labels: 7 | app.kubernetes.io/component: ploigos-workflow 8 | app.kubernetes.io/instance: ploigos-workflow-tekton-pipeline-everything-1611177845 9 | app.kubernetes.io/managed-by: Helm 10 | app.kubernetes.io/name: {{ service_name }} 11 | app.kubernetes.io/part-of: {{ app_name }} 12 | helm.sh/chart: ploigos-workflow-tekton-pipeline-everything-0.18.0 13 | ploigos.dev/workflow: standard 14 | tekton.dev/pipeline: {{ app_name }}-{{ service_name }} 15 | spec: 16 | params: 17 | - name: verbose 18 | value: 'true' 19 | - name: appRepoUrl 20 | value: >- 21 | http://gitea.{{ ploigos_namespace }}.svc.cluster.local:3000/platform/{{ app_repo.destination_repo_name }}.git 22 | - name: appRepoRef 23 | value: main 24 | - name: appCloneSSLVerify 25 | value: 'false' 26 | - name: stepRunnerConfigDir 27 | value: /opt/platform-config/ /opt/platform-config-secrets/ cicd/ploigos-software-factory-operator/ploigos-step-runner-config/ 28 | - name: pgpKeysSecretName 29 | value: ploigos-gpg-key 30 | - name: envNameDev 31 | value: DEV 32 | - name: envNameTest 33 | value: TEST 34 | - name: envNameProd 35 | value: PROD 36 | - name: ciOnlyGitRefPatterns 37 | value: ^$ 38 | - name: devGitRefPatterns 39 | value: ^feature/.+$|^PR-.+$ 40 | - name: releaseGitRefPatterns 41 | value: ^main$ 42 | - name: stepRunnerPackageName 43 | value: ploigos-step-runner 44 | - name: stepRunnerUpdateLibrary 45 | value: 'false' 46 | - name: stepRunnerLibIndexUrl 47 | value: 'https://pypi.org/simple/' 48 | - name: stepRunnerLibExtraIndexUrl 49 | value: 'https://pypi.org/simple/' 50 | - name: stepRunnerLibVersion 51 | value: v0.16.0 52 | - name: stepRunnerLibSourceUrl 53 | value: 'https://github.com/ploigos/ploigos-step-runner.git' 54 | - name: workflowWorkerImageDefault 55 | value: 'quay.io/ploigos/ploigos-base:latest' 56 | - name: workflowWorkerImageSourceClone 57 | value: 'gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.12.1' 58 | - name: workflowWorkerImageUnitTest 59 | value: 'quay.io/ploigos/ploigos-tool-maven:latest' 60 | - name: workflowWorkerImagePackage 61 | value: 'quay.io/ploigos/ploigos-tool-maven:latest' 62 | - name: workflowWorkerImageStaticCodeAnalysis 63 | value: 'quay.io/ploigos/ploigos-tool-sonar:latest' 64 | - name: workflowWorkerImagePushArtifacts 65 | value: 'quay.io/ploigos/ploigos-tool-maven:latest' 66 | - name: workflowWorkerImageContainerOperations 67 | value: 'quay.io/ploigos/ploigos-tool-containers:latest' 68 | - name: workflowWorkerImageContainerImageStaticComplianceScan 69 | value: 'quay.io/ploigos/ploigos-tool-openscap:latest' 70 | - name: workflowWorkerImageContainerImageStaticVulnerabilityScan 71 | value: 'quay.io/ploigos/ploigos-tool-openscap:latest' 72 | - name: workflowWorkerImageDeploy 73 | value: 'quay.io/ploigos/ploigos-tool-argocd:latest' 74 | - name: workflowWorkerImageValidateEnvironmentConfiguraiton 75 | value: 'quay.io/ploigos/ploigos-tool-config-lint:latest' 76 | - name: workflowWorkerImageUAT 77 | value: 'quay.io/ploigos/ploigos-tool-maven:latest' 78 | pipelineRef: 79 | name: {{ app_name }}-{{ service_name }} 80 | serviceAccountName: ploigos-workflow-{{ app_name }}-{{ service_name }} 81 | timeout: 1h0m0s 82 | workspaces: 83 | - name: home 84 | volumeClaimTemplate: 85 | spec: 86 | accessModes: 87 | - ReadWriteOnce 88 | resources: 89 | requests: 90 | storage: 20Gi 91 | - name: app 92 | volumeClaimTemplate: 93 | spec: 94 | accessModes: 95 | - ReadWriteOnce 96 | resources: 97 | requests: 98 | storage: 1Gi 99 | - name: ploigos-platform-config 100 | configMap: 101 | name: ploigos-platform-config 102 | - name: ploigos-platform-config-secrets 103 | secret: 104 | secretName: ploigos-platform-config-secrets 105 | -------------------------------------------------------------------------------- /roles/ploigos-pipeline/templates/ploigos-pipeline-values.yaml.j2: -------------------------------------------------------------------------------- 1 | global: 2 | verbose: false 3 | serviceName: {{ service_name }} 4 | applicationName: {{ app_name }} 5 | eventListenerCreateRoute: true 6 | eventListenerCreateIngress: false 7 | eventListenerRouteHostOverride: el-{{ service_name }}-{{ app_name }}.apps.{{ full_cluster_name }} 8 | cleanupPipelineRunsToKeep: 10 9 | cleanupPipelineRunsSchedule: "*/15 * * * *" 10 | cleanupPipelineSuccessfulJobHistoryLimit: 5 11 | cleanupPipelineFailedJobHistoryLimit: 1 12 | cleanupPipelineStartingDeadlineSeconds: 600 13 | cleanupPipelineCronJobImage: quay.io/ploigos/ploigos-base:latest 14 | stepRunnerPackageName: ploigos-step-runner 15 | stepRunnerUpdateLibrary: false 16 | stepRunnerLibIndexUrl: https://pypi.org/simple/ 17 | stepRunnerLibExtraIndexUrl: https://pypi.org/simple/ 18 | envNameDev: DEV 19 | envNameTest: TEST 20 | envNameProd: PROD 21 | ciOnlyGitRefPatterns: "^$" 22 | devGitRefPatterns: "^feature/.+$|^PR-.+$" 23 | releaseGitRefPatterns: "^main$" 24 | pgpKeysSecretNameOverride: ploigos-gpg-key 25 | pgpKeys: {} 26 | stepRunnerConfigDir: "/opt/platform-config/ /opt/platform-config-secrets/ cicd/ploigos-software-factory-operator/ploigos-step-runner-config/" 27 | workflowWorkerHomeDirPVCRequestSize: 20Gi 28 | workflowWorkerAppDirPVCRequestSize: 1Gi 29 | workflowWorkerRunnerRoleName: ploigos-container-builder 30 | workflowWorkerImageDefault: quay.io/ploigos/ploigos-base:latest 31 | workflowWorkerImageSourceClone: 'gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.12.1' 32 | workflowWorkerImageUnitTest: quay.io/ploigos/ploigos-tool-maven:latest 33 | workflowWorkerImagePackage: quay.io/ploigos/ploigos-tool-maven:latest 34 | workflowWorkerImageStaticCodeAnalysis: quay.io/ploigos/ploigos-tool-sonar:latest 35 | workflowWorkerImagePushArtifacts: quay.io/ploigos/ploigos-tool-maven:latest 36 | workflowWorkerImageContainerOperations: quay.io/ploigos/ploigos-tool-containers:latest 37 | workflowWorkerImageContainerImageStaticComplianceScan: quay.io/ploigos/ploigos-tool-openscap:latest 38 | workflowWorkerImageContainerImageStaticVulnerabilityScan: quay.io/ploigos/ploigos-tool-openscap:latest 39 | workflowWorkerImageDeploy: quay.io/ploigos/ploigos-tool-argocd:latest 40 | workflowWorkerImageValidateEnvironmentConfiguraiton: quay.io/ploigos/ploigos-tool-config-lint:latest 41 | workflowWorkerImageUAT: quay.io/ploigos/ploigos-tool-maven:latest 42 | tektonGitSSHKeys: {} 43 | separatePlatformConfig: true 44 | 45 | ploigos-workflow-tekton-shared-resources: 46 | nameOverride: ploigos-workflow-standard-tekton-pipeline 47 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | subdomain: '{{ full_cluster_name }}' 4 | 5 | redhat_gov_catalog_tag: "latest" 6 | 7 | jenkins_repository_name: openshift-release-dev 8 | 9 | platform_tls_verify: "{{ ploigos_platform.tls.verify | default(true) }}" 10 | platform_trust_bundle_config_map: "{{ ploigos_platform.tls.trust_bundle_config_map | default }}" 11 | 12 | all_services: "{{ ploigos_platform.services.values() | list | json_query('[].keys(@)[]') }}" 13 | 14 | transformed_services: |- 15 | {% for service in ploigos_platform.services.values()|list %} 16 | {% for service_name in service.keys()|list %} 17 | - name: "{{ service_name }}" 18 | values: {{ service[service_name]|to_json }} 19 | {% endfor %} 20 | {% endfor %} 21 | 22 | enabled_services: '{{ transformed_services|from_yaml|json_query("[?values.enabled].name") }}' 23 | 24 | external_services: '{{ transformed_services|from_yaml|json_query("[?values.external_properties].name") | intersect(enabled_services) }}' 25 | 26 | managed_services: '{{ enabled_services | difference(external_services) }}' 27 | 28 | target_namespaces: ['{{ ploigos_namespace }}'] 29 | 30 | ## GitLab Variables 31 | gitlab_chart_version: "5.9.1" 32 | gitlab_url: "https://gitlab.apps{{ full_cluster_name }}" 33 | gitlab_ci_trustca_secret: "{{ ploigos_platform.services.continuous_integration.gitlab_ci.ca_secret | default(None) }}" 34 | cert_manager: "{{ ploigos_platform.services.source_control.gitlab.install_cert_manager }}" 35 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/files/gitlab-system.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: gitlab-system 5 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/files/gitlab.yml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: gitlab-operator 5 | namespace: gitlab-system 6 | spec: 7 | channel: stable 8 | installPlanApproval: Automatic 9 | name: gitlab-operator-kubernetes 10 | source: community-operators 11 | sourceNamespace: openshift-marketplace 12 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/files/gitlab_ci_role.yml: -------------------------------------------------------------------------------- 1 | allowHostPorts: false 2 | priority: null 3 | requiredDropCapabilities: 4 | - KILL 5 | - MKNOD 6 | allowPrivilegedContainer: false 7 | runAsUser: 8 | type: MustRunAsNonRoot 9 | users: [] 10 | allowHostDirVolumePlugin: false 11 | allowHostIPC: false 12 | seLinuxContext: 13 | type: MustRunAs 14 | readOnlyRootFilesystem: false 15 | metadata: 16 | name: ploigos-workflow-runner-gitlab 17 | fsGroup: 18 | type: RunAsAny 19 | groups: 20 | - 'system:authenticated' 21 | kind: SecurityContextConstraints 22 | defaultAddCapabilities: null 23 | supplementalGroups: 24 | type: RunAsAny 25 | volumes: 26 | - configMap 27 | - downwardAPI 28 | - emptyDir 29 | - persistentVolumeClaim 30 | - projected 31 | - secret 32 | allowHostPID: false 33 | allowHostNetwork: false 34 | allowPrivilegeEscalation: true 35 | apiVersion: security.openshift.io/v1 36 | allowedCapabilities: 37 | - SETUID 38 | - SETGID 39 | --- 40 | kind: ClusterRole 41 | apiVersion: rbac.authorization.k8s.io/v1 42 | metadata: 43 | name: ploigos-workflow-runner-gitlab 44 | rules: 45 | - verbs: 46 | - use 47 | apiGroups: 48 | - security.openshift.io 49 | resources: 50 | - securitycontextconstraints 51 | resourceNames: 52 | - ploigos-workflow-runner-gitlab 53 | - verbs: 54 | - create 55 | - update 56 | - patch 57 | - get 58 | - delete 59 | apiGroups: 60 | - '' 61 | resources: 62 | - secrets 63 | - configmaps 64 | - pods 65 | - pods/attach 66 | - pods/exec 67 | - pods/portforward 68 | - pods/proxy 69 | - verbs: 70 | - get 71 | apiGroups: 72 | - '' 73 | resources: 74 | - pvc 75 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/tasks/cleanup-ploigos-platform.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Clean up resources 3 | shell: | 4 | oc delete -n {{ ploigos_namespace }} KeycloakUser,KeycloakClient,KeycloakRealm --all || true 5 | oc delete -n {{ ploigos_namespace }} all,configmap,pvc,serviceaccount,rolebinding -l app=jenkins-persistent || true 6 | oc delete -n {{ ploigos_namespace }} secret gitea-access-token || true 7 | oc delete -n {{ ploigos_namespace }} secret ploigos-platform-config-secrets || true 8 | oc delete -n {{ ploigos_namespace }} cm ploigos-platform-config-mvn || true 9 | oc delete -n {{ ploigos_namespace }} cm ploigos-platform-config-npm || true 10 | 11 | 12 | - name: Remove CustomResources 13 | k8s: 14 | definition: '{{ lookup("template", "custom-resources/" + operator_app + ".yml.j2") }}' 15 | state: absent 16 | loop: '{{ managed_services }}' 17 | loop_control: 18 | loop_var: operator_app 19 | label: operator_app 20 | when: lookup("template", "custom-resources/" + operator_app + ".yml.j2", errors='ignore') 21 | 22 | ## GitLab specifc task 23 | - name: Tasks for GitLab removal 24 | block: 25 | - name: Remove GitLab Operator 26 | k8s: 27 | src: "{{ role_path }}/files/gitlab.yml" 28 | state: absent 29 | 30 | - name: Delete Gitlab Custom Resources 31 | k8s: 32 | definition: '{{ lookup("template", "gitlab-resources/" + operator_app + ".yml.j2") }}' 33 | state: absent 34 | loop: 35 | - gitlab_ci 36 | - gitlab_ci_configmap 37 | - gitlab_ci_serviceaccount 38 | - gitlab_ci_binding 39 | - gitlab_ci_pvc 40 | - gitlab_route 41 | loop_control: 42 | loop_var: operator_app 43 | label: operator_app 44 | when: lookup("template", "gitlab-resources/" + operator_app + ".yml.j2", errors='ignore') 45 | 46 | - name: Remove GitLab CI Role 47 | k8s: 48 | src: "{{ role_path }}/files/gitlab_ci_role.yml" 49 | state: absent 50 | 51 | - name: Delete Cert Manager Custom Resource 52 | k8s: 53 | definition: '{{ lookup("template", "gitlab-resources/cert_manager.yml.j2") | from_yaml }}' 54 | state: absent 55 | when: cert_manager 56 | 57 | when: '"gitlab" in managed_services' 58 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/tasks/deploy_services.yml: -------------------------------------------------------------------------------- 1 | - name: Create ploigos-container-builder SCC and role 2 | k8s: 3 | definition: "{{ lookup('template', 'container-builder.yml.j2') | from_yaml }}" 4 | 5 | - name: Complete Jenkins tasks, if enabled 6 | block: 7 | - name: Create a Jenkins instance 8 | k8s: 9 | namespace: '{{ ploigos_namespace }}' 10 | definition: '{{ lookup("template", "jenkins.yml.j2")|from_yaml }}' 11 | state: '{{ cr_state }}' 12 | 13 | - name: Create RoleBinding for use by jenkins 14 | k8s: 15 | definition: "{{ lookup('template', 'jenkins-rolebinding.yml.j2') | from_yaml }}" 16 | when: "'jenkins' in managed_services" 17 | 18 | ## GitLab specifc tasks 19 | - name: Complete GitLab tasks, if enabled 20 | block: 21 | - name: Create GitLab system namespace 22 | k8s: 23 | definition: '{{ lookup("file", "gitlab-system.yml") | from_yaml }}' 24 | 25 | - name: Create Cert Manager Subscription 26 | k8s: 27 | definition: '{{ lookup("template", "subscriptions/cert_manager.yml.j2") | from_yaml }}' 28 | when: cert_manager 29 | 30 | - name: Deploy GitLab Operator 31 | k8s: 32 | src: "{{ role_path }}/files/gitlab.yml" 33 | register: deployment 34 | until: not deployment.failed 35 | retries: 5 36 | delay: 10 37 | 38 | - name: Add GitLab to target namespaces 39 | set_fact: 40 | target_namespaces: "{{ target_namespaces + [item] }}" 41 | with_items: 42 | - gitlab-system 43 | 44 | - name: Apply GitLab Routes 45 | k8s: 46 | definition: '{{ lookup("template", "gitlab-resources/" + route + ".yml.j2") }}' 47 | loop: 48 | - gitlab_route 49 | loop_control: 50 | loop_var: route 51 | label: route 52 | when: lookup("template", "gitlab-resources/" + route + ".yml.j2", errors='ignore') 53 | when: '"gitlab" in managed_services' 54 | ## End of GitLab specfic tasks 55 | 56 | # Operator-backed services 57 | - name: Install Operator Group 58 | include_tasks: install_operatorgroup.yml 59 | loop: '{{ target_namespaces }}' 60 | 61 | - name: 'Create Subscriptions' 62 | k8s: 63 | definition: '{{ lookup("template", "subscriptions/" + operator_app + ".yml.j2") }}' 64 | loop: '{{ managed_services }}' 65 | loop_control: 66 | loop_var: operator_app 67 | label: operator_app 68 | when: lookup("template", "subscriptions/" + operator_app + ".yml.j2", errors='ignore') 69 | 70 | - name: 'Apply CustomResources' 71 | k8s: 72 | definition: '{{ lookup("template", "custom-resources/" + operator_app + ".yml.j2") }}' 73 | register: deployment 74 | until: not deployment.failed 75 | retries: 5 76 | delay: 10 77 | loop: '{{ managed_services }}' 78 | loop_control: 79 | loop_var: operator_app 80 | label: operator_app 81 | when: lookup("template", "custom-resources/" + operator_app + ".yml.j2", errors='ignore') 82 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/tasks/generate_gpg_key.yml: -------------------------------------------------------------------------------- 1 | - name: Fetch ploigos-gpg-key secret 2 | set_fact: 3 | secret_data: "{{ lookup('k8s', kind='Secret', namespace=ploigos_namespace, resource_name='ploigos-gpg-key') }}" 4 | 5 | - name: Set ploigos-service-account credential facts from secret 6 | set_fact: 7 | gpg_public_key: "{{ secret_data.data.gpg_public_key | b64decode }}" 8 | gpg_private_key: "{{ secret_data.data.gpg_private_key | b64decode }}" 9 | when: 10 | - secret_data != [] 11 | 12 | - name: Generate key and create secret if missing 13 | block: 14 | - name: Generate PGP key 15 | shell: | 16 | gpg --generate-key --batch <- 4 | {{ ploigos_platform.services.values() | list | 5 | json_query('[?(' + ( all_services | join('||') ) + ').enabled 6 | && *.external_properties].{key:keys(@)[0], value:values(@)[0].external_properties}') | items2dict }} 7 | 8 | - name: Initialize managed RHSSO details 9 | when: '"rhsso" in managed_services' 10 | set_fact: 11 | all_services_details: "{{ all_services_details | combine(_rhsso_details) }}" 12 | vars: 13 | _rhsso_details: 14 | rhsso: {} 15 | 16 | - name: Initialize managed Gitea details 17 | when: '"gitea" in managed_services' 18 | block: 19 | - k8s_info: 20 | kind: Secret 21 | api_version: v1 22 | namespace: '{{ ploigos_namespace }}' 23 | name: gitea-admin-credentials 24 | register: gitea_secret_result 25 | until: gitea_secret_result.resources 26 | retries: 30 27 | delay: 10 28 | 29 | - name: Set Gitea service details 30 | set_fact: 31 | all_services_details: "{{ all_services_details | combine(_gitea_details) }}" 32 | vars: 33 | _gitea_details: 34 | gitea: 35 | url: "http://gitea:3000" 36 | username: "{{ gitea_secret_result.resources[0].data.username | b64decode }}" 37 | password: "{{ gitea_secret_result.resources[0].data.password | b64decode }}" 38 | 39 | - name: Initialize managed GitLab details 40 | when: '"gitlab" in managed_services' 41 | block: 42 | - name: Create GitLab token 43 | set_fact: 44 | gitlab_root_token: "{{ lookup('password', '/dev/null length=15 chars=ascii_letters') }}" 45 | 46 | - name: Set api creation command 47 | set_fact: 48 | gitlab_console_command: >- 49 | "token = User.find_by_username('root').personal_access_tokens.create(scopes: [:api, :read_api, :sudo, :read_user],name: 'Automation token'); 50 | token.set_token('{{ gitlab_root_token }}'); token.save!" 51 | 52 | - name: Create access token for GitLab root user 53 | shell: | 54 | oc='{{ oc_cli }}' 55 | set -eo pipefail 56 | pod=$($oc get pods -n gitlab-system -l app=toolbox -o jsonpath='{.items[0].metadata.name}') 57 | output=$($oc exec $pod -n gitlab-system -c toolbox -- gitlab-rails runner {{ gitlab_console_command }} 2>&1) 58 | if [ ! -z "$output" ]; then 59 | if echo "$output" | grep -q 'already exist'; then 60 | echo ok 61 | else 62 | echo failed 63 | fi 64 | else 65 | echo changed 66 | fi 67 | echo $output 68 | register: gitlab_token 69 | args: 70 | executable: /bin/bash 71 | failed_when: '"failed" in gitlab_token.stdout_lines' 72 | changed_when: '"changed" in gitlab_token.stdout_lines' 73 | 74 | - name: Set GitLab service details 75 | set_fact: 76 | all_services_details: "{{ all_services_details | combine(_gitlab_details) }}" 77 | vars: 78 | _gitlab_details: 79 | gitlab: 80 | url: "https://gitlab.apps.{{ full_cluster_name }}" 81 | api-token: "{{ gitlab_root_token }}" 82 | 83 | - name: Initialize managed Jenkins details 84 | when: '"jenkins" in managed_services' 85 | set_fact: 86 | all_services_details: "{{ all_services_details | combine(_jenkins_details) }}" 87 | vars: 88 | _jenkins_details: 89 | jenkins: 90 | url: 'https://jenkins-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}' 91 | token: "{{ lookup('file', '/var/run/secrets/kubernetes.io/serviceaccount/token') }}" 92 | 93 | - name: Initialize managed Tekton details 94 | when: '"tekton" in managed_services' 95 | set_fact: 96 | all_services_details: "{{ all_services_details | combine(_tekton_details) }}" 97 | vars: 98 | _tekton_details: 99 | tekton: {} 100 | 101 | - name: Initialize managed Nexus details (Artifact Repository) 102 | when: '"nexus_artifacts" in managed_services' 103 | set_fact: 104 | all_services_details: "{{ all_services_details | combine(_nexus_artifacts_details) }}" 105 | vars: 106 | _nexus_artifacts_details: 107 | nexus_artifacts: 108 | url: "http://nexus-sonatype-nexus-service.{{ ploigos_namespace }}.svc.cluster.local:8081" 109 | username: admin 110 | password: admin123 111 | 112 | - name: Initialize managed Nexus details (Container Registry) 113 | when: '"nexus_containers" in managed_services' 114 | set_fact: 115 | all_services_details: "{{ all_services_details | combine(_nexus_containers_details) }}" 116 | vars: 117 | _nexus_containers_details: 118 | nexus_containers: 119 | server_url: "http://nexus-sonatype-nexus-service.{{ ploigos_namespace }}.svc.cluster.local:8081" 120 | docker_url: "nexus-docker-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}" 121 | username: admin 122 | password: admin123 123 | 124 | - name: Initialize managed Sonarqube details 125 | when: '"sonarqube" in managed_services' 126 | set_fact: 127 | all_services_details: "{{ all_services_details | combine(_sonarqube_details) }}" 128 | vars: 129 | _sonarqube_details: 130 | sonarqube: 131 | url: "http://sonarqube:9000" 132 | username: admin 133 | password: admin 134 | 135 | - name: Initialize managed Quay details 136 | when: '"quay" in managed_services' 137 | set_fact: 138 | all_services_details: "{{ all_services_details | combine(_quay_details) }}" 139 | vars: 140 | _quay_details: 141 | quay: 142 | url: "quay-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}" 143 | username: quay 144 | password: password 145 | 146 | - name: Initialize managed ArgoCD details 147 | when: '"argocd" in managed_services' 148 | set_fact: 149 | all_services_details: "{{ all_services_details | combine(_argocd_details) }}" 150 | vars: 151 | _argocd_details: 152 | argocd: 153 | kubernetes_api: https://kubernetes.default.svc.cluster.local:443 154 | kubernetes_token: "{{ lookup('file', '/var/run/secrets/kubernetes.io/serviceaccount/token') }}" 155 | kubernetes_cr_name: argocd 156 | kubernetes_namespace: "{{ ploigos_namespace }}" 157 | 158 | - name: Initialize managed Selenium Grid details 159 | when: '"selenium" in managed_services' 160 | set_fact: 161 | all_services_details: "{{ all_services_details | combine(_selenium_details) }}" 162 | vars: 163 | _selenium_details: 164 | selenium: 165 | url: "http://selenium-grid.{{ ploigos_namespace }}.svc.cluster.local:4444" 166 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/catalog-source.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: operators.coreos.com/v1alpha1 3 | kind: CatalogSource 4 | metadata: 5 | name: redhatgov-operators 6 | namespace: openshift-marketplace 7 | spec: 8 | sourceType: grpc 9 | image: quay.io/redhatgov/operator-catalog:{{ redhat_gov_catalog_tag }} 10 | displayName: Red Hat NAPS Community Operators 11 | publisher: RedHatGov 12 | icon: 13 | base64data: "" 14 | mediatype: "" 15 | updateStrategy: 16 | registryPoll: 17 | interval: 30m 18 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/container-builder.yml.j2: -------------------------------------------------------------------------------- 1 | - kind: SecurityContextConstraints 2 | apiVersion: security.openshift.io/v1 3 | metadata: 4 | annotations: 5 | kubernetes.io/description: Jenkins Workers SCC 6 | name: ploigos-container-builder 7 | allowedCapabilities: 8 | - SETUID 9 | - SETGID 10 | seLinuxContext: 11 | type: MustRunAs 12 | runAsUser: 13 | type: MustRunAsRange 14 | - apiVersion: rbac.authorization.k8s.io/v1 15 | kind: Role 16 | metadata: 17 | name: ploigos-container-builder 18 | namespace: {{ ploigos_namespace }} 19 | rules: 20 | - apiGroups: 21 | - security.openshift.io 22 | resourceNames: 23 | - ploigos-container-builder 24 | resources: 25 | - securitycontextconstraints 26 | verbs: 27 | - use 28 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/custom-resources/argocd.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ArgoCD 3 | metadata: 4 | name: argocd 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | server: 8 | route: 9 | enabled: true 10 | tls: 11 | termination: reencrypt 12 | insecureEdgeTerminationPolicy: Redirect 13 | rbac: 14 | defaultPolicy: 'role:readonly' 15 | policy: | 16 | g, argocd-admins, role:admin 17 | p, {{ ploigos_service_account.username | default('ploigos') }}, *, *, *, allow 18 | scopes: '[groups]' 19 | repo: 20 | image: argoproj/argocd 21 | version: v2.1.3 22 | image: argoproj/argocd 23 | version: v2.1.3 24 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/custom-resources/gitea.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: redhatgov.io/v1alpha1 2 | kind: Gitea 3 | metadata: 4 | name: gitea 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | gitea: 8 | expose: 9 | kind: Route 10 | ssl: true 11 | uri: gitea-{{ ploigos_namespace }}.apps.{{ full_cluster_name }} 12 | volumeSize: 4Gi 13 | resources: 14 | memory: 15 | limit: 2Gi 16 | persistent: true 17 | postgresql: 18 | volumeSize: 10Gi 19 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/custom-resources/gitlab.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: apps.gitlab.com/v1beta1 2 | kind: GitLab 3 | metadata: 4 | name: gitlab 5 | namespace: gitlab-system 6 | spec: 7 | chart: 8 | version: {{ gitlab_chart_version }} 9 | values: 10 | global: 11 | hosts: 12 | domain: apps.{{ full_cluster_name }} 13 | ingress: 14 | enabled: false 15 | class: openshift-default 16 | nginx-ingress: 17 | enabled: false 18 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/custom-resources/nexus_artifacts.yml.j2: -------------------------------------------------------------------------------- 1 | nexus_containers.yml.j2 -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/custom-resources/nexus_containers.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: sonatype.com/v1alpha1 2 | kind: NexusRepo 3 | metadata: 4 | name: nexus 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | config: 8 | enabled: false 9 | mountPath: /sonatype-nexus-conf 10 | deployment: 11 | terminationGracePeriodSeconds: 120 12 | route: 13 | enabled: false 14 | secret: 15 | enabled: false 16 | mountPath: /etc/secret-volume 17 | readOnly: true 18 | ingress: 19 | annotations: {} 20 | enabled: false 21 | path: / 22 | tls: 23 | enabled: true 24 | secretName: nexus-tls 25 | service: 26 | annotations: {} 27 | enabled: true 28 | labels: {} 29 | name: nexus-docker 30 | ports: 31 | - name: nexus-docker 32 | port: 8082 33 | targetPort: 8082 34 | statefulset: 35 | enabled: false 36 | replicaCount: 1 37 | deploymentStrategy: {} 38 | nexusProxyRoute: 39 | enabled: false 40 | tolerations: [] 41 | persistence: 42 | accessMode: ReadWriteOnce 43 | enabled: true 44 | storageSize: 50Gi 45 | nexus: 46 | nexusPort: 8081 47 | dockerPort: 8082 48 | resources: {} 49 | imageName: >- 50 | registry.connect.redhat.com/sonatype/nexus-repository-manager@sha256:9dafcabb682b0f361a7368dcce6d1589a7229c1558fc884fbd2f45113b7db18e 51 | readinessProbe: 52 | failureThreshold: 6 53 | initialDelaySeconds: 30 54 | path: / 55 | periodSeconds: 30 56 | livenessProbe: 57 | failureThreshold: 6 58 | initialDelaySeconds: 30 59 | path: / 60 | periodSeconds: 30 61 | env: 62 | - name: install4jAddVmParams 63 | value: >- 64 | -Xms1200M -Xmx1200M -XX:MaxDirectMemorySize=2G 65 | -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap 66 | - name: NEXUS_SECURITY_RANDOMPASSWORD 67 | value: 'false' 68 | securityContext: {} 69 | imagePullSecret: '' 70 | imagePullPolicy: IfNotPresent 71 | service: 72 | type: NodePort 73 | hostAliases: [] 74 | podAnnotations: {} 75 | --- 76 | kind: Route 77 | apiVersion: route.openshift.io/v1 78 | metadata: 79 | name: nexus-docker 80 | namespace: {{ ploigos_namespace }} 81 | spec: 82 | to: 83 | kind: Service 84 | name: nexus-docker 85 | weight: 100 86 | port: 87 | targetPort: nexus-docker 88 | tls: 89 | termination: edge 90 | wildcardPolicy: None 91 | --- 92 | kind: Route 93 | apiVersion: route.openshift.io/v1 94 | metadata: 95 | name: nexus 96 | namespace: {{ ploigos_namespace }} 97 | spec: 98 | to: 99 | kind: Service 100 | name: nexus-sonatype-nexus-service 101 | weight: 100 102 | port: 103 | targetPort: application 104 | tls: 105 | termination: edge 106 | wildcardPolicy: None 107 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/custom-resources/quay.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: redhatcop.redhat.io/v1alpha1 2 | kind: QuayEcosystem 3 | metadata: 4 | name: quayecosystem 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | clair: 8 | enabled: false 9 | quay: 10 | externalAccess: 11 | hostname: quay-{{ ploigos_namespace }}.apps.{{ full_cluster_name }} 12 | tls: 13 | termination: edge 14 | imagePullSecretName: dummy-pull-secret 15 | database: 16 | image: registry.redhat.io/rhscl/postgresql-96-rhel7 17 | volumeSize: 10Gi 18 | registryStorage: 19 | persistentVolumeSize: 10Gi 20 | persistentVolumeAccessModes: 21 | - ReadWriteOnce 22 | securityContext: 23 | fsGroup: 1001 24 | redis: 25 | image: 'registry.redhat.io/rhscl/redis-32-rhel7:latest' 26 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/custom-resources/rhsso.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: keycloak.org/v1alpha1 2 | kind: Keycloak 3 | metadata: 4 | name: rhsso 5 | namespace: {{ ploigos_namespace }} 6 | labels: 7 | app: rhsso 8 | spec: 9 | instances: 1 10 | externalAccess: 11 | enabled: True 12 | externalDatabase: 13 | enabled: False 14 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/custom-resources/selenium.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: redhatgov.io/v1alpha1 2 | kind: SeleniumGrid 3 | metadata: 4 | name: selenium-grid 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | expose: 8 | kind: None 9 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/custom-resources/sonarqube.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: redhatgov.io/v1alpha1 2 | kind: Sonarqube 3 | metadata: 4 | name: sonarqube 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | persistent: true 8 | postgresql: 9 | volumeSize: 4Gi 10 | sonarqube: 11 | expose: 12 | kind: Route 13 | ssl: true 14 | uri: sonarqube-{{ ploigos_namespace }}.apps.{{ full_cluster_name }} 15 | volumeSize: 4Gi 16 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/gitlab-resources/gitlab_ci.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: apps.gitlab.com/v1beta2 2 | kind: Runner 3 | metadata: 4 | name: gitlab-runner 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | gitlabUrl: {{ gitlab_url }} 8 | config: gitlab-runner-toml 9 | buildImage: {{ ploigos_platform.services.continuous_integration.gitlab_ci.build_image }} 10 | serviceaccount: gitlab-runner-sa 11 | token: {{ ploigos_platform.services.continuous_integration.gitlab_ci.token_secret }} 12 | tags: ploigos 13 | ca: {{ gitlab_ci_trustca_secret }} -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/gitlab-resources/gitlab_ci_binding.yml.j2: -------------------------------------------------------------------------------- 1 | kind: RoleBinding 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | name: gitlab-runner-binding 5 | namespace: {{ ploigos_namespace }} 6 | subjects: 7 | - kind: ServiceAccount 8 | name: gitlab-runner-sa 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: ClusterRole 12 | name: ploigos-workflow-runner-gitlab -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/gitlab-resources/gitlab_ci_configmap.yml.j2: -------------------------------------------------------------------------------- 1 | kind: ConfigMap 2 | apiVersion: v1 3 | metadata: 4 | name: gitlab-runner-toml 5 | namespace: {{ ploigos_namespace }} 6 | data: 7 | config.toml: > 8 | ## TODO: The following file is copied from EXAMPLES 9 | (https://docs.gitlab.com/runner/executors/kubernetes.html) 10 | 11 | ## TODO: Need to modify to be Ploigos-specific, and load into the GitLab 12 | Runner pod on OpenShift 13 | 14 | 15 | ## TODO: Cannot have concurrency right now, given the hard-coded volume 16 | names!! 17 | 18 | concurrent = 1 19 | 20 | 21 | [[runners]] 22 | executor = "kubernetes" 23 | 24 | [runners.custom_build_dir] 25 | enabled = true 26 | 27 | [runners.kubernetes] 28 | pull_policy = "if-not-present" 29 | 30 | # Optional. Kubernetes host URL. If not specified, the runner attempts to auto-discovery it. 31 | #host = "https://:" 32 | 33 | # Optional. Kubernetes auth certificate. 34 | #cert_file = "/etc/ssl/kubernetes/api.crt" 35 | 36 | # Optional. Kubernetes auth private key. 37 | #key_file = "/etc/ssl/kubernetes/api.key" 38 | 39 | # Optional. Kubernetes auth ca certificate. 40 | #ca_file = "/etc/ssl/kubernetes/ca.crt" 41 | 42 | # Namespace to run Kubernetes jobs in. 43 | namespace = "gitlab-runner" 44 | 45 | #namespace_overwrite_allowed = "ci-.*" 46 | 47 | #bearer_token_overwrite_allowed = true 48 | 49 | # Run all containers with the privileged flag enabled. 50 | #privileged = true 51 | 52 | # Default Docker image to use for jobs when none is specified. 53 | image = "ploigos/ploigos-base:latest" 54 | 55 | # Optional. Runs all containers with the allowPrivilegeEscalation flag enabled. 56 | #allow_privilege_escalation = true 57 | 58 | # A list of secrets that are used to authenticate Docker image pulling. 59 | #image_pull_secrets = ["docker-registry-credentials"] 60 | 61 | [runners.kubernetes.pod_security_context] 62 | run_as_non_root = true 63 | run_as_user = 1001 64 | fs_group = 0 65 | 66 | #[runners.kubernetes.node_selector] 67 | # gitlab = "true" 68 | 69 | #cpu_limit = "1" 70 | #memory_limit = "1Gi" 71 | #service_cpu_limit = "1" 72 | #service_memory_limit = "1Gi" 73 | #helper_cpu_limit = "500m" 74 | #helper_memory_limit = "100Mi" 75 | poll_interval = 5 76 | poll_timeout = 3600 77 | #dns_policy = "cluster-first" 78 | 79 | ## Node Scheduling ## 80 | 81 | #[runners.kubernetes.node_selector] 82 | # gitlab = "true" 83 | 84 | #[runners.kubernetes.node_tolerations] 85 | # "node-role.kubernetes.io/master" = "NoSchedule" 86 | # "custom.toleration=value" = "NoSchedule" 87 | # "empty.value=" = "PreferNoSchedule" 88 | # "onlyKey" = "" 89 | 90 | ## Volumes ## 91 | 92 | [[runners.kubernetes.volumes.pvc]] 93 | name = "gitlab-ci-pvc" 94 | mount_path = "/home/ploigos" 95 | 96 | [[runners.kubernetes.volumes.secret]] 97 | name = "ploigos-gpg-key" 98 | mount_path = "/var/pgp-private-keys" 99 | 100 | [[runners.kubernetes.volumes.config_map]] 101 | name = "ploigos-platform-config" 102 | mount_path = "/opt/platform-config/config.yml" 103 | sub_path = "config.yml" 104 | 105 | [[runners.kubernetes.volumes.secret]] 106 | name = "ploigos-platform-config-secrets" 107 | mount_path = "/opt/platform-config/config-secrets.yml" 108 | sub_path = "config-secrets.yml" 109 | 110 | {% if (platform_trust_bundle_config_map is defined) and (platform_trust_bundle_config_map != '') and (platform_trust_bundle_config_map|length != 0) %} 111 | [[runners.kubernetes.volumes.config_map]] 112 | name = "{{ platform_trust_bundle_config_map }}" 113 | mount_path = "/etc/pki/ca-trust/source/anchors" 114 | read_only = true 115 | [runners.kubernetes.volumes.config_map.items] 116 | "ca-bundle.crt" = "tls-ca-bundle.pem" 117 | {% endif %} 118 | 119 | #[[runners.kubernetes.volumes.pvc]] 120 | # name = "gitlab-ci-build-pvc" 121 | # mount_path = "/builds" 122 | 123 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/gitlab-resources/gitlab_ci_pvc.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | kind: PersistentVolumeClaim 3 | apiVersion: v1 4 | metadata: 5 | name: gitlab-ci-pvc 6 | namespace: {{ ploigos_namespace }} 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 20Gi 13 | volumeMode: Filesystem -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/gitlab-resources/gitlab_ci_serviceaccount.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: gitlab-runner-sa 5 | namespace: {{ ploigos_namespace }} -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/gitlab-resources/gitlab_route.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | name: gitlab 5 | namespace: gitlab-system 6 | spec: 7 | spec: 8 | host: gitlab.apps.{{ full_cluster_name }} 9 | path: / 10 | port: 11 | targetPort: http-workhorse 12 | to: 13 | kind: Service 14 | name: gitlab-webservice-default 15 | weight: 100 16 | tls: 17 | termination: edge 18 | insecureEdgeTerminationPolicy: Redirect 19 | wildcardPolicy: None 20 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/jenkins-rolebinding.yml.j2: -------------------------------------------------------------------------------- 1 | - apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: jenkins-ploigos-container-builder 5 | namespace: {{ ploigos_namespace }} 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: Role 9 | name: ploigos-container-builder 10 | subjects: 11 | - kind: ServiceAccount 12 | name: jenkins 13 | namespace: {{ ploigos_namespace }} 14 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/jenkins.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | items: 3 | - apiVersion: route.openshift.io/v1 4 | kind: Route 5 | metadata: 6 | annotations: 7 | haproxy.router.openshift.io/timeout: 4m 8 | openshift.io/generated-by: OpenShiftNewApp 9 | template.openshift.io/expose-uri: http://{.spec.host}{.spec.path} 10 | creationTimestamp: null 11 | labels: 12 | app: jenkins-persistent 13 | template: jenkins-persistent-template 14 | app.kubernetes.io/part-of: jenkins 15 | name: jenkins 16 | namespace: {{ ploigos_namespace }} 17 | spec: 18 | host: "" 19 | tls: 20 | insecureEdgeTerminationPolicy: Redirect 21 | termination: edge 22 | to: 23 | kind: Service 24 | name: jenkins 25 | weight: null 26 | status: 27 | ingress: null 28 | - apiVersion: v1 29 | kind: PersistentVolumeClaim 30 | metadata: 31 | annotations: 32 | openshift.io/generated-by: OpenShiftNewApp 33 | creationTimestamp: null 34 | labels: 35 | app: jenkins-persistent 36 | template: jenkins-persistent-template 37 | app.kubernetes.io/part-of: jenkins 38 | name: jenkins 39 | namespace: {{ ploigos_namespace }} 40 | spec: 41 | accessModes: 42 | - ReadWriteOnce 43 | resources: 44 | requests: 45 | storage: 20Gi 46 | status: {} 47 | - apiVersion: apps.openshift.io/v1 48 | kind: DeploymentConfig 49 | metadata: 50 | annotations: 51 | openshift.io/generated-by: OpenShiftNewApp 52 | template.alpha.openshift.io/wait-for-ready: "true" 53 | creationTimestamp: null 54 | labels: 55 | app: jenkins-persistent 56 | template: jenkins-persistent-template 57 | app.kubernetes.io/part-of: jenkins 58 | name: jenkins 59 | namespace: {{ ploigos_namespace }} 60 | spec: 61 | replicas: 1 62 | selector: 63 | name: jenkins 64 | strategy: 65 | resources: {} 66 | type: Recreate 67 | template: 68 | metadata: 69 | annotations: 70 | openshift.io/generated-by: OpenShiftNewApp 71 | creationTimestamp: null 72 | labels: 73 | name: jenkins 74 | app.kubernetes.io/part-of: jenkins 75 | spec: 76 | initContainers: 77 | - name: ploigos-plugins 78 | command: 79 | - /bin/bash 80 | imagePullPolicy: Always 81 | volumeMounts: 82 | - name: jenkins-data 83 | mountPath: /var/lib/jenkins 84 | image: >- 85 | quay.io/akrohg/ploigos-jenkins-sidecar:latest 86 | args: 87 | - '-c' 88 | - >- 89 | mkdir -p /var/lib/jenkins/plugins ; cp --remove-destination -v 90 | /ploigos-jenkins-plugins/* /var/lib/jenkins/plugins 91 | containers: 92 | - env: 93 | - name: OPENSHIFT_ENABLE_OAUTH 94 | value: "true" 95 | - name: OPENSHIFT_ENABLE_REDIRECT_PROMPT 96 | value: "true" 97 | - name: DISABLE_ADMINISTRATIVE_MONITORS 98 | value: "false" 99 | - name: KUBERNETES_MASTER 100 | value: https://kubernetes.default:443 101 | - name: KUBERNETES_TRUST_CERTIFICATES 102 | value: "true" 103 | - name: JENKINS_SERVICE_NAME 104 | value: jenkins 105 | - name: JNLP_SERVICE_NAME 106 | value: jenkins-jnlp 107 | - name: ENABLE_FATAL_ERROR_LOG_FILE 108 | value: "false" 109 | - name: JENKINS_UC_INSECURE 110 | value: "false" 111 | image: ' ' 112 | imagePullPolicy: IfNotPresent 113 | livenessProbe: 114 | failureThreshold: 2 115 | httpGet: 116 | path: /login 117 | port: 8080 118 | initialDelaySeconds: 420 119 | periodSeconds: 360 120 | timeoutSeconds: 240 121 | name: jenkins 122 | readinessProbe: 123 | httpGet: 124 | path: /login 125 | port: 8080 126 | initialDelaySeconds: 3 127 | timeoutSeconds: 240 128 | resources: 129 | limits: 130 | cpu: '2' 131 | memory: 4Gi 132 | requests: 133 | cpu: 100m 134 | memory: 512Mi 135 | securityContext: 136 | capabilities: {} 137 | privileged: false 138 | terminationMessagePath: /dev/termination-log 139 | volumeMounts: 140 | - mountPath: /var/lib/jenkins 141 | name: jenkins-data 142 | dnsPolicy: ClusterFirst 143 | restartPolicy: Always 144 | serviceAccountName: jenkins 145 | volumes: 146 | - name: jenkins-data 147 | persistentVolumeClaim: 148 | claimName: jenkins 149 | test: false 150 | triggers: 151 | - imageChangeParams: 152 | automatic: true 153 | containerNames: 154 | - jenkins 155 | from: 156 | kind: ImageStreamTag 157 | name: jenkins:2 158 | namespace: openshift 159 | type: ImageChange 160 | - type: ConfigChange 161 | status: 162 | availableReplicas: 0 163 | latestVersion: 0 164 | observedGeneration: 0 165 | replicas: 0 166 | unavailableReplicas: 0 167 | updatedReplicas: 0 168 | - apiVersion: v1 169 | kind: ServiceAccount 170 | imagePullSecrets: 171 | - name: quay-robot-token 172 | metadata: 173 | annotations: 174 | openshift.io/generated-by: OpenShiftNewApp 175 | serviceaccounts.openshift.io/oauth-redirectreference.jenkins: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"jenkins"}}' 176 | creationTimestamp: null 177 | labels: 178 | app: jenkins-persistent 179 | template: jenkins-persistent-template 180 | app.kubernetes.io/part-of: jenkins 181 | name: jenkins 182 | namespace: {{ ploigos_namespace }} 183 | - apiVersion: authorization.openshift.io/v1 184 | groupNames: null 185 | kind: RoleBinding 186 | metadata: 187 | annotations: 188 | openshift.io/generated-by: OpenShiftNewApp 189 | creationTimestamp: null 190 | labels: 191 | app: jenkins-persistent 192 | template: jenkins-persistent-template 193 | app.kubernetes.io/part-of: jenkins 194 | name: jenkins_edit 195 | namespace: {{ ploigos_namespace }} 196 | roleRef: 197 | name: edit 198 | subjects: 199 | - kind: ServiceAccount 200 | name: jenkins 201 | userNames: null 202 | - apiVersion: v1 203 | kind: Service 204 | metadata: 205 | annotations: 206 | openshift.io/generated-by: OpenShiftNewApp 207 | creationTimestamp: null 208 | labels: 209 | app: jenkins-persistent 210 | template: jenkins-persistent-template 211 | app.kubernetes.io/part-of: jenkins 212 | name: jenkins-jnlp 213 | namespace: {{ ploigos_namespace }} 214 | spec: 215 | ports: 216 | - name: agent 217 | port: 50000 218 | protocol: TCP 219 | targetPort: 50000 220 | selector: 221 | name: jenkins 222 | sessionAffinity: None 223 | type: ClusterIP 224 | status: 225 | loadBalancer: {} 226 | - apiVersion: v1 227 | kind: Service 228 | metadata: 229 | annotations: 230 | openshift.io/generated-by: OpenShiftNewApp 231 | service.alpha.openshift.io/dependencies: '[{"name": "jenkins-jnlp", "namespace": 232 | "", "kind": "Service"}]' 233 | service.openshift.io/infrastructure: "true" 234 | creationTimestamp: null 235 | labels: 236 | app: jenkins-persistent 237 | template: jenkins-persistent-template 238 | app.kubernetes.io/part-of: jenkins 239 | name: jenkins 240 | spec: 241 | ports: 242 | - name: web 243 | port: 80 244 | protocol: TCP 245 | targetPort: 8080 246 | selector: 247 | name: jenkins 248 | sessionAffinity: None 249 | type: ClusterIP 250 | status: 251 | loadBalancer: {} 252 | kind: List 253 | metadata: {} 254 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/operator-group.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1 2 | kind: OperatorGroup 3 | metadata: 4 | name: {{ operator_group_name | default(og_namespace + '-operatorgroup') }} 5 | namespace: {{ og_namespace }} 6 | spec: 7 | targetNamespaces: 8 | - {{ og_namespace }} -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/ploigos-platform-config-mvn.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: ploigos-platform-config-mvn 5 | namespace: {{ ploigos_namespace }} 6 | data: 7 | config.yml: | 8 | step-runner-config: 9 | config-decryptors: 10 | - implementer: SOPS 11 | 12 | global-defaults: 13 | tls-verify: {{ platform_tls_verify }} 14 | maven-servers: 15 | internal-mirror: 16 | id: maven-public 17 | username: {{ ploigos_service_account.username }} 18 | maven-releases: 19 | id: maven-releases 20 | username: {{ ploigos_service_account.username }} 21 | maven-repositories: 22 | maven-releases: 23 | id: maven-releases 24 | url: {{ all_services_details.nexus_artifacts.url }}/repository/maven-releases/ 25 | snapshots: false 26 | releases: true 27 | maven-mirrors: 28 | internal-mirror: 29 | id: maven-public 30 | url: {{ all_services_details.nexus_artifacts.url }}/repository/maven-public/ 31 | mirror-of: '*' 32 | container-registries: 33 | {% if 'quay' in (all_services_details | list) %} 34 | {{ all_services_details.quay.url }}: 35 | {% else %} 36 | {{ all_services_details.nexus_containers.docker_url }}: 37 | {% endif %} 38 | username: "{{ ploigos_service_account.username }}" 39 | registry.redhat.io: 40 | username: "{{ rhio_username }}" 41 | global-environment-defaults: 42 | DEV: 43 | kube-app-domain: dev.apps.{{ full_cluster_name }} 44 | TEST: 45 | kube-app-domain: test.apps.{{ full_cluster_name }} 46 | PROD: 47 | kube-app-domain: apps.{{ full_cluster_name }} 48 | 49 | generate-metadata: 50 | - implementer: Maven 51 | - implementer: Git 52 | - implementer: SemanticVersion 53 | 54 | tag-source: 55 | - implementer: Git 56 | config: 57 | git-username: {{ ploigos_service_account.username }} 58 | 59 | unit-test: 60 | - implementer: MavenTest 61 | 62 | package: 63 | - implementer: MavenPackage 64 | 65 | static-code-analysis: 66 | - implementer: SonarQube 67 | config: 68 | url: {{ all_services_details.sonarqube.url }}/ 69 | username: {{ ploigos_service_account.username }} 70 | push-artifacts: 71 | - implementer: MavenDeploy 72 | config: 73 | maven-push-artifact-repo-id: maven-releases 74 | maven-push-artifact-repo-url: {{ all_services_details.nexus_artifacts.url }}/repository/maven-releases 75 | create-container-image: 76 | - implementer: Buildah 77 | 78 | push-container-image: 79 | - implementer: Skopeo 80 | config: 81 | {% if 'quay' in (all_services_details | list) %} 82 | destination-url: {{ all_services_details.quay.url }} 83 | {% else %} 84 | destination-url: {{ all_services_details.nexus_containers.docker_url }} 85 | {% endif %} 86 | sign-container-image: 87 | - implementer: PodmanSign 88 | config: 89 | container-image-signature-server-url: {{ all_services_details.nexus_artifacts.url }}/repository/container-image-signatures 90 | container-image-signature-server-username: ploigos 91 | 92 | deploy: 93 | - implementer: ArgoCD 94 | config: 95 | argocd-api: argocd-server.{{ ploigos_namespace }}.svc.cluster.local 96 | argocd-username: {{ ploigos_service_account.username }} 97 | argocd-skip-tls: True 98 | git-username: {{ ploigos_service_account.username }} 99 | git-email: 'napsspo+ploigos@redhat.com' 100 | argocd-sync-timeout-seconds: 130 101 | 102 | validate-environment-configuration: 103 | - implementer: ConfiglintFromArgocd 104 | 105 | uat: 106 | - implementer: MavenIntegrationTest 107 | config: 108 | target-host-url-maven-argument-name: 'target.base.url' 109 | maven-additional-arguments: 110 | - -Dselenium.hub.url=http://selenium-grid.{{ ploigos_namespace }}.svc.cluster.local:4444 111 | 112 | report: 113 | - implementer: ResultArtifactsArchive 114 | config: 115 | results-archive-destination-url: {{ all_services_details.nexus_artifacts.url }}/repository/result-artifacts-archive 116 | results-archive-destination-username: ploigos 117 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/ploigos-platform-config-npm.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: ploigos-platform-config-npm 5 | namespace: {{ ploigos_namespace }} 6 | data: 7 | config.yml: | 8 | step-runner-config: 9 | config-decryptors: 10 | - implementer: SOPS 11 | 12 | global-defaults: 13 | tls-verify: {{ platform_tls_verify }} 14 | container-registries: 15 | {% if 'quay' in (all_services_details | list) %} 16 | {{ all_services_details.quay.url }}: 17 | {% else %} 18 | {{ all_services_details.nexus_containers.docker_url }}: 19 | {% endif %} 20 | username: "{{ ploigos_service_account.username }}" 21 | registry.redhat.io: 22 | username: "{{ rhio_username }}" 23 | global-environment-defaults: 24 | DEV: 25 | kube-app-domain: dev.apps.{{ full_cluster_name }} 26 | TEST: 27 | kube-app-domain: test.apps.{{ full_cluster_name }} 28 | PROD: 29 | kube-app-domain: apps.{{ full_cluster_name }} 30 | 31 | generate-metadata: 32 | - implementer: Npm 33 | - implementer: Git 34 | - implementer: SemanticVersion 35 | 36 | tag-source: 37 | - implementer: Git 38 | config: 39 | git-username: {{ ploigos_service_account.username }} 40 | 41 | unit-test: 42 | - implementer: NpmTest 43 | config: 44 | install-first: True 45 | 46 | package: 47 | - implementer: NpmPackage 48 | 49 | static-code-analysis: 50 | - implementer: SonarQube 51 | config: 52 | url: {{ all_services_details.sonarqube.url }}/ 53 | username: {{ ploigos_service_account.username }} 54 | 55 | push-artifacts: 56 | - implementer: NpmPushArtifacts 57 | config: 58 | npm-registry: {{ all_services_details.nexus_artifacts.url }}/repository/npm-internal/ 59 | npm-user: {{ ploigos_service_account.username }} 60 | 61 | create-container-image: 62 | - implementer: Buildah 63 | 64 | push-container-image: 65 | - implementer: Skopeo 66 | config: 67 | {% if 'quay' in (all_services_details | list) %} 68 | destination-url: {{ all_services_details.quay.url }} 69 | {% else %} 70 | destination-url: {{ all_services_details.nexus_containers.docker_url }} 71 | {% endif %} 72 | sign-container-image: 73 | - implementer: PodmanSign 74 | config: 75 | container-image-signature-server-url: {{ all_services_details.nexus_artifacts.url }}/repository/container-image-signatures 76 | container-image-signature-server-username: ploigos 77 | 78 | deploy: 79 | - implementer: ArgoCD 80 | config: 81 | argocd-api: argocd-server.{{ ploigos_namespace }}.svc.cluster.local 82 | argocd-username: {{ ploigos_service_account.username }} 83 | argocd-skip-tls: True 84 | git-username: {{ ploigos_service_account.username }} 85 | git-email: 'napsspo+ploigos@redhat.com' 86 | argocd-sync-timeout-seconds: 130 87 | 88 | validate-environment-configuration: 89 | - implementer: ConfiglintFromArgocd 90 | 91 | report: 92 | - implementer: ResultArtifactsArchive 93 | config: 94 | results-archive-destination-url: {{ all_services_details.nexus_artifacts.url }}/repository/result-artifacts-archive 95 | results-archive-destination-username: ploigos 96 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/ploigos-platform-config-secrets-mvn.yml.j2: -------------------------------------------------------------------------------- 1 | step-runner-config: 2 | global-defaults: 3 | container-registries: 4 | {% if 'quay' in (all_services_details | list) %} 5 | {{ all_services_details.quay.url }}: 6 | {% else %} 7 | {{ all_services_details.nexus_containers.docker_url }}: 8 | {% endif %} 9 | password: {{ ploigos_service_account.password }} 10 | registry.redhat.io: 11 | password: {{ rhio_token }} 12 | maven-servers: 13 | maven-releases: 14 | password: {{ ploigos_service_account.password }} 15 | internal-mirror: 16 | password: {{ ploigos_service_account.password }} 17 | tag-source: 18 | - implementer: Git 19 | config: 20 | git-password: {{ ploigos_service_account.password }} 21 | static-code-analysis: 22 | - implementer: SonarQube 23 | config: 24 | password: {{ ploigos_service_account.password }} 25 | sign-container-image: 26 | - implementer: PodmanSign 27 | config: 28 | container-image-signer-pgp-private-key: | 29 | {{ (gpg_private_key | default('TODO')) | regex_replace('\n', '\n ') }} 30 | container-image-signature-server-password: {{ ploigos_service_account.password }} 31 | deploy: 32 | - implementer: ArgoCD 33 | config: 34 | git-password: {{ ploigos_service_account.password }} 35 | argocd-password: {{ ploigos_service_account.password }} 36 | report: 37 | - implementer: ResultArtifactsArchive 38 | config: 39 | results-archive-destination-password: {{ ploigos_service_account.password }} 40 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/ploigos-platform-config-secrets-npm.yml.j2: -------------------------------------------------------------------------------- 1 | step-runner-config: 2 | global-defaults: 3 | container-registries: 4 | {% if 'quay' in (all_services_details | list) %} 5 | {{ all_services_details.quay.url }}: 6 | {% else %} 7 | {{ all_services_details.nexus_containers.docker_url }}: 8 | {% endif %} 9 | password: {{ ploigos_service_account.password }} 10 | registry.redhat.io: 11 | password: {{ rhio_token }} 12 | tag-source: 13 | - implementer: Git 14 | config: 15 | git-password: {{ ploigos_service_account.password }} 16 | static-code-analysis: 17 | - implementer: SonarQube 18 | config: 19 | password: {{ ploigos_service_account.password }} 20 | push-artifacts: 21 | - implementer: NpmPushArtifacts 22 | config: 23 | npm-password: {{ ploigos_service_account.password }} 24 | 25 | sign-container-image: 26 | - implementer: PodmanSign 27 | config: 28 | container-image-signer-pgp-private-key: | 29 | {{ (gpg_private_key | default('TODO')) | regex_replace('\n', '\n ') }} 30 | container-image-signature-server-password: {{ ploigos_service_account.password }} 31 | deploy: 32 | - implementer: ArgoCD 33 | config: 34 | git-password: {{ ploigos_service_account.password }} 35 | argocd-password: {{ ploigos_service_account.password }} 36 | report: 37 | - implementer: ResultArtifactsArchive 38 | config: 39 | results-archive-destination-password: {{ ploigos_service_account.password }} 40 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/ploigos-service-account-secret.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ ploigos_service_account_secret }} 5 | namespace: {{ ploigos_namespace }} 6 | data: 7 | username: {{ ploigos_service_account.username | b64encode }} 8 | password: {{ ploigos_service_account.password | b64encode }} 9 | email: {{ ploigos_service_account.email | b64encode }} 10 | first_name: {{ ploigos_service_account.first_name | b64encode }} 11 | last_name: {{ ploigos_service_account.last_name | b64encode }} 12 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/argocd.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: argocd 5 | namespace: openshift-operators 6 | spec: 7 | channel: stable 8 | installPlanApproval: Automatic 9 | name: openshift-gitops-operator 10 | source: redhat-operators 11 | sourceNamespace: openshift-marketplace 12 | config: 13 | env: 14 | - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES 15 | value: {{ ploigos_namespace }} 16 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/cert_manager.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: cert-manager-operator 5 | namespace: openshift-operators 6 | spec: 7 | channel: stable 8 | name: cert-manager 9 | source: community-operators 10 | sourceNamespace: openshift-marketplace 11 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/gitea.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: gitea-operator 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | channel: alpha 8 | installPlanApproval: Automatic 9 | name: gitea-operator 10 | source: redhatgov-operators 11 | sourceNamespace: openshift-marketplace 12 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/gitlab_ci.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: gitlab-runner-operator 5 | namespace: openshift-operators 6 | spec: 7 | channel: stable 8 | name: gitlab-runner-operator 9 | source: certified-operators 10 | sourceNamespace: openshift-marketplace -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/nexus_artifacts.yml.j2: -------------------------------------------------------------------------------- 1 | nexus_containers.yml.j2 -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/nexus_containers.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: nexus-operator 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | channel: stable 8 | installPlanApproval: Automatic 9 | name: nxrm-operator-certified 10 | source: certified-operators 11 | sourceNamespace: openshift-marketplace 12 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/quay.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: List 3 | items: 4 | # Subscription to trigger OLM installation 5 | - apiVersion: operators.coreos.com/v1alpha1 6 | kind: Subscription 7 | metadata: 8 | name: quay-operator 9 | namespace: {{ ploigos_namespace }} 10 | spec: 11 | channel: quay-v3.3 12 | installPlanApproval: Automatic 13 | name: quay-operator 14 | source: redhat-operators 15 | sourceNamespace: openshift-marketplace 16 | 17 | # Secret required to pull from quay.io 18 | - apiVersion: v1 19 | kind: Secret 20 | metadata: 21 | name: dummy-pull-secret 22 | namespace: {{ ploigos_namespace }} 23 | type: kubernetes.io/dockerconfigjson 24 | data: 25 | .dockerconfigjson: ewogICJhdXRocyI6IHsKICAgICJxdWF5LmlvIjogewogICAgICAiYXV0aCI6ICJjbVZrYUdGMEszRjFZWGs2VHpneFYxTklVbE5LVWpFMFZVRmFRa3MxTkVkUlNFcFRNRkF4VmpSRFRGZEJTbFl4V0RKRE5GTkVOMHRQTlRsRFVUbE9NMUpGTVRJMk1USllWVEZJVWc9PSIsCiAgICAgICJlbWFpbCI6ICIiCiAgICB9CiAgfQp9 26 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/rhsso.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: rhsso-operator 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | channel: alpha 8 | installPlanApproval: Automatic 9 | name: rhsso-operator 10 | source: redhat-operators 11 | sourceNamespace: openshift-marketplace 12 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/selenium.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: selenium-grid-operator 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | channel: alpha 8 | installPlanApproval: Automatic 9 | name: selenium-grid-operator 10 | source: redhatgov-operators 11 | sourceNamespace: openshift-marketplace 12 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/sonarqube.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: sonarqube-operator 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | channel: alpha 8 | installPlanApproval: Automatic 9 | name: sonarqube-operator 10 | source: redhatgov-operators 11 | sourceNamespace: openshift-marketplace 12 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/subscriptions/tekton.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: openshift-pipelines-operator-rh 5 | namespace: openshift-operators 6 | spec: 7 | channel: stable 8 | installPlanApproval: Automatic 9 | name: openshift-pipelines-operator-rh 10 | source: redhat-operators 11 | sourceNamespace: openshift-marketplace 12 | -------------------------------------------------------------------------------- /roles/ploigos-platform/deployment/templates/trustedcabundle.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ platform_trust_bundle_config_map }} 5 | namespace: {{ ploigos_namespace }} 6 | labels: 7 | config.openshift.io/inject-trusted-cabundle: "true" 8 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/defaults/main.yml: -------------------------------------------------------------------------------- 1 | gitea_pod_label: app=gitea 2 | rhsso_realm_name: openshift 3 | rhsso_project_name: '{{ ploigos_namespace }}' 4 | rhsso_app_label: rhsso 5 | 6 | rhsso_gitea_client_id: gitea 7 | rhsso_quay_client_id: quay-enterprise 8 | 9 | subdomain: '{{ full_cluster_name }}' 10 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/tasks/argocd.yml: -------------------------------------------------------------------------------- 1 | - name: Create RHSSO Client definition for Gitea 2 | k8s: 3 | definition: '{{ lookup("template", "rhsso-argocd-client.yml.j2") }}' 4 | 5 | - name: Wait for ArgoCD KeycloakClient to reconcile 6 | k8s_info: 7 | api_version: keycloak.org/v1alpha1 8 | kind: KeycloakClient 9 | name: argocd-client 10 | namespace: "{{ rhsso_project_name }}" 11 | register: argocd_kc 12 | until: 13 | - argocd_kc.resources|length > 0 14 | - argocd_kc.resources[0].status is defined 15 | - argocd_kc.resources[0].status['ready'] 16 | delay: 10 17 | retries: 6 18 | 19 | - name: Wait for client secret to be created 20 | k8s_info: 21 | api_version: v1 22 | kind: Secret 23 | name: keycloak-client-secret-argocd 24 | namespace: "{{ rhsso_project_name }}" 25 | register: argocd_secret 26 | until: 27 | - "argocd_secret.resources[0].data['CLIENT_SECRET'] != ''" 28 | retries: 12 29 | delay: 5 30 | 31 | - name: Fetch ArgoCD secret for RHSSO 32 | set_fact: 33 | rhsso_argocd_secret: "{{ argocd_secret.resources[0].data['CLIENT_SECRET'] | b64decode }}" 34 | 35 | - name: Patch ArgoCD deployment with OpenID Connect info 36 | k8s: 37 | definition: "{{ lookup('template', 'argocd-rhsso-patch.yml.j2') }}" 38 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/tasks/gitea.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Get Gitea Pod 3 | k8s_info: 4 | api_version: v1 5 | kind: Pod 6 | namespace: '{{ ploigos_namespace }}' 7 | label_selectors: 8 | - app=gitea 9 | register: gitea_pod 10 | 11 | - name: Create RHSSO Client definition for Gitea 12 | k8s: 13 | definition: '{{ lookup("template", "rhsso-gitea-client.yml.j2")|from_yaml }}' 14 | register: rhsso_client 15 | 16 | - name: Wait for Gitea KeycloakClient to reconcile 17 | k8s_info: 18 | api_version: keycloak.org/v1alpha1 19 | kind: KeycloakClient 20 | name: gitea-client 21 | namespace: "{{ rhsso_project_name }}" 22 | register: gitea_kc 23 | until: 24 | - gitea_kc.resources|length > 0 25 | - gitea_kc.resources[0].status is defined 26 | - gitea_kc.resources[0].status['ready'] 27 | delay: 10 28 | retries: 6 29 | 30 | - name: Wait for client secret to be created 31 | k8s_info: 32 | api_version: v1 33 | kind: Secret 34 | name: keycloak-client-secret-gitea 35 | namespace: "{{ rhsso_project_name }}" 36 | register: gitea_secret 37 | until: 38 | - "gitea_secret.resources[0].data['CLIENT_SECRET'] != ''" 39 | retries: 12 40 | delay: 5 41 | 42 | - name: Fetch Gitea secret for RHSSO 43 | set_fact: 44 | rhsso_gitea_secret: "{{ gitea_secret.resources[0].data['CLIENT_SECRET'] | b64decode }}" 45 | 46 | # TODO: this should all be part of the Gitea operator 47 | # unfortunately, when we move to Secret-based client secrets we cannot 48 | # easily know when the Secret has changed, so updating an existing client secret 49 | # is difficult. Plus, Gitea CLI doesn't provide a means to check that no 50 | # changes were made to OAuth config - all update commands will cause an UPDATE 51 | # query regardless. 52 | - name: Configure RHSSO as an OAuth2 provider for Gitea 53 | shell: | 54 | oc='{{ oc_cli }}' 55 | pod=$($oc get pods -n {{ gitea_project_name }} -l {{ gitea_pod_label }} -o jsonpath='{.items[0].metadata.name}') 56 | gitea="$oc exec $pod -n {{ gitea_project_name }} -- /home/gitea/gitea --config=/home/gitea/conf/app.ini" 57 | $gitea admin auth list |& grep -F rhsso 58 | if [ $? -gt 0 ]; then 59 | output=$($gitea admin auth add-oauth --name=rhsso --provider openidConnect --key {{ rhsso_gitea_client_id }} --secret {{ rhsso_gitea_secret }} --auto-discover-url https://keycloak-{{ rhsso_project_name }}.apps.{{ subdomain }}/auth/realms/{{ rhsso_realm_name }}/.well-known/openid-configuration 2>&1) 60 | if echo "$output" | grep -qF 'login source already exists'; then 61 | echo ok 62 | elif echo "$output" | grep -qF 'Failed to run app'; then 63 | echo failed 64 | elif echo "$output" | grep -qE 'INSERT INTO "login_source"'; then 65 | echo changed 66 | else 67 | echo failed 68 | fi 69 | else 70 | echo ok 71 | fi 72 | register: gitea_rhsso_oauth 73 | changed_when: '"changed" in gitea_rhsso_oauth.stdout_lines' 74 | failed_when: '"failed" in gitea_rhsso_oauth.stdout_lines' 75 | 76 | - name: Restart gitea if the RHSSO changes were applied 77 | block: 78 | - name: Kill gitea pod for auth changes to take into effect 79 | k8s: 80 | api_version: v1 81 | kind: Pod 82 | namespace: '{{ gitea_project_name }}' 83 | name: "{{ (gitea_pod.resources|first).metadata.name }}" 84 | state: absent 85 | wait: true 86 | 87 | - name: Wait **again** for Gitea to finish being created with RHSSO settings 88 | k8s_info: 89 | api_version: v1 90 | kind: Pod 91 | namespace: '{{ gitea_project_name }}' 92 | label_selectors: 93 | - "{{ gitea_pod_label }}" 94 | register: gitea_pod 95 | until: gitea_pod.resources|length > 0 and (gitea_pod.resources|first).status.phase == "Running" 96 | retries: 10 97 | delay: 30 98 | when: gitea_rhsso_oauth.changed 99 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - set_fact: 2 | sso_services: "{{ supported_services | intersect(managed_services) }}" 3 | vars: 4 | supported_services: 5 | - gitea 6 | - quay 7 | - sonarqube 8 | - argocd 9 | 10 | - name: Deploy OpenShift Realm 11 | k8s: 12 | definition: "{{ lookup('template', 'realm.yml.j2') }}" 13 | 14 | - name: Configure platform services 15 | include_tasks: '{{ _sso_service }}.yml' 16 | loop: "{{ sso_services }}" 17 | loop_control: 18 | loop_var: _sso_service 19 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/tasks/quay.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create RHSSO Client definition for Quay 3 | k8s: 4 | definition: '{{ lookup("template", "../templates/rhsso-quay-client.yml.j2")|from_yaml }}' 5 | 6 | - name: Wait for Quay KeycloakClient to reconcile 7 | k8s_info: 8 | api_version: keycloak.org/v1alpha1 9 | kind: KeycloakClient 10 | name: quay-client 11 | namespace: "{{ rhsso_project_name }}" 12 | register: quay_kc 13 | until: 14 | - quay_kc.resources|length > 0 15 | - quay_kc.resources[0].status is defined 16 | - quay_kc.resources[0].status.ready 17 | delay: 10 18 | retries: 6 19 | 20 | - name: Wait for client secret to be created 21 | k8s_info: 22 | api_version: v1 23 | kind: Secret 24 | name: "keycloak-client-secret-{{ rhsso_quay_client_id }}" 25 | namespace: "{{ rhsso_project_name }}" 26 | register: quay_secret 27 | until: 28 | - "quay_secret.resources[0].data['CLIENT_SECRET'] != ''" 29 | retries: 12 30 | delay: 5 31 | 32 | - name: Generate a Quay secret for RHSSO 33 | set_fact: 34 | rhsso_quay_secret: "{{ quay_secret.resources[0].data['CLIENT_SECRET'] | b64decode }}" 35 | 36 | # TODO: the below (updating Quay config for SSO) should be moved to the Quay Operator. 37 | - name: Get Quay pod info 38 | k8s_info: 39 | api_version: v1 40 | kind: Pod 41 | namespace: '{{ ploigos_namespace }}' 42 | label_selectors: 43 | - "quay-enterprise-component=app" 44 | register: quay_pod 45 | 46 | 47 | - name: Get Quay config secret 48 | k8s_info: 49 | api_version: v1 50 | kind: Secret 51 | namespace: '{{ ploigos_namespace }}' 52 | name: quay-enterprise-config-secret 53 | register: quay_config_secret 54 | 55 | - name: Save Quay config.yaml to temp file 56 | copy: 57 | content: "{{ quay_config_secret.resources[0].data['config.yaml'] | b64decode }}" 58 | dest: "{{ tmp_dir }}/quay-config.yml" 59 | 60 | - name: Append RHSSO config to Quay config 61 | blockinfile: 62 | marker: "# {mark} TISC RHSSO Config" 63 | backup: yes 64 | block: "{{ lookup('template', 'quay-config-rhsso-block.yml.j2') }}" 65 | path: "{{ tmp_dir }}/quay-config.yml" 66 | register: rhsso_config_added 67 | 68 | - name: Patch the secret with the update quay config value 69 | k8s_json_patch: 70 | api_version: v1 71 | kind: Secret 72 | namespace: '{{ ploigos_namespace }}' 73 | name: quay-enterprise-config-secret 74 | patch: 75 | - op: replace 76 | path: /data/config.yaml 77 | value: "{{ lookup('file', tmp_dir + '/quay-config.yml') | b64encode }}" 78 | when: rhsso_config_added.changed 79 | 80 | - name: Restart quay if the RHSSO changes were applied 81 | block: 82 | - name: Kill quay pod for auth changes to take into effect 83 | k8s: 84 | api_version: v1 85 | kind: Pod 86 | namespace: '{{ ploigos_namespace }}' 87 | name: "{{ (quay_pod.resources|first).metadata.name }}" 88 | state: absent 89 | 90 | - name: Wait for Quay to finish being created with RHSSO settings 91 | k8s_info: 92 | api_version: v1 93 | kind: Pod 94 | namespace: '{{ ploigos_namespace }}' 95 | label_selectors: 96 | - "quay-enterprise-component=app" 97 | register: quay_pod 98 | until: quay_pod.resources|length > 0 and (quay_pod.resources|first).status.phase == "Running" 99 | retries: 10 100 | delay: 30 101 | when: rhsso_config_added.changed 102 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/tasks/sonarqube.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure RH SSO options for sonarqube 3 | block: 4 | - name: Create SonarQube SSO Client 5 | k8s: 6 | definition: '{{ lookup("template", "rhsso-sonar4-client.yml.j2")|from_yaml }}' 7 | 8 | - name: Wait for SonarQube KeycloakClient to reconcile 9 | k8s_info: 10 | api_version: keycloak.org/v1alpha1 11 | kind: KeycloakClient 12 | name: sonarqube-client 13 | namespace: "{{ rhsso_project_name }}" 14 | register: sonarqube_kc 15 | until: 16 | - sonarqube_kc.resources|length > 0 17 | - sonarqube_kc.resources[0].status is defined 18 | - sonarqube_kc.resources[0].status.ready 19 | 20 | - name: Get RHSSO login 21 | k8s_info: 22 | api_version: v1 23 | kind: Secret 24 | name: credential-rhsso 25 | namespace: "{{ ploigos_namespace }}" 26 | register: rhsso_login_secret 27 | 28 | - set_fact: 29 | sso_admin_password: "{{ rhsso_login_secret | json_query('resources[0].data.ADMIN_PASSWORD') | b64decode }}" 30 | sso_admin_user: "{{ rhsso_login_secret | json_query('resources[0].data.ADMIN_USERNAME') | b64decode }}" 31 | keycloak_url: 'https://keycloak-{{ rhsso_project_name }}.apps.{{ subdomain }}' 32 | sonarqube_url: 'https://sonarqube-{{ sonarqube_project_name }}.apps.{{ subdomain }}' 33 | sonarqube_username: "{{ all_services_details.sonarqube.username }}" 34 | sonarqube_password: "{{ all_services_details.sonarqube.password }}" 35 | 36 | - name: Wait for KeyCloak to respond to requests 37 | uri: 38 | url: '{{ keycloak_url }}' 39 | return_content: yes 40 | validate_certs: no 41 | register: keycloak_endpoint 42 | until: keycloak_endpoint.status == 200 43 | retries: 10 44 | delay: 30 45 | 46 | - name: Get RH SSO admin token 47 | uri: 48 | url: '{{ keycloak_url }}/auth/realms/master/protocol/openid-connect/token' 49 | validate_certs: false 50 | method: POST 51 | body: 52 | username: "{{ sso_admin_user }}" 53 | password: "{{ sso_admin_password }}" 54 | grant_type: "password" 55 | client_id: "admin-cli" 56 | body_format: form-urlencoded 57 | status_code: 200,201,204 58 | register: rh_sso_admin_token 59 | 60 | # The following is needed to compensate for not being able to set the defaultClientScopes in the KeyCloakClient configuration 61 | # https://issues.redhat.com/browse/KEYCLOAK-15014 62 | # ------------------------ begin role_list removal ------------------------------------- 63 | - name: Remove 'role_list' from client scopes 64 | block: 65 | - name: Get all clients 66 | uri: 67 | url: '{{ keycloak_url }}/auth/admin/realms/openshift/clients?first=0&max=20' 68 | validate_certs: false 69 | method: GET 70 | headers: 71 | Content-Type: application/json 72 | Authorization: "Bearer {{ rh_sso_admin_token.json.access_token }}" 73 | return_content: yes 74 | status_code: 200 75 | register: all_clients 76 | 77 | - name: Get all client scopes 78 | uri: 79 | url: '{{ keycloak_url }}/auth/admin/realms/openshift/client-scopes' 80 | validate_certs: false 81 | method: GET 82 | headers: 83 | Content-Type: application/json 84 | Authorization: "Bearer {{ rh_sso_admin_token.json.access_token }}" 85 | return_content: yes 86 | status_code: 200 87 | register: all_client_scopes 88 | 89 | - set_fact: 90 | sonarqube_client: "{{ all_clients.json | json_query(sonarqube_client_query) | first }}" 91 | vars: 92 | sonarqube_client_query: "[?clientId == 'sonarqube']" 93 | 94 | - set_fact: 95 | sonarqube_client_id: "{{ sonarqube_client.id }}" 96 | sonarqube_client_has_role_list: "{{ 'role_list' in sonarqube_client.defaultClientScopes }}" 97 | role_list_client_scope_id: "{{ all_client_scopes.json | json_query(role_list_scope_query) | first }}" 98 | vars: 99 | role_list_scope_query: "[?name == 'role_list'].id" 100 | 101 | - name: Remove 'role_list' from default client scope 102 | uri: 103 | url: '{{ keycloak_url }}/auth/admin/realms/openshift/clients/{{sonarqube_client_id}}/default-client-scopes/{{role_list_client_scope_id}}' 104 | validate_certs: false 105 | method: DELETE 106 | headers: 107 | Content-Type: application/json 108 | Authorization: "Bearer {{ rh_sso_admin_token.json.access_token }}" 109 | return_content: yes 110 | status_code: 204 111 | when: sonarqube_client_has_role_list 112 | # ------------------------ end of role_list removal ------------------------------------- 113 | 114 | 115 | - name: Configure SonarQube SAML Settings 116 | block: 117 | - name: Get realm cert for sonarqube from RHSSO 118 | uri: 119 | url: '{{ keycloak_url }}/auth/admin/realms/openshift/keys' 120 | validate_certs: false 121 | method: GET 122 | headers: 123 | Content-Type: application/json 124 | Authorization: "Bearer {{ rh_sso_admin_token.json.access_token }}" 125 | return_content: yes 126 | status_code: 200 127 | register: realm_keys 128 | 129 | - set_fact: 130 | rhsso_saml_cert_value: "{{ realm_keys.json | json_query(cert_query) | first }}" 131 | vars: 132 | cert_query: "keys[?type == 'RSA'].certificate" 133 | 134 | - name: Fetch sonarqube secret from cluster 135 | set_fact: 136 | sonarqube_secret_data: "{{ lookup('k8s', kind='Secret', namespace=sonarqube_project_name, resource_name='sonarqube-admin-credentials') }}" 137 | 138 | - name: Update sonarqube settings 139 | shell: >- 140 | devsecops-api sonarqube update-setting 141 | {{ sonarqube_url }} 142 | --login-username {{ sonarqube_username }} --login-password {{ sonarqube_password }} 143 | --setting-name '{{ item.name }}' 144 | --setting-value '{{ item.value }}' 145 | # FIXME : not really sure how to deal with the changed_when deal 146 | register: sonarqube_settings 147 | # changed_when: sonarqube_settings.stdout_lines|to_json|from_json|json_query("[?ends_with(@, ` added`)]")|length > 0 148 | failed_when: sonarqube_settings.rc != 0 149 | until: not sonarqube_settings.failed 150 | retries: 3 151 | delay: 10 152 | loop: 153 | - { name: 'sonar.core.serverBaseURL', value: '{{ sonarqube_url }}' } 154 | - { name: 'sonar.auth.saml.enabled', value: 'true' } 155 | - { name: 'sonar.auth.saml.applicationId', value: 'sonarqube' } 156 | - { name: 'sonar.auth.saml.providerName', value: 'RH SSO' } 157 | - { name: 'sonar.auth.saml.providerId', value: '{{ keycloak_url }}/auth/realms/{{ rhsso_realm_name }}' } 158 | - { name: 'sonar.auth.saml.loginUrl', value: '{{ keycloak_url }}/auth/realms/{{ rhsso_realm_name }}/protocol/saml' } 159 | - { name: 'sonar.auth.saml.certificate.secured', value: '{{ rhsso_saml_cert_value }}' } 160 | - { name: 'sonar.auth.saml.user.login', value: 'login' } 161 | - { name: 'sonar.auth.saml.user.name', value: 'name' } 162 | - { name: 'sonar.auth.saml.group.name', value: 'groups' } 163 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/templates/argocd-rhsso-patch.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: ArgoCD 3 | metadata: 4 | name: argocd 5 | namespace: {{ ploigos_namespace }} 6 | spec: 7 | oidcConfig: | 8 | name: Red Hat SSO 9 | issuer: https://keycloak-{{ rhsso_project_name }}.apps.{{ subdomain }}/auth/realms/openshift 10 | clientID: argocd 11 | clientSecret: {{ rhsso_argocd_secret }} 12 | requestedScopes: ["openid", "profile", "email", "groups"] 13 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/templates/quay-config-rhsso-block.yml.j2: -------------------------------------------------------------------------------- 1 | RHSSO_LOGIN_CONFIG: 2 | CLIENT_ID: {{ rhsso_quay_client_id }} 3 | CLIENT_SECRET: {{ rhsso_quay_secret }} 4 | LOGIN_SCOPES: 5 | - openid 6 | OIDC_SERVER: https://keycloak-{{ rhsso_project_name }}.apps.{{ subdomain }}/auth/realms/openshift/ 7 | SERVICE_NAME: Red Hat SSO 8 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/templates/realm.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: keycloak.org/v1alpha1 2 | kind: KeycloakRealm 3 | metadata: 4 | name: openshift 5 | namespace: {{ ploigos_namespace }} 6 | labels: 7 | app: {{ rhsso_app_label }} 8 | spec: 9 | realm: 10 | id: openshift 11 | realm: openshift 12 | enabled: True 13 | displayName: Ploigos OpenShift Realm 14 | instanceSelector: 15 | matchLabels: 16 | app: {{ rhsso_app_label }} 17 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/templates/rhsso-argocd-client.yml.j2: -------------------------------------------------------------------------------- 1 | # Add keycloak client for OpenShift 2 | apiVersion: keycloak.org/v1alpha1 3 | kind: KeycloakClient 4 | metadata: 5 | name: argocd-client 6 | namespace: {{ ploigos_namespace }} 7 | labels: 8 | app: {{ rhsso_app_label }} 9 | spec: 10 | realmSelector: 11 | matchLabels: 12 | app: {{ rhsso_app_label }} 13 | client: 14 | clientId: argocd 15 | name: argocd 16 | enabled: true 17 | clientAuthenticatorType: client-secret 18 | publicClient: false 19 | redirectUris: 20 | - 'https://argocd-server-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}/*' 21 | rootUrl: 'https://argocd-server-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}/' 22 | protocol: openid-connect 23 | standardFlowEnabled: true 24 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/templates/rhsso-gitea-client.yml.j2: -------------------------------------------------------------------------------- 1 | # Add keycloak client for OpenShift 2 | - apiVersion: keycloak.org/v1alpha1 3 | kind: KeycloakClient 4 | metadata: 5 | name: gitea-client 6 | namespace: {{ rhsso_project_name }} 7 | labels: 8 | app: {{ rhsso_app_label }} 9 | spec: 10 | realmSelector: 11 | matchLabels: 12 | app: {{ rhsso_app_label }} 13 | client: 14 | clientId: {{ rhsso_gitea_client_id }} 15 | name: {{ rhsso_gitea_client_id }} 16 | enabled: true 17 | clientAuthenticatorType: client-secret 18 | publicClient: false 19 | redirectUris: 20 | - 'https://gitea-{{ ploigos_namespace }}.apps.{{ subdomain }}/*' 21 | rootUrl: 'https://gitea-{{ ploigos_namespace }}.apps.{{ subdomain }}/' 22 | protocol: openid-connect 23 | standardFlowEnabled: true 24 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/templates/rhsso-quay-client.yml.j2: -------------------------------------------------------------------------------- 1 | # Add keycloak client for OpenShift 2 | - apiVersion: keycloak.org/v1alpha1 3 | kind: KeycloakClient 4 | metadata: 5 | name: quay-client 6 | namespace: {{ rhsso_project_name }} 7 | labels: 8 | app: {{ rhsso_app_label }} 9 | spec: 10 | realmSelector: 11 | matchLabels: 12 | app: {{ rhsso_app_label }} 13 | client: 14 | clientId: {{ rhsso_quay_client_id }} 15 | name: {{ rhsso_quay_client_id }} 16 | enabled: true 17 | clientAuthenticatorType: client-secret 18 | publicClient: false 19 | redirectUris: 20 | - 'https://quay-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}/oauth2/rhsso/callback' 21 | rootUrl: 'https://quay-{{ ploigos_namespace }}.apps.{{ full_cluster_name }}/' 22 | protocol: openid-connect 23 | standardFlowEnabled: true 24 | -------------------------------------------------------------------------------- /roles/ploigos-platform/sso-integration/templates/rhsso-sonar4-client.yml.j2: -------------------------------------------------------------------------------- 1 | # Add keycloak client for OpenShift 2 | - apiVersion: keycloak.org/v1alpha1 3 | kind: KeycloakClient 4 | metadata: 5 | name: sonarqube-client 6 | namespace: {{ rhsso_project_name }} 7 | labels: 8 | app: {{ rhsso_app_label }} 9 | spec: 10 | realmSelector: 11 | matchLabels: 12 | app: {{ rhsso_app_label }} 13 | client: 14 | clientId: sonarqube 15 | secret: sonarqube 16 | name: sonarqube 17 | enabled: true 18 | clientAuthenticatorType: client-secret 19 | publicClient: false 20 | redirectUris: 21 | - 'https://sonarqube-{{ sonarqube_project_name }}.apps.{{ subdomain }}/oauth2/callback/saml' 22 | rootUrl: 'https://sonarqube-{{ sonarqube_project_name }}.apps.{{ subdomain }}/' 23 | protocol: saml 24 | attributes: 25 | saml.authnstatement: 'true' 26 | saml.assertion.signature: 'false' 27 | saml.multivalued.roles: 'false' 28 | saml.server.signature: 'true' 29 | saml.client.signature: 'false' 30 | saml.signature.algorithm: RSA_SHA256 31 | saml_name_id_format: username 32 | standardFlowEnabled: true 33 | 34 | protocolMappers: 35 | - config: 36 | attribute.name: email 37 | user.attribute: Email 38 | id: email-1 39 | name: Email 40 | protocol: saml 41 | protocolMapper: saml-user-property-mapper 42 | - config: 43 | attribute.name: groups 44 | single: 'true' 45 | id: groups-1 46 | name: Groups 47 | protocol: saml 48 | protocolMapper: saml-role-list-mapper 49 | - config: 50 | attribute.name: name 51 | user.attribute: username 52 | id: name-1 53 | name: name 54 | protocol: saml 55 | protocolMapper: saml-user-property-mapper 56 | - config: 57 | attribute.name: login 58 | user.attribute: Username 59 | id: login-1 60 | name: Login 61 | protocol: saml 62 | protocolMapper: saml-user-property-mapper 63 | 64 | -------------------------------------------------------------------------------- /vars/ploigos-platform/common.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This tells the playbooks to not try to include default-named vars files 3 | common_included: yes 4 | 5 | # This toggle indicates whether or not the cluster is connected to the internet 6 | # and can pull content sources from their normal places 7 | connected_cluster: yes 8 | 9 | # This toggle indicates whether the cluster router certificates are trusted by 10 | # the default bundles on most operating system images (and therefore would be 11 | # trusted by most hosts and container images validating them) 12 | trusted_router_certificates: yes 13 | 14 | ################################################################################ 15 | # The following variables are especially important to pay attention to if you're 16 | # using an RHPDS-provisioned cluster and some of it needs to be updated every 17 | # time you provision a cluster to match information from the email you receive. 18 | # 19 | # NOTE: IF A VARIABLE IS COMMENTED, IT LIKELY DEFAULTS TO THE OPPOSITE 20 | ################################################################################ 21 | 22 | ################################################################################ 23 | # DO NOT CHANGE OR COMMENT THESE FOR ANY REASON - they're used heavily 24 | # throughout all the various roles 25 | _tmp_parent: '{{ "/".join([ playbook_dir, "../tmp" ])|realpath }}' 26 | full_cluster_name: '{{ ".".join([ cluster_name, openshift_base_domain ]) }}' 27 | tmp_dir: '{{ "/".join([ _tmp_parent, full_cluster_name ]) }}' 28 | 29 | 30 | # The path to your 'oc' client - the provisioner puts it in {{ tmp_dir }}, 31 | # for RHPDS clusters, or those being used with the container workflow, you 32 | # should specify the absolute path of the oc client. The value commented out 33 | # below is the location inside the container for that workflow. 34 | # oc_cli: '/usr/local/bin/oc' 35 | oc_cli: /usr/local/bin/oc 36 | 37 | generated_password: "{{ lookup('password', '../tmp/rhssopasswd.txt length=15 chars=ascii_letters') }}" 38 | 39 | # The number of users created, as a string 40 | number_of_users: "3" 41 | 42 | # Generate a sequence of users 43 | sequence_users: | 44 | {%- for username in lookup("sequence", "1-" + number_of_users + ":user%0i", wantlist=True) %} 45 | - username: {{ username }} 46 | firstName: {{ username }} 47 | lastName: Workshop user 48 | password: "{{ generated_password }}" 49 | email: {{ username }}@example.com 50 | jenkinsRole: edit 51 | nexusRole: nx-admin 52 | {% endfor -%} 53 | 54 | # Set common variables for RH-SSO setup and client integration 55 | rhsso_users: '{{ sequence_users|from_yaml }}' 56 | rhsso_realm_name: openshift 57 | rhsso_project_name: rhsso 58 | rhsso_app_label: rhsso 59 | -------------------------------------------------------------------------------- /watches.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - version: v1alpha1 3 | group: redhatgov.io 4 | kind: PloigosPlatform 5 | playbook: playbooks/ploigos.yml 6 | watchDependentResources: false 7 | vars: 8 | cr_state: present 9 | cr_type: platform 10 | finalizer: 11 | name: finalizer.ploigosplatform.redhatgov.io 12 | vars: 13 | cr_state: absent 14 | cr_type: platform 15 | - version: v1alpha1 16 | group: redhatgov.io 17 | kind: PloigosPipeline 18 | playbook: playbooks/ploigos.yml 19 | vars: 20 | cr_state: present 21 | cr_type: pipeline 22 | --------------------------------------------------------------------------------