├── .gitignore ├── LICENSE ├── README.md ├── better-helm ├── README.md ├── with-crd │ ├── Chart.yaml │ ├── crds │ │ └── crunchy-data.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── another-hook.yaml │ │ ├── config-map.yaml │ │ ├── deployment.yaml │ │ ├── postgres-cluster.yaml │ │ ├── route.yaml │ │ ├── security.yaml │ │ └── service.yaml │ └── values.yaml ├── with-subchart │ ├── Chart.lock │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── another-hook.yaml │ │ ├── config-map.yaml │ │ ├── deployment.yaml │ │ ├── route.yaml │ │ ├── security.yaml │ │ └── service.yaml │ └── values.yaml └── with-templ │ ├── Chart.yaml │ ├── templates │ ├── NOTES.txt │ ├── another-hook.yaml │ ├── config-map.yaml │ ├── deployment.yaml │ ├── postgres-from-template.yaml │ ├── route.yaml │ ├── security.yaml │ └── service.yaml │ └── values.yaml ├── gitops ├── README.md ├── argocd │ ├── book-apps.yaml │ ├── kustomization.yaml │ ├── ns.yaml │ ├── postgresql.yaml │ └── roles.yaml └── tekton │ ├── infra │ ├── maven-artifact-cache-pvc.yaml │ ├── maven-settings-cm.yaml │ ├── nexus.yaml │ ├── ns.yaml │ ├── roles.yaml │ ├── sa.yaml │ └── secret.yaml │ ├── kustomization.yaml │ ├── pipeline.sh │ ├── pipelines │ ├── dev-pipeline.yaml │ └── stage-release.yaml │ └── tasks │ ├── bash-task.yaml │ ├── create-release.yaml │ ├── extract-digest-from-kustomize-task.yaml │ ├── extract-digest-task.yaml │ ├── git-update-deployment.yaml │ └── maven-task.yaml ├── helm-chart ├── Chart.yaml ├── README.md ├── templates │ ├── NOTES.txt │ ├── config-map.yaml │ ├── deployment.yaml │ ├── post-install-hook.yaml │ ├── route.yaml │ └── service.yaml └── values.yaml ├── kube-operator ├── .gitignore ├── Dockerfile ├── Makefile ├── PROJECT ├── README.md ├── bundle.Dockerfile ├── bundle │ ├── manifests │ │ ├── charts.wanja.org_personservices.yaml │ │ ├── person-service-operator-controller-manager-metrics-service_v1_service.yaml │ │ ├── person-service-operator-manager-config_v1_configmap.yaml │ │ ├── person-service-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml │ │ └── person-service-operator.clusterserviceversion.yaml │ ├── metadata │ │ └── annotations.yaml │ └── tests │ │ └── scorecard │ │ └── config.yaml ├── config │ ├── crd │ │ ├── bases │ │ │ └── charts.wanja.org_personservices.yaml │ │ └── kustomization.yaml │ ├── default │ │ ├── kustomization.yaml │ │ ├── manager_auth_proxy_patch.yaml │ │ └── manager_config_patch.yaml │ ├── manager │ │ ├── controller_manager_config.yaml │ │ ├── kustomization.yaml │ │ └── manager.yaml │ ├── manifests │ │ ├── bases │ │ │ └── person-service-operator.clusterserviceversion.yaml │ │ └── kustomization.yaml │ ├── prometheus │ │ ├── kustomization.yaml │ │ └── monitor.yaml │ ├── rbac │ │ ├── auth_proxy_client_clusterrole.yaml │ │ ├── auth_proxy_role.yaml │ │ ├── auth_proxy_role_binding.yaml │ │ ├── auth_proxy_service.yaml │ │ ├── kustomization.yaml │ │ ├── leader_election_role.yaml │ │ ├── leader_election_role_binding.yaml │ │ ├── personservice_editor_role.yaml │ │ ├── personservice_viewer_role.yaml │ │ ├── role.yaml │ │ ├── role_binding.yaml │ │ └── service_account.yaml │ ├── samples │ │ ├── charts_v1alpha1_personservice.yaml │ │ └── kustomization.yaml │ └── scorecard │ │ ├── bases │ │ └── config.yaml │ │ ├── kustomization.yaml │ │ └── patches │ │ ├── basic.config.yaml │ │ └── olm.config.yaml ├── helm-charts │ └── person-service │ │ ├── Chart.yaml │ │ ├── templates │ │ ├── NOTES.txt │ │ ├── config-map.yaml │ │ ├── deployment.yaml │ │ ├── route.yaml │ │ └── service.yaml │ │ └── values.yaml └── watches.yaml ├── kustomize-ext ├── README.md ├── base │ ├── deployment.yaml │ ├── kustomization.yaml │ ├── postgres.yaml │ ├── route.yaml │ └── service.yaml └── overlays │ ├── dev │ └── kustomization.yaml │ └── stage │ ├── apply-health-checks.yaml │ └── kustomization.yaml ├── kustomize ├── README.md ├── base │ ├── deployment.yaml │ ├── kustomization.yaml │ ├── route.yaml │ └── service.yaml └── overlays │ ├── dev │ ├── deployment.yaml │ ├── kustomization.yaml │ └── route.yaml │ └── stage │ ├── deployment.yaml │ ├── kustomization.yaml │ └── route.yaml ├── ocp-template ├── README.md └── service-template.yaml ├── person-service ├── .dockerignore ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ └── maven-wrapper.properties ├── .s2i │ └── environment ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── docker │ │ ├── Dockerfile.jvm │ │ ├── Dockerfile.legacy-jar │ │ ├── Dockerfile.native │ │ └── Dockerfile.native-distroless │ ├── java │ │ └── org │ │ │ └── wanja │ │ │ └── book │ │ │ └── quarkus │ │ │ ├── HelloResource.java │ │ │ ├── Person.java │ │ │ └── PersonResource.java │ └── resources │ │ ├── META-INF │ │ └── resources │ │ │ └── index.html │ │ ├── application.properties │ │ └── import.sql │ └── test │ └── java │ └── org │ └── wanja │ └── book │ └── quarkus │ ├── HelloResourceTest.java │ └── NativeHelloResourceIT.java ├── raw-kubernetes ├── README.md ├── deployment.yaml ├── route.yaml └── service.yaml └── tekton ├── README.md ├── infra ├── maven-artifact-cache-pvc.yaml └── maven-settings-cm.yaml ├── pipeline.sh ├── pipelines ├── tekton-pipeline-test.yaml └── tekton-pipeline.yaml └── tasks ├── bash-task.yaml ├── kustomize-task.yaml └── maven-task.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | .DS_Store 25 | .vscode/settings.json 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using the Examples discussed in the book *Getting GitOps.* 2 | 3 | You can download the final eBook as PDF here: 4 | 5 | https://developers.redhat.com/e-books/getting-gitops-practical-platform-openshift-argo-cd-and-tekton 6 | 7 | ## Prerequisites 8 | In order to run all of the examples I am discussing in this book, you should have the following software available 9 | 10 | - OpenShift 4.8.x (see below for instructions) 11 | - Quarkus 2.16.x 12 | - Maven 3.8.3 13 | - Java JDK 11 or better 14 | - git 15 | - Docker Desktop or Podman Desktop 16 | - OpenShift client (`oc`) matching the version of the OpenShift Cluster 17 | - An Editor to work with (VScode, Eclipse, IntelliJ) 18 | 19 | OpenShift needs to have the following Operators installed: 20 | - OpenShift GitOps 21 | - OpenShift Pipelines 22 | - Crunchy Postgres for Kubernetes by Crunchy Data 23 | 24 | ## A quick Note on Quarkus Versions 25 | If you want to use latest Quarkus versions, please make sure to install the matching requirements for that version of Quarkus. Have a look at the following [guide](https://quarkus.io/guides/getting-started) 26 | 27 | ## Getting an OpenShift instance 28 | You have three possible options to get an OpenShift installation 29 | 30 | ### Using Red Hat Developer Sandbox 31 | This solution is the easiest one but unfortunately very limited. You can’t create new projects (namespaces) and you’re not allowed to install additional operators. This solution is only meant to be used for chapters one and two and the helm chart part of chapter three. 32 | 33 | Go to the [Developer Sandbox][1] and register for free. 34 | 35 | ### Using CodeReady Containers (`crc`) 36 | CodeReady Containers (`crc`) provides a single node OpenShift installation for Windows, macOS and Linux. It runs OpenShift on an embedded virtual machine. You have all the flexibility of an external OpenShift cluster without the need of 3 or more master nodes. You are also able to install additional Operators. 37 | 38 | This solution requires the following resources on your local machine: 39 | - 9 GB free memory 40 | - 4 CPU cores 41 | - 50 GB free hard disk space 42 | 43 | Go to [GitHub][2] for a list of releases and have a look at the [official documentation][3]. 44 | 45 | 46 | ### Using Single Node OpenShift (`SNO`) 47 | With this solution you have most flexibility in using your OpenShift installation. But of course this also requires most resources. You should have a dedicated spare machine with the following specs in order to use `SNO`: 48 | - 8 vCPU cores 49 | - 32GB free memory 50 | - 150GB free hard disk space 51 | 52 | Have a look at the [Red Hat Console][4] to start the installation process. After installation you should have a look at my [OpenShift Config script][5], you can find on GitHub as well. This script creates persistent volumes, makes the internal registry non-ephemeral, creates a cluster-admin user and installs necessary operators and a CI environment with Nexus (a maven repository) and Gogs (a Git repository). 53 | 54 | ## The container image registry 55 | I am using [Quay.io][6] for all of my images. The account is for free and it does not limit upload/download rates. Once registered, go to `Account Settings` —\> `User Settings` and generate an encrypted password. Quay.io will give you some options to store your password hash, for example as a Kubernetes-secret, which you can then directly use as push-/pull secrets. 56 | 57 | The free account however limits you to only create public repositories, so anybody can read from your repository but only you are allowed to write and update your image. 58 | 59 | Once you’ve created your image in the repository, you have to go to the image properties and make sure it’s public. By default, Quay.io creates private repositories. 60 | 61 | However, you can use any docker compliant registry to store your container images on. 62 | 63 | ## The structure of the example 64 | All the examples can be found on GitHub: [https://github.com/wpernath/book-example][7] 65 | 66 | Please fork it and then use it as you want to use it. 67 | 68 | ### Chapter One: Folders 69 | The folder `person-service` contains the Java sources of the Quarkus example. If you want to deploy it on OpenShift, please make sure to first install a PostgreSQL server, either via Crunchy Data or by instantiating the template `postgresql-persistent`. 70 | 71 | ```bash 72 | $ oc new-app postgresql-persistent \ 73 | -p POSTGRESQL_USER=wanja \ 74 | -p POSTGRESQL_PASSWORD=wanja \ 75 | -p POSTGRESQL_DATABASE=wanjadb \ 76 | -p DATABASE_SERVICE_NAME=wanjaserver 77 | ``` 78 | 79 | ### Chapter Two: Folders 80 | - `raw-kubernetes` contains the raw Kubernetes manifest files 81 | - `ocp-template` contains the OpenShift Template file 82 | - `kustomize`contains a set of basic files for use with Kustomize 83 | - `kustomize-ext` contains a set of advanced files for use with Kustomize 84 | 85 | ### Chapter Three: Folders 86 | This chapter is about Helm Charts and Kubernetes Operators. So you can find the corresponding folders are `helm-chart` and `kube-operator`. 87 | 88 | ### Chapter Four: Folders 89 | This chapter is about Tekton / OpenShift Pipelines. The sources can be found in the folder `tekton`. Please also have a look at the script `pipeline.sh`. It installs all the necessary Tasks and resources if you call it with the `init` parameter: 90 | 91 | ```bash 92 | $ pipeline.sh init 93 | configmap/maven-settings configured 94 | persistentvolumeclaim/maven-repo-pvc configured 95 | persistentvolumeclaim/builder-pvc configured 96 | task.tekton.dev/kustomize configured 97 | task.tekton.dev/maven-caching configured 98 | pipeline.tekton.dev/build-and-push-image configured 99 | ``` 100 | 101 | You can start the pipeline by executing 102 | ```bash 103 | $ pipeline.sh start -u wpernath -p 104 | pipelinerun.tekton.dev/build-and-push-image-run-20211125-163308 created 105 | ``` 106 | 107 | ### Chapter Five: Folders 108 | This chapter is about using Tekton and ArgoCD. The sources can be found in the folder `gitops`. 109 | 110 | To initialize call: 111 | ```bash 112 | $ ./pipeline.sh init [--force] --git-user \ 113 | --git-password \ 114 | --registry-user \ 115 | --registry-password 116 | ``` 117 | 118 | This call (if given the `--force` flag) will create the following namespaces and ArgoCD applications for you: 119 | - `book-ci`: Pipelines, Tasks and a Nexus instance 120 | - `book-dev`: The current dev stage 121 | - `book-stage`: The last stage release 122 | 123 | ```bash 124 | $ ./pipeline.sh build -u \ 125 | -p 126 | ``` 127 | 128 | This starts the development pipeline as discussed in chapter 5. Whenever the pipeline is successfully executed, you should see an updated message on the `person-service-config` Git repository. And you should see that ArgoCD has initiated a synchronization process, which ends with a redeployment of the Quarkus application. 129 | 130 | To start the staging pipeline, call 131 | ```bash 132 | $ ./pipeline.sh stage -r v1.0.1-testing 133 | ``` 134 | 135 | This creates a new branch in Git called `release-v1.0.1-testing`, uses the current DEV image, tags it on quay.io and updates the `stage` config in git. 136 | 137 | In order to apply the changes, you need to either merge the branch directly or create a pull request and merge the changes then. 138 | 139 | [1]: https://developers.redhat.com/developer-sandbox 140 | [2]: https://github.com/code-ready/crc/releases 141 | [3]: https://access.redhat.com/documentation/en-us/red_hat_codeready_containers/1.33/html-single/getting_started_guide/index 142 | [4]: https://console.redhat.com/openshift/assisted-installer/clusters/~new 143 | [5]: https://github.com/wpernath/openshift-config 144 | [6]: https://quay.io/ 145 | [7]: https://github.com/wpernath/book-example 146 | -------------------------------------------------------------------------------- /better-helm/with-crd/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: person-service-crd 3 | description: A better helm chart for the basic person-service used as example in the book 4 | home: https://github.com/wpernath/book-example 5 | type: application 6 | version: 0.0.10 7 | appVersion: "v1.8.9-native" 8 | sources: 9 | - https://github.com/wpernath/book-example 10 | maintainers: 11 | - name: Wanja Pernath 12 | email: wpernath@redhat.com 13 | -------------------------------------------------------------------------------- /better-helm/with-crd/crds/crunchy-data.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: Subscription 3 | metadata: 4 | name: postgresql-operator 5 | namespace: openshift-operators 6 | spec: 7 | channel: v5 8 | name: postgresql 9 | source: community-operators 10 | sourceNamespace: openshift-marketplace 11 | -------------------------------------------------------------------------------- /better-helm/with-crd/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | # Release NOTES.txt of person-service helm chart 2 | - Chart name: {{ .Chart.Name }} 3 | - Chart description: {{ .Chart.Description }} 4 | - Chart version: {{ .Chart.Version }} 5 | - App version: {{ .Chart.AppVersion }} 6 | - Release name: {{ .Release.Name }} 7 | - Release version: {{ .Release.Release }} 8 | 9 | ## Build and install this helm chart 10 | ```bash 11 | $ helm package better-helm/ -u 12 | $ helm install person-service 13 | $ helm install person-service2 14 | $ helm upgrade person-service 15 | ``` 16 | 17 | ## To learn more about this relase 18 | ```bash 19 | $ helm list 20 | $ helm history person-service 21 | ``` 22 | 23 | ## Version history 24 | - 0.0.1 Initial release 25 | - 0.0.2 release with some fixed bugs 26 | - 0.0.3 release with image coming from quay.io and better parameter substitution 27 | - 0.0.4 added NOTES.txt and a configmap 28 | - 0.0.5 added a batch Job for post-install and post-upgrade 29 | - 0.0.6 added database from CrunchyData PostgreSQL Operator 30 | - 0.0.7 added a post-install job which is filling the DB by calling the service API 31 | - 0.0.8 Made sure you can install this chart multiple times by using the release name as metadata/name entries 32 | - 0.0.9 added kubectl wait --for to wait for the service to be successfully rolled out 33 | - 0.0.10 added required operator as subscription CRD in crds folder of the chart 34 | 35 | 36 | -------------------------------------------------------------------------------- /better-helm/with-crd/templates/another-hook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: {{ .Release.Name }}-job 5 | annotations: 6 | "helm.sh/hook": post-install,post-upgrade 7 | "helm.sh/hook-weight": "-5" 8 | "helm.sh/hook-delete-policy": before-hook-creation #, hook-succeeded 9 | labels: 10 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 11 | spec: 12 | ttlSecondsAfterFinished: 600 13 | template: 14 | metadata: 15 | name: {{ .Chart.Name | quote }} 16 | spec: 17 | serviceAccountName: {{ .Release.Name }}-sa 18 | restartPolicy: Never 19 | containers: 20 | - name: initialize-database 21 | image: "quay.io/wpernath/kustomize-ubi@sha256:7b2ce43d1b4a0f829ba750d4d44da4a85cfb6089ca0436678aff09862e8feb83" 22 | env: 23 | - name: NAMESPACE 24 | value: {{ .Release.Namespace | default "better-helm" | quote }} 25 | - name: NAME 26 | value: {{ .Release.Name }}-service 27 | command: 28 | - bin/bash 29 | - -c 30 | - | 31 | SERVICE_URL=${NAME}.${NAMESPACE}.svc:8080/person 32 | echo "SERVICE_URL: http://${SERVICE_URL}" 33 | echo "Waiting for the service to be successfully rolled out..." 34 | 35 | # wait until the service has been successfully rolled out 36 | #kubectl wait --for=condition=Ready Pod -l deployment={{ .Release.Name }} 37 | 38 | NUM_PERSONS=$(curl -s http://$SERVICE_URL/count) 39 | 40 | if [ $NUM_PERSONS -eq 0 ]; then 41 | echo "There are no persons in the database, filling some" 42 | 43 | http -I --json POST ${SERVICE_URL} firstName=Carlos lastName=Santana salutation=Mr 44 | http -I --json POST ${SERVICE_URL} firstName=Joe lastName=Cocker salutation=Mr 45 | http -I --json POST ${SERVICE_URL} firstName=Eric lastName=Clapton salutation=Mr 46 | http -I --json POST ${SERVICE_URL} firstName=Kurt lastName=Cobain salutation=Mr 47 | 48 | else 49 | echo "There are already $NUM_PERSONS persons in the database." 50 | http -I --pretty format ${SERVICE_URL} 51 | fi 52 | -------------------------------------------------------------------------------- /better-helm/with-crd/templates/config-map.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Release.Name }}-config 5 | labels: 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | data: 8 | APP_GREETING: |- 9 | {{ .Values.config.greeting | default "Yeah, it's openshift time" }} 10 | -------------------------------------------------------------------------------- /better-helm/with-crd/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: person-service 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | name: {{ .Release.Name }}-deployment 8 | spec: 9 | progressDeadlineSeconds: 600 10 | replicas: {{ .Values.deployment.replicas | default 1 }} 11 | selector: 12 | matchLabels: 13 | deployment: {{ .Release.Name }} 14 | strategy: 15 | rollingUpdate: 16 | maxSurge: 25% 17 | maxUnavailable: 25% 18 | type: RollingUpdate 19 | template: 20 | metadata: 21 | labels: 22 | deployment: {{ .Release.Name }} 23 | spec: 24 | containers: 25 | - image: "{{ .Values.deployment.image }}:{{ .Values.deployment.version }}" 26 | imagePullPolicy: IfNotPresent 27 | name: person-service 28 | ports: 29 | - containerPort: 8080 30 | protocol: TCP 31 | envFrom: 32 | - configMapRef: 33 | name: {{ .Release.Name }}-config 34 | - secretRef: 35 | name: {{ .Release.Name }}-db-pguser-{{ .Release.Name }}-db 36 | prefix: DB_ 37 | resources: 38 | limits: 39 | cpu: 500m 40 | memory: 256Mi 41 | requests: 42 | cpu: 5m 43 | memory: 128Mi 44 | {{- if .Values.deployment.includeHealthChecks }} 45 | readinessProbe: 46 | httpGet: 47 | path: /q/health/ready 48 | port: 8080 49 | scheme: HTTP 50 | timeoutSeconds: 1 51 | periodSeconds: 10 52 | successThreshold: 1 53 | failureThreshold: 3 54 | livenessProbe: 55 | httpGet: 56 | path: /q/health/live 57 | port: 8080 58 | scheme: HTTP 59 | timeoutSeconds: 2 60 | periodSeconds: 10 61 | successThreshold: 1 62 | failureThreshold: 3 63 | {{- end }} 64 | dnsPolicy: ClusterFirst 65 | restartPolicy: Always 66 | schedulerName: default-scheduler 67 | securityContext: {} 68 | terminationGracePeriodSeconds: 30 69 | -------------------------------------------------------------------------------- /better-helm/with-crd/templates/postgres-cluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgres-operator.crunchydata.com/v1beta1 2 | kind: PostgresCluster 3 | metadata: 4 | name: {{ .Release.Name }}-db 5 | labels: 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | spec: 8 | postgresVersion: 13 9 | instances: 10 | - name: instance1 11 | dataVolumeClaimSpec: 12 | accessModes: 13 | - "ReadWriteOnce" 14 | resources: 15 | requests: 16 | storage: 1Gi 17 | backups: 18 | pgbackrest: 19 | repos: 20 | - name: repo1 21 | volume: 22 | volumeClaimSpec: 23 | accessModes: 24 | - "ReadWriteOnce" 25 | resources: 26 | requests: 27 | storage: 1Gi -------------------------------------------------------------------------------- /better-helm/with-crd/templates/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | labels: 5 | app: person-service 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | name: {{ .Release.Name }}-route 8 | spec: 9 | port: 10 | targetPort: 8080-tcp 11 | to: 12 | kind: Service 13 | name: {{ .Release.Name }}-service 14 | weight: 100 15 | wildcardPolicy: None 16 | -------------------------------------------------------------------------------- /better-helm/with-crd/templates/security.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ .Release.Name }}-sa 5 | --- 6 | apiVersion: rbac.authorization.k8s.io/v1 7 | kind: RoleBinding 8 | metadata: 9 | name: {{ .Release.Name }}-rb 10 | subjects: 11 | - kind: ServiceAccount 12 | name: {{ .Release.Name }}-sa 13 | roleRef: 14 | kind: ClusterRole 15 | name: edit 16 | apiGroup: rbac.authorization.k8s.io 17 | 18 | 19 | -------------------------------------------------------------------------------- /better-helm/with-crd/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: person-service 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | name: {{ .Release.Name }}-service 8 | spec: 9 | ports: 10 | - name: 8080-tcp 11 | port: 8080 12 | protocol: TCP 13 | targetPort: 8080 14 | selector: 15 | deployment: {{ .Release.Name }} 16 | sessionAffinity: None 17 | type: ClusterIP 18 | -------------------------------------------------------------------------------- /better-helm/with-crd/values.yaml: -------------------------------------------------------------------------------- 1 | deployment: 2 | image: quay.io/wpernath/person-service 3 | version: v1.8.10-native 4 | replicas: 1 5 | includeHealthChecks: false 6 | 7 | config: 8 | greeting: 'Hello from better-helm-chart' 9 | 10 | -------------------------------------------------------------------------------- /better-helm/with-subchart/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: postgresql 3 | repository: https://charts.bitnami.com/bitnami 4 | version: 11.1.3 5 | digest: sha256:e743082cbe5e81348abab399e199150a279f4b6b186e9af70f863cc95d22a1c5 6 | generated: "2022-03-01T11:21:03.319266+01:00" 7 | -------------------------------------------------------------------------------- /better-helm/with-subchart/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: person-service-sub 3 | description: A better helm chart for the basic person-service used as example in the book. This release is using the Bitnami postgresql chart 4 | home: https://github.com/wpernath/book-example 5 | type: application 6 | version: 0.0.11 7 | appVersion: "v1.8.9-native" 8 | sources: 9 | - https://github.com/wpernath/book-example 10 | dependencies: 11 | - name: postgresql 12 | repository: https://charts.bitnami.com/bitnami 13 | version: 11.1.3 14 | maintainers: 15 | - name: Wanja Pernath 16 | email: wpernath@redhat.com 17 | -------------------------------------------------------------------------------- /better-helm/with-subchart/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | # Release NOTES.txt of person-service helm chart 2 | - Chart name: {{ .Chart.Name }} 3 | - Chart description: {{ .Chart.Description }} 4 | - Chart version: {{ .Chart.Version }} 5 | - App version: {{ .Chart.AppVersion }} 6 | - Release name: {{ .Release.Name }} 7 | - Release version: {{ .Release.Release }} 8 | 9 | ## Build and install this helm chart 10 | ```bash 11 | $ helm package better-helm/ -u 12 | $ helm install person-service 13 | $ helm install person-service2 14 | $ helm upgrade person-service 15 | ``` 16 | 17 | ## To learn more about this relase 18 | ```bash 19 | $ helm list 20 | $ helm history person-service 21 | ``` 22 | 23 | ## Version history 24 | - 0.0.1 Initial release 25 | - 0.0.2 release with some fixed bugs 26 | - 0.0.3 release with image coming from quay.io and better parameter substitution 27 | - 0.0.4 added NOTES.txt and a configmap 28 | - 0.0.5 added a batch Job for post-install and post-upgrade 29 | - 0.0.6 added database from CrunchyData PostgreSQL Operator 30 | - 0.0.7 added a post-install job which is filling the DB by calling the service API 31 | - 0.0.8 Made sure you can install this chart multiple times by using the release name as metadata/name entries 32 | - 0.0.9 added kubectl wait --for to wait for the service to be successfully rolled out 33 | - 0.0.10 added required operator as subscription CRD in crds folder of the chart 34 | - 0.0.11 hello accenture 35 | 36 | 37 | -------------------------------------------------------------------------------- /better-helm/with-subchart/templates/another-hook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: {{ .Release.Name }}-job 5 | annotations: 6 | "helm.sh/hook": post-install,post-upgrade 7 | "helm.sh/hook-weight": "-5" 8 | "helm.sh/hook-delete-policy": before-hook-creation #, hook-succeeded 9 | labels: 10 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 11 | spec: 12 | ttlSecondsAfterFinished: 600 13 | template: 14 | metadata: 15 | name: {{ .Chart.Name | quote }} 16 | spec: 17 | serviceAccountName: {{ .Release.Name }}-sa 18 | restartPolicy: Never 19 | containers: 20 | - name: initialize-database 21 | image: "quay.io/wpernath/kustomize-ubi@sha256:7b2ce43d1b4a0f829ba750d4d44da4a85cfb6089ca0436678aff09862e8feb83" 22 | env: 23 | - name: NAMESPACE 24 | value: {{ .Release.Namespace | default "better-helm" | quote }} 25 | - name: NAME 26 | value: {{ .Release.Name }}-service 27 | command: 28 | - bin/bash 29 | - -c 30 | - | 31 | SERVICE_URL=${NAME}.${NAMESPACE}.svc:8080/person 32 | echo "SERVICE_URL: http://${SERVICE_URL}" 33 | echo "Waiting for the service to be successfully rolled out..." 34 | 35 | # wait until the service has been successfully rolled out 36 | #kubectl wait --for=condition=Ready Pod -l deployment={{ .Release.Name }} 37 | 38 | NUM_PERSONS=$(curl -s http://$SERVICE_URL/count) 39 | 40 | if [ $NUM_PERSONS -eq 0 ]; then 41 | echo "There are no persons in the database, filling some" 42 | 43 | http -I --json POST ${SERVICE_URL} firstName=Carlos lastName=Santana salutation=Mr 44 | http -I --json POST ${SERVICE_URL} firstName=Joe lastName=Cocker salutation=Mr 45 | http -I --json POST ${SERVICE_URL} firstName=Eric lastName=Clapton salutation=Mr 46 | http -I --json POST ${SERVICE_URL} firstName=Kurt lastName=Cobain salutation=Mr 47 | 48 | else 49 | echo "There are already $NUM_PERSONS persons in the database." 50 | http -I --pretty format ${SERVICE_URL} 51 | fi 52 | -------------------------------------------------------------------------------- /better-helm/with-subchart/templates/config-map.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Release.Name }}-config 5 | labels: 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | data: 8 | APP_GREETING: |- 9 | {{ .Values.config.greeting | default "Yeah, it's openshift time" }} 10 | -------------------------------------------------------------------------------- /better-helm/with-subchart/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: person-service 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | name: {{ .Release.Name }}-deployment 8 | spec: 9 | progressDeadlineSeconds: 600 10 | replicas: {{ .Values.deployment.replicas | default 1 }} 11 | selector: 12 | matchLabels: 13 | deployment: {{ .Release.Name }} 14 | strategy: 15 | rollingUpdate: 16 | maxSurge: 25% 17 | maxUnavailable: 25% 18 | type: RollingUpdate 19 | template: 20 | metadata: 21 | labels: 22 | deployment: {{ .Release.Name }} 23 | spec: 24 | containers: 25 | - image: "{{ .Values.deployment.image }}:{{ .Values.deployment.version }}" 26 | imagePullPolicy: IfNotPresent 27 | name: person-service 28 | ports: 29 | - containerPort: 8080 30 | protocol: TCP 31 | envFrom: 32 | - configMapRef: 33 | name: {{ .Release.Name }}-config 34 | env: 35 | - name: DB_user 36 | value: wanja 37 | - name: DB_password 38 | valueFrom: 39 | secretKeyRef: 40 | name: {{ .Release.Name }}-postgresql 41 | key: password 42 | - name: DB_dbname 43 | value: wanjadb 44 | - name: DB_host 45 | value: {{ .Release.Name }}-postgresql.{{ .Release.Namespace }}.svc 46 | resources: 47 | limits: 48 | cpu: 500m 49 | memory: 256Mi 50 | requests: 51 | cpu: 5m 52 | memory: 128Mi 53 | {{- if .Values.deployment.includeHealthChecks }} 54 | readinessProbe: 55 | httpGet: 56 | path: /q/health/ready 57 | port: 8080 58 | scheme: HTTP 59 | timeoutSeconds: 1 60 | periodSeconds: 10 61 | successThreshold: 1 62 | failureThreshold: 3 63 | livenessProbe: 64 | httpGet: 65 | path: /q/health/live 66 | port: 8080 67 | scheme: HTTP 68 | timeoutSeconds: 2 69 | periodSeconds: 10 70 | successThreshold: 1 71 | failureThreshold: 3 72 | {{- end }} 73 | dnsPolicy: ClusterFirst 74 | restartPolicy: Always 75 | schedulerName: default-scheduler 76 | securityContext: {} 77 | terminationGracePeriodSeconds: 30 78 | -------------------------------------------------------------------------------- /better-helm/with-subchart/templates/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | labels: 5 | app: person-service 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | name: {{ .Release.Name }}-route 8 | spec: 9 | port: 10 | targetPort: 8080-tcp 11 | to: 12 | kind: Service 13 | name: {{ .Release.Name }}-service 14 | weight: 100 15 | wildcardPolicy: None 16 | -------------------------------------------------------------------------------- /better-helm/with-subchart/templates/security.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ .Release.Name }}-sa 5 | --- 6 | apiVersion: rbac.authorization.k8s.io/v1 7 | kind: RoleBinding 8 | metadata: 9 | name: {{ .Release.Name }}-rb 10 | subjects: 11 | - kind: ServiceAccount 12 | name: {{ .Release.Name }}-sa 13 | roleRef: 14 | kind: ClusterRole 15 | name: edit 16 | apiGroup: rbac.authorization.k8s.io 17 | 18 | 19 | -------------------------------------------------------------------------------- /better-helm/with-subchart/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: person-service 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | name: {{ .Release.Name }}-service 8 | spec: 9 | ports: 10 | - name: 8080-tcp 11 | port: 8080 12 | protocol: TCP 13 | targetPort: 8080 14 | selector: 15 | deployment: {{ .Release.Name }} 16 | sessionAffinity: None 17 | type: ClusterIP 18 | -------------------------------------------------------------------------------- /better-helm/with-subchart/values.yaml: -------------------------------------------------------------------------------- 1 | postgresql: 2 | auth: 3 | username: wanja 4 | password: wanja 5 | database: wanjadb 6 | 7 | primary: 8 | podSecurityContext: 9 | enabled: false 10 | fsGroup: "" 11 | containerSecurityContext: 12 | enabled: false 13 | runAsUser: "auto" 14 | 15 | readReplicas: 16 | podSecurityContext: 17 | enabled: false 18 | fsGroup: "" 19 | containerSecurityContext: 20 | enabled: false 21 | runAsUser: "auto" 22 | 23 | volumePermissions: 24 | enabled: false 25 | securityContext: 26 | runAsUser: "auto" 27 | 28 | deployment: 29 | image: quay.io/wpernath/person-service 30 | version: v1.8.9-native 31 | replicas: 1 32 | includeHealthChecks: false 33 | 34 | config: 35 | greeting: 'Hello from chart with subchart' 36 | 37 | -------------------------------------------------------------------------------- /better-helm/with-templ/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: person-service-templ 3 | description: A better helm chart for the basic person-service used as example in the book. The necessary postgresql database is being instanciated via Template. 4 | home: https://github.com/wpernath/book-example 5 | type: application 6 | version: 0.0.10 7 | appVersion: "v1.8.9-native" 8 | sources: 9 | - https://github.com/wpernath/book-example 10 | maintainers: 11 | - name: Wanja Pernath 12 | email: wpernath@redhat.com 13 | -------------------------------------------------------------------------------- /better-helm/with-templ/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | # Release NOTES.txt of person-service helm chart 2 | - Chart name: {{ .Chart.Name }} 3 | - Chart description: {{ .Chart.Description }} 4 | - Chart version: {{ .Chart.Version }} 5 | - App version: {{ .Chart.AppVersion }} 6 | - Release name: {{ .Release.Name }} 7 | - Release version: {{ .Release.Release }} 8 | 9 | ## Build and install this helm chart 10 | ```bash 11 | $ helm package better-helm/ -u 12 | $ helm install person-service 13 | $ helm install person-service2 14 | $ helm upgrade person-service 15 | ``` 16 | 17 | ## To learn more about this relase 18 | ```bash 19 | $ helm list 20 | $ helm history person-service 21 | ``` 22 | 23 | ## Version history 24 | - 0.0.1 Initial release 25 | - 0.0.2 release with some fixed bugs 26 | - 0.0.3 release with image coming from quay.io and better parameter substitution 27 | - 0.0.4 added NOTES.txt and a configmap 28 | - 0.0.5 added a batch Job for post-install and post-upgrade 29 | - 0.0.6 added database from CrunchyData PostgreSQL Operator 30 | - 0.0.7 added a post-install job which is filling the DB by calling the service API 31 | - 0.0.8 Made sure you can install this chart multiple times by using the release name as metadata/name entries 32 | - 0.0.9 added kubectl wait --for to wait for the service to be successfully rolled out 33 | - 0.0.10 added required operator as subscription CRD in crds folder of the chart 34 | 35 | 36 | -------------------------------------------------------------------------------- /better-helm/with-templ/templates/another-hook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: {{ .Release.Name }}-job 5 | annotations: 6 | "helm.sh/hook": post-install,post-upgrade 7 | "helm.sh/hook-weight": "-5" 8 | "helm.sh/hook-delete-policy": before-hook-creation #, hook-succeeded 9 | labels: 10 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 11 | spec: 12 | ttlSecondsAfterFinished: 600 13 | template: 14 | metadata: 15 | name: {{ .Chart.Name | quote }} 16 | spec: 17 | serviceAccountName: {{ .Release.Name }}-sa 18 | restartPolicy: Never 19 | containers: 20 | - name: initialize-database 21 | image: "quay.io/wpernath/kustomize-ubi@sha256:7b2ce43d1b4a0f829ba750d4d44da4a85cfb6089ca0436678aff09862e8feb83" 22 | env: 23 | - name: NAMESPACE 24 | value: {{ .Release.Namespace | default "better-helm" | quote }} 25 | - name: NAME 26 | value: {{ .Release.Name }}-service 27 | command: 28 | - bin/bash 29 | - -c 30 | - | 31 | SERVICE_URL=${NAME}.${NAMESPACE}.svc:8080/person 32 | echo "SERVICE_URL: http://${SERVICE_URL}" 33 | echo "Waiting for the service to be successfully rolled out..." 34 | 35 | # wait until the service has been successfully rolled out 36 | kubectl wait --for=condition=Ready Pod -l deployment={{ .Release.Name }} 37 | 38 | NUM_PERSONS=$(curl -s http://$SERVICE_URL/count) 39 | 40 | if [ $NUM_PERSONS -eq 0 ]; then 41 | echo "There are no persons in the database, filling some" 42 | 43 | http -I --json POST ${SERVICE_URL} firstName=Carlos lastName=Santana salutation=Mr 44 | http -I --json POST ${SERVICE_URL} firstName=Joe lastName=Cocker salutation=Mr 45 | http -I --json POST ${SERVICE_URL} firstName=Eric lastName=Clapton salutation=Mr 46 | http -I --json POST ${SERVICE_URL} firstName=Kurt lastName=Cobain salutation=Mr 47 | 48 | else 49 | echo "There are already $NUM_PERSONS persons in the database." 50 | http -I --pretty format ${SERVICE_URL} 51 | fi 52 | -------------------------------------------------------------------------------- /better-helm/with-templ/templates/config-map.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Release.Name }}-config 5 | labels: 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | data: 8 | APP_GREETING: |- 9 | {{ .Values.config.greeting | default "Yeah, it's openshift time" }} 10 | -------------------------------------------------------------------------------- /better-helm/with-templ/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: person-service 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | name: {{ .Release.Name }}-deployment 8 | spec: 9 | progressDeadlineSeconds: 600 10 | replicas: {{ .Values.deployment.replicas | default 1 }} 11 | selector: 12 | matchLabels: 13 | deployment: {{ .Release.Name }} 14 | strategy: 15 | rollingUpdate: 16 | maxSurge: 25% 17 | maxUnavailable: 25% 18 | type: RollingUpdate 19 | template: 20 | metadata: 21 | labels: 22 | deployment: {{ .Release.Name }} 23 | spec: 24 | containers: 25 | - image: "{{ .Values.deployment.image }}:{{ .Values.deployment.version }}" 26 | imagePullPolicy: IfNotPresent 27 | name: person-service 28 | ports: 29 | - containerPort: 8080 30 | protocol: TCP 31 | envFrom: 32 | - configMapRef: 33 | name: {{ .Release.Name }}-config 34 | env: 35 | - name: DB_host 36 | value: {{ .Release.Name }}-postgresql.{{ .Release.Namespace }}.svc 37 | - name: DB_dbname 38 | value: wanjadb 39 | - name: DB_user 40 | value: wanja 41 | - name: DB_password 42 | valueFrom: 43 | secretKeyRef: 44 | name: {{ .Release.Name }}-postgresql 45 | key: password 46 | resources: 47 | limits: 48 | cpu: 500m 49 | memory: 256Mi 50 | requests: 51 | cpu: 5m 52 | memory: 128Mi 53 | {{- if .Values.deployment.includeHealthChecks }} 54 | readinessProbe: 55 | httpGet: 56 | path: /q/health/ready 57 | port: 8080 58 | scheme: HTTP 59 | timeoutSeconds: 1 60 | periodSeconds: 10 61 | successThreshold: 1 62 | failureThreshold: 3 63 | livenessProbe: 64 | httpGet: 65 | path: /q/health/live 66 | port: 8080 67 | scheme: HTTP 68 | timeoutSeconds: 2 69 | periodSeconds: 10 70 | successThreshold: 1 71 | failureThreshold: 3 72 | {{- end }} 73 | dnsPolicy: ClusterFirst 74 | restartPolicy: Always 75 | schedulerName: default-scheduler 76 | securityContext: {} 77 | terminationGracePeriodSeconds: 30 78 | -------------------------------------------------------------------------------- /better-helm/with-templ/templates/postgres-from-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | items: 3 | - apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | annotations: 7 | template.openshift.io/expose-database_name: '{.data[''database-name'']}' 8 | template.openshift.io/expose-password: '{.data[''database-password'']}' 9 | template.openshift.io/expose-username: '{.data[''database-user'']}' 10 | labels: 11 | template: postgresql-persistent-template 12 | name: pg-{{ .Release.Name }} 13 | stringData: 14 | database-name: wanjadb 15 | database-password: wanja 16 | database-user: wanja 17 | - apiVersion: v1 18 | kind: Service 19 | metadata: 20 | annotations: 21 | template.openshift.io/expose-uri: postgres://{.spec.clusterIP}:{.spec.ports[?(.name=="postgresql")].port} 22 | labels: 23 | template: postgresql-persistent-template 24 | name: pg-{{ .Release.Name }} 25 | spec: 26 | ports: 27 | - name: postgresql 28 | nodePort: 0 29 | port: 5432 30 | protocol: TCP 31 | targetPort: 5432 32 | selector: 33 | name: pg-{{ .Release.Name }} 34 | sessionAffinity: None 35 | type: ClusterIP 36 | - apiVersion: v1 37 | kind: PersistentVolumeClaim 38 | metadata: 39 | labels: 40 | template: postgresql-persistent-template 41 | name: pg-{{ .Release.Name }} 42 | spec: 43 | accessModes: 44 | - ReadWriteOnce 45 | resources: 46 | requests: 47 | storage: 1Gi 48 | - apiVersion: apps.openshift.io/v1 49 | kind: DeploymentConfig 50 | metadata: 51 | annotations: 52 | template.alpha.openshift.io/wait-for-ready: "true" 53 | labels: 54 | template: postgresql-persistent-template 55 | name: pg-{{ .Release.Name }} 56 | spec: 57 | replicas: 1 58 | selector: 59 | name: pg-{{ .Release.Name }} 60 | strategy: 61 | type: Recreate 62 | template: 63 | metadata: 64 | labels: 65 | name: pg-{{ .Release.Name }} 66 | spec: 67 | containers: 68 | - env: 69 | - name: POSTGRESQL_USER 70 | valueFrom: 71 | secretKeyRef: 72 | key: database-user 73 | name: pg-{{ .Release.Name }} 74 | - name: POSTGRESQL_PASSWORD 75 | valueFrom: 76 | secretKeyRef: 77 | key: database-password 78 | name: pg-{{ .Release.Name }} 79 | - name: POSTGRESQL_DATABASE 80 | valueFrom: 81 | secretKeyRef: 82 | key: database-name 83 | name: pg-{{ .Release.Name }} 84 | image: ' ' 85 | imagePullPolicy: IfNotPresent 86 | livenessProbe: 87 | exec: 88 | command: 89 | - /usr/libexec/check-container 90 | - --live 91 | initialDelaySeconds: 120 92 | timeoutSeconds: 10 93 | name: postgresql 94 | ports: 95 | - containerPort: 5432 96 | protocol: TCP 97 | readinessProbe: 98 | exec: 99 | command: 100 | - /usr/libexec/check-container 101 | initialDelaySeconds: 5 102 | timeoutSeconds: 1 103 | resources: 104 | limits: 105 | memory: 512Mi 106 | securityContext: 107 | capabilities: {} 108 | privileged: false 109 | terminationMessagePath: /dev/termination-log 110 | volumeMounts: 111 | - mountPath: /var/lib/pgsql/data 112 | name: pg-{{ .Release.Name }}-data 113 | dnsPolicy: ClusterFirst 114 | restartPolicy: Always 115 | volumes: 116 | - name: pg-{{ .Release.Name }}-data 117 | persistentVolumeClaim: 118 | claimName: pg-{{ .Release.Name }} 119 | triggers: 120 | - imageChangeParams: 121 | automatic: true 122 | containerNames: 123 | - postgresql 124 | from: 125 | kind: ImageStreamTag 126 | name: postgresql:10-el8 127 | namespace: openshift 128 | lastTriggeredImage: "" 129 | type: ImageChange 130 | - type: ConfigChange 131 | kind: List 132 | -------------------------------------------------------------------------------- /better-helm/with-templ/templates/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | labels: 5 | app: person-service 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | name: {{ .Release.Name }}-route 8 | spec: 9 | port: 10 | targetPort: 8080-tcp 11 | to: 12 | kind: Service 13 | name: {{ .Release.Name }}-service 14 | weight: 100 15 | wildcardPolicy: None 16 | -------------------------------------------------------------------------------- /better-helm/with-templ/templates/security.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ .Release.Name }}-sa 5 | --- 6 | apiVersion: rbac.authorization.k8s.io/v1 7 | kind: RoleBinding 8 | metadata: 9 | name: {{ .Release.Name }}-rb 10 | subjects: 11 | - kind: ServiceAccount 12 | name: {{ .Release.Name }}-sa 13 | roleRef: 14 | kind: ClusterRole 15 | name: edit 16 | apiGroup: rbac.authorization.k8s.io 17 | 18 | 19 | -------------------------------------------------------------------------------- /better-helm/with-templ/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: person-service 6 | app.kubernetes.io/part-of: {{ .Release.Name }}-chart 7 | name: {{ .Release.Name }}-service 8 | spec: 9 | ports: 10 | - name: 8080-tcp 11 | port: 8080 12 | protocol: TCP 13 | targetPort: 8080 14 | selector: 15 | deployment: {{ .Release.Name }} 16 | sessionAffinity: None 17 | type: ClusterIP 18 | -------------------------------------------------------------------------------- /better-helm/with-templ/values.yaml: -------------------------------------------------------------------------------- 1 | deployment: 2 | image: quay.io/wpernath/person-service 3 | version: v1.8.9-native 4 | replicas: 1 5 | includeHealthChecks: false 6 | 7 | config: 8 | greeting: 'Hello from better-helm-chart' 9 | 10 | -------------------------------------------------------------------------------- /gitops/README.md: -------------------------------------------------------------------------------- 1 | # Getting GitOps. 2 | ## GitOps sample 3 | 4 | This is the sample discussed in chapter 5. It's about using Tekton and ArgoCD. 5 | 6 | You have to do the following two steps in order to setup the environment. 7 | 8 | ## 1. Setup Tekton Pipelines (the CI part) 9 | In order to initialize the Tekton pipelines, call 10 | 11 | ```bash 12 | $ ./pipeline.sh init --force --git-user \ 13 | --git-password \ 14 | --registry-user \ 15 | --registry-password 16 | namespace/book-ci created 17 | serviceaccount/pipeline-bot created 18 | rolebinding.rbac.authorization.k8s.io/book-ci-role-binding created 19 | rolebinding.rbac.authorization.k8s.io/piplinebot-rolebinding1 created 20 | rolebinding.rbac.authorization.k8s.io/piplinebot-rolebinding2 created 21 | configmap/maven-settings created 22 | service/nexus created 23 | persistentvolumeclaim/builder-pvc created 24 | persistentvolumeclaim/nexus-pv created 25 | deployment.apps/nexus created 26 | route.route.openshift.io/nexus created 27 | pipeline.tekton.dev/dev-pipeline created 28 | pipeline.tekton.dev/stage-pipeline created 29 | task.tekton.dev/create-release created 30 | task.tekton.dev/extract-kustomize-digest created 31 | task.tekton.dev/extract-quarkus-digest created 32 | task.tekton.dev/git-update-deployment created 33 | task.tekton.dev/maven-caching created 34 | secret/git-user-pass created 35 | secret/quay-push-secret created 36 | ``` 37 | 38 | This call (if given the `--force` flag) will create the following namespaces and ArgoCD applications for you: 39 | - `book-ci`: Pipelines, Tasks and a Nexus instance 40 | 41 | ## 2. Setup ArgoCD Applications (the CD part) 42 | 43 | To initialize the ArgoCD part, call the following 44 | 45 | ```bash 46 | $ oc apply -k book-example/gitops/argocd 47 | namespace/book-dev created 48 | namespace/book-stage created 49 | rolebinding.rbac.authorization.k8s.io/book-dev-role-binding created 50 | rolebinding.rbac.authorization.k8s.io/book-stage-role-binding created 51 | application.argoproj.io/book-dev created 52 | application.argoproj.io/book-stage created 53 | ``` 54 | 55 | This will create two namespaces with all roles properly setup so that Argo CD can then start initializing the environment. It will take a while until you're able to see the `person-service` with a PostgreSQL database instance up and running in those two namespaces. 56 | 57 | 58 | ## Calling the pipelines 59 | 60 | In order to call the build pipeline in `book-ci`, call 61 | 62 | ```bash 63 | $ ./pipeline.sh build -u \ 64 | -p 65 | ``` 66 | 67 | This starts the development pipeline as discussed in chapter 5. Whenever the pipeline is successfully executed, you should see an updated message on the `person-service-config` Git repository. And you should see that ArgoCD has initiated a synchronization process, which ends with a redeployment of the Quarkus application. 68 | 69 | To start the staging pipeline, call 70 | ```bash 71 | $ ./pipeline.sh stage -r v1.0.1-testing 72 | ``` 73 | 74 | This creates a new branch in Git called `release-v1.0.1-testing`, uses the current DEV image, tags it on quay.io and updates the `stage` config in git. 75 | 76 | In order to apply the changes, you need to either merge the branch directly or create a pull request and merge the changes then. 77 | -------------------------------------------------------------------------------- /gitops/argocd/book-apps.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: book-dev 5 | namespace: openshift-gitops 6 | finalizers: 7 | - resources-finalizer.argocd.argoproj.io 8 | spec: 9 | destination: 10 | namespace: book-dev 11 | server: https://kubernetes.default.svc 12 | project: default 13 | source: 14 | path: config/overlays/dev 15 | repoURL: https://github.com/wpernath/person-service-config.git 16 | targetRevision: HEAD 17 | syncPolicy: 18 | automated: 19 | prune: true 20 | syncOptions: 21 | - PruneLast=true 22 | --- 23 | apiVersion: argoproj.io/v1alpha1 24 | kind: Application 25 | metadata: 26 | name: book-stage 27 | namespace: openshift-gitops 28 | finalizers: 29 | - resources-finalizer.argocd.argoproj.io 30 | spec: 31 | destination: 32 | namespace: book-stage 33 | server: https://kubernetes.default.svc 34 | project: default 35 | source: 36 | path: config/overlays/stage 37 | repoURL: https://github.com/wpernath/person-service-config.git 38 | targetRevision: HEAD 39 | syncPolicy: 40 | automated: 41 | prune: true 42 | syncOptions: 43 | - PruneLast=true 44 | -------------------------------------------------------------------------------- /gitops/argocd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - ns.yaml 6 | - roles.yaml 7 | - book-apps.yaml 8 | 9 | 10 | -------------------------------------------------------------------------------- /gitops/argocd/ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | annotations: 5 | openshift.io/description: "" 6 | openshift.io/display-name: "DEV" 7 | labels: 8 | kubernetes.io/metadata.name: book-dev 9 | name: book-dev 10 | spec: 11 | finalizers: 12 | - kubernetes 13 | --- 14 | apiVersion: v1 15 | kind: Namespace 16 | metadata: 17 | annotations: 18 | openshift.io/description: "" 19 | openshift.io/display-name: "STAGE" 20 | labels: 21 | kubernetes.io/metadata.name: book-stage 22 | name: book-stage 23 | spec: 24 | finalizers: 25 | - kubernetes 26 | -------------------------------------------------------------------------------- /gitops/argocd/roles.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: book-dev-role-binding 5 | namespace: book-dev 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: admin 10 | subjects: 11 | - kind: ServiceAccount 12 | name: openshift-gitops-argocd-application-controller 13 | namespace: openshift-gitops 14 | --- 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: RoleBinding 17 | metadata: 18 | name: book-stage-role-binding 19 | namespace: book-stage 20 | roleRef: 21 | apiGroup: rbac.authorization.k8s.io 22 | kind: ClusterRole 23 | name: admin 24 | subjects: 25 | - kind: ServiceAccount 26 | name: openshift-gitops-argocd-application-controller 27 | namespace: openshift-gitops 28 | -------------------------------------------------------------------------------- /gitops/tekton/infra/maven-artifact-cache-pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: builder-pvc 5 | spec: 6 | volumeMode: Filesystem 7 | accessModes: 8 | - ReadWriteOnce 9 | resources: 10 | requests: 11 | storage: 10Gi 12 | -------------------------------------------------------------------------------- /gitops/tekton/infra/maven-settings-cm.yaml: -------------------------------------------------------------------------------- 1 | kind: ConfigMap 2 | apiVersion: v1 3 | metadata: 4 | name: maven-settings 5 | data: 6 | settings.xml: | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | http://nexus.book-ci.svc:8081/repository/maven-public 19 | * 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /gitops/tekton/infra/nexus.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | labels: 6 | app: nexus 7 | app.kubernetes.io/instance: nexus 8 | app.kubernetes.io/name: nexus 9 | app.kubernetes.io/part-of: nexus 10 | name: nexus 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app: nexus 16 | template: 17 | metadata: 18 | labels: 19 | app: nexus 20 | spec: 21 | containers: 22 | - name: nexus 23 | image: quay.io/siamaksade/nexus3:3.16.2 24 | env: 25 | - name: CONTEXT_PATH 26 | value: / 27 | imagePullPolicy: IfNotPresent 28 | ports: 29 | - containerPort: 8081 30 | protocol: TCP 31 | livenessProbe: 32 | exec: 33 | command: 34 | - echo 35 | - ok 36 | failureThreshold: 3 37 | initialDelaySeconds: 30 38 | periodSeconds: 10 39 | successThreshold: 1 40 | timeoutSeconds: 1 41 | readinessProbe: 42 | failureThreshold: 3 43 | httpGet: 44 | path: / 45 | port: 8081 46 | scheme: HTTP 47 | initialDelaySeconds: 30 48 | periodSeconds: 10 49 | successThreshold: 1 50 | timeoutSeconds: 1 51 | resources: 52 | limits: 53 | memory: 4Gi 54 | cpu: 2 55 | requests: 56 | memory: 512Mi 57 | cpu: 200m 58 | terminationMessagePath: /dev/termination-log 59 | volumeMounts: 60 | - mountPath: /nexus-data 61 | name: nexus-data 62 | volumes: 63 | - name: nexus-data 64 | persistentVolumeClaim: 65 | claimName: nexus-pv 66 | --- 67 | apiVersion: v1 68 | kind: Service 69 | metadata: 70 | labels: 71 | app: nexus 72 | name: nexus 73 | spec: 74 | ports: 75 | - name: 8081-tcp 76 | port: 8081 77 | protocol: TCP 78 | targetPort: 8081 79 | selector: 80 | app: nexus 81 | sessionAffinity: None 82 | type: ClusterIP 83 | --- 84 | apiVersion: route.openshift.io/v1 85 | kind: Route 86 | metadata: 87 | labels: 88 | app: nexus 89 | name: nexus 90 | spec: 91 | port: 92 | targetPort: 8081-tcp 93 | to: 94 | kind: Service 95 | name: nexus 96 | weight: 100 97 | --- 98 | apiVersion: v1 99 | kind: PersistentVolumeClaim 100 | metadata: 101 | labels: 102 | app: nexus 103 | name: nexus-pv 104 | spec: 105 | accessModes: 106 | - ReadWriteOnce 107 | resources: 108 | requests: 109 | storage: 5Gi -------------------------------------------------------------------------------- /gitops/tekton/infra/ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | annotations: 5 | openshift.io/description: "" 6 | openshift.io/display-name: "CI" 7 | labels: 8 | kubernetes.io/metadata.name: book-ci 9 | name: book-ci 10 | spec: 11 | finalizers: 12 | - kubernetes 13 | -------------------------------------------------------------------------------- /gitops/tekton/infra/roles.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: book-ci-role-binding 5 | namespace: book-ci 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: admin 10 | subjects: 11 | - kind: ServiceAccount 12 | name: openshift-gitops-argocd-application-controller 13 | namespace: openshift-gitops 14 | -------------------------------------------------------------------------------- /gitops/tekton/infra/sa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: pipeline-bot 5 | secrets: 6 | - name: git-user-pass 7 | - name: quay-push-secret 8 | --- 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: RoleBinding 11 | metadata: 12 | name: piplinebot-rolebinding1 13 | roleRef: 14 | apiGroup: rbac.authorization.k8s.io 15 | kind: ClusterRole 16 | name: pipelines-scc-clusterrole 17 | subjects: 18 | - kind: ServiceAccount 19 | name: pipeline-bot 20 | --- 21 | apiVersion: rbac.authorization.k8s.io/v1 22 | kind: RoleBinding 23 | metadata: 24 | name: piplinebot-rolebinding2 25 | roleRef: 26 | apiGroup: rbac.authorization.k8s.io 27 | kind: ClusterRole 28 | name: edit 29 | subjects: 30 | - kind: ServiceAccount 31 | name: pipeline-bot 32 | -------------------------------------------------------------------------------- /gitops/tekton/infra/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: git-user-pass 5 | annotations: 6 | tekton.dev/git-0: https://github.com # Described below 7 | type: kubernetes.io/basic-auth 8 | stringData: 9 | username: 10 | password: 11 | --- 12 | apiVersion: v1 13 | kind: Secret 14 | metadata: 15 | annotations: 16 | tekton.dev/docker-0: https://quay.io 17 | name: quay-push-secret 18 | type: kubernetes.io/basic-auth 19 | stringData: 20 | username: 21 | password: 22 | 23 | -------------------------------------------------------------------------------- /gitops/tekton/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: book-ci 4 | 5 | resources: 6 | # infra resources 7 | - infra/ns.yaml 8 | - infra/roles.yaml 9 | - infra/sa.yaml 10 | - infra/maven-artifact-cache-pvc.yaml 11 | - infra/maven-settings-cm.yaml 12 | 13 | # This is an old nexus server which will be installed in book-ci to cache artifacts 14 | # NOTE, you need to change tekton/infra/maven-settings-cm.yaml if you don't want to use 15 | # THIS nexus instance! 16 | - infra/nexus.yaml 17 | 18 | 19 | # tasks 20 | - tasks/create-release.yaml 21 | - tasks/extract-digest-from-kustomize-task.yaml 22 | - tasks/extract-digest-task.yaml 23 | - tasks/git-update-deployment.yaml 24 | - tasks/maven-task.yaml 25 | 26 | # pipelines 27 | - pipelines/dev-pipeline.yaml 28 | - pipelines/stage-release.yaml 29 | 30 | # please note, you still have to call pipeline.sh init to generate the secrets! 31 | 32 | -------------------------------------------------------------------------------- /gitops/tekton/pipeline.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This starts the pipeline new-pipeline with a given 3 | 4 | set -e -u -o pipefail 5 | declare -r SCRIPT_DIR=$(cd -P $(dirname $0) && pwd) 6 | declare COMMAND="help" 7 | 8 | GIT_URL=https://github.com/wpernath/person-service-config.git 9 | GIT_REVISION=main 10 | GIT_USER="" 11 | GIT_PASSWORD="" 12 | 13 | PIPELINE=dev-pipeline 14 | CONTEXT_DIR=person-service 15 | IMAGE_NAME=quay.io/wpernath/person-service 16 | IMAGE_USER=wpernath 17 | IMAGE_PASSWORD= 18 | TARGET_NAMESPACE=book-ci 19 | FORCE_SETUP="false" 20 | 21 | valid_command() { 22 | local fn=$1; shift 23 | [[ $(type -t "$fn") == "function" ]] 24 | } 25 | 26 | info() { 27 | printf "\n# INFO: $@\n" 28 | } 29 | 30 | err() { 31 | printf "\n# ERROR: $1\n" 32 | exit 1 33 | } 34 | 35 | command.help() { 36 | cat <<-EOF 37 | Starts a new pipeline in current kubernetes context 38 | 39 | Usage: 40 | pipeline.sh [command] [options] 41 | 42 | Examples: 43 | pipeline.sh init [--force] --git-user --git-password --registry-user --registry-password 44 | pipeline.sh build -u wpernath -p [-t ] 45 | pipeline.sh stage -r v1.2.5 [-g ] [-i ] [-t ] 46 | pipeline.sh logs [-t /tmp/secret.yaml <<-EOF 167 | apiVersion: v1 168 | kind: Secret 169 | metadata: 170 | name: git-user-pass 171 | annotations: 172 | tekton.dev/git-0: https://github.com # Described below 173 | type: kubernetes.io/basic-auth 174 | stringData: 175 | username: $GIT_USER 176 | password: $GIT_PASSWORD 177 | --- 178 | apiVersion: v1 179 | kind: Secret 180 | metadata: 181 | annotations: 182 | tekton.dev/docker-0: https://quay.io 183 | name: quay-push-secret 184 | type: kubernetes.io/basic-auth 185 | stringData: 186 | username: $IMAGE_USER 187 | password: $IMAGE_PASSWORD 188 | EOF 189 | 190 | # apply all tekton related setup 191 | if [[ "$FORCE_SETUP" == "true" ]]; then 192 | info "Creating demo setup by calling $SCRIPT_DIR/kustomization.yaml" 193 | oc apply -k "$SCRIPT_DIR" -n $TARGET_NAMESPACE 194 | 195 | while :; do 196 | oc get ns/book-ci > /dev/null && break 197 | sleep 2 198 | done 199 | fi 200 | 201 | oc apply -f /tmp/secret.yaml -n $TARGET_NAMESPACE 202 | } 203 | 204 | 205 | command.logs() { 206 | tkn pr logs -f -L -n $TARGET_NAMESPACE 207 | } 208 | 209 | command.stage() { 210 | cat > /tmp/stage-pr.yaml <<-EOF 211 | apiVersion: tekton.dev/v1beta1 212 | kind: PipelineRun 213 | metadata: 214 | name: stage-pipeline-run-$(date "+%Y%m%d-%H%M%S") 215 | spec: 216 | params: 217 | - name: release-name 218 | value: $GIT_REVISION 219 | workspaces: 220 | - name: shared-workspace 221 | persistentVolumeClaim: 222 | claimName: builder-pvc 223 | pipelineRef: 224 | name: stage-pipeline 225 | serviceAccountName: pipeline-bot 226 | EOF 227 | 228 | oc apply -f /tmp/stage-pr.yaml -n $TARGET_NAMESPACE 229 | } 230 | 231 | command.build() { 232 | cat > /tmp/pipelinerun.yaml <<-EOF 233 | apiVersion: tekton.dev/v1beta1 234 | kind: PipelineRun 235 | metadata: 236 | name: $PIPELINE-run-$(date "+%Y%m%d-%H%M%S") 237 | spec: 238 | params: 239 | - name: repo-password 240 | value: $IMAGE_PASSWORD 241 | workspaces: 242 | - name: source 243 | persistentVolumeClaim: 244 | claimName: builder-pvc 245 | - configMap: 246 | name: maven-settings 247 | name: maven-settings 248 | pipelineRef: 249 | name: dev-pipeline 250 | serviceAccountName: pipeline-bot 251 | EOF 252 | 253 | oc apply -f /tmp/pipelinerun.yaml -n $TARGET_NAMESPACE 254 | } 255 | 256 | main() { 257 | local fn="command.$COMMAND" 258 | valid_command "$fn" || { 259 | command.help 260 | err "invalid command '$COMMAND'" 261 | } 262 | 263 | cd $SCRIPT_DIR 264 | $fn 265 | return $? 266 | } 267 | 268 | main 269 | -------------------------------------------------------------------------------- /gitops/tekton/pipelines/dev-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Pipeline 3 | metadata: 4 | name: dev-pipeline 5 | spec: 6 | params: 7 | - default: 'https://github.com/wpernath/book-example.git' 8 | description: URL of the Git Repo for the source 9 | name: git-url 10 | type: string 11 | - default: main 12 | description: revision to be used 13 | name: git-revision 14 | type: string 15 | - default: person-service 16 | description: context dir within git repo where the source code is 17 | name: git-context 18 | type: string 19 | - default: 'https://github.com/wpernath/person-service-config.git' 20 | description: URL of the config repository 21 | name: config-git-url 22 | type: string 23 | - default: main 24 | description: revision of the config repo to use 25 | name: config-git-revision 26 | type: string 27 | - default: config/overlays/dev 28 | description: real config within config git to use 29 | name: kustomize-dir 30 | type: string 31 | - default: 'quay.io/wpernath/person-service:latest' 32 | description: Image to produce 33 | name: image-name 34 | type: string 35 | - default: wpernath 36 | description: 'username for image repo ' 37 | name: repo-username 38 | type: string 39 | - description: password for image repo 40 | name: repo-password 41 | type: string 42 | tasks: 43 | - name: clone-source 44 | params: 45 | - name: url 46 | value: $(params.git-url) 47 | - name: revision 48 | value: $(params.git-revision) 49 | - name: submodules 50 | value: 'true' 51 | - name: subdirectory 52 | value: the-source 53 | - name: deleteExisting 54 | value: 'true' 55 | - name: verbose 56 | value: 'false' 57 | taskRef: 58 | kind: ClusterTask 59 | name: git-clone 60 | workspaces: 61 | - name: output 62 | workspace: source 63 | - name: package 64 | params: 65 | - name: GOALS 66 | value: 67 | - package 68 | - '-DskipTests' 69 | - '-Dquarkus.container-image.build=false' 70 | - '-Dquarkus.container-image.push=false' 71 | - name: CONTEXT_DIR 72 | value: the-source/$(params.git-context) 73 | runAfter: 74 | - clone-source 75 | - clone-config 76 | taskRef: 77 | kind: Task 78 | name: maven-caching 79 | workspaces: 80 | - name: source 81 | workspace: source 82 | - name: maven-settings 83 | workspace: maven-settings 84 | - name: build-image 85 | params: 86 | - name: GOALS 87 | value: 88 | - package 89 | - '-DskipTests' 90 | - '-Dquarkus.container-image.build=true' 91 | - '-Dquarkus.container-image.push=true' 92 | - '-Dquarkus.container-image.username=$(params.repo-username)' 93 | - '-Dquarkus.container-image.password=$(params.repo-password)' 94 | - '-Dquarkus.container-image.image=$(params.image-name)' 95 | - name: CONTEXT_DIR 96 | value: the-source/$(params.git-context) 97 | runAfter: 98 | - package 99 | taskRef: 100 | kind: Task 101 | name: maven-caching 102 | workspaces: 103 | - name: source 104 | workspace: source 105 | - name: maven-settings 106 | workspace: maven-settings 107 | - name: clone-config 108 | params: 109 | - name: url 110 | value: $(params.config-git-url) 111 | - name: revision 112 | value: $(params.config-git-revision) 113 | - name: subdirectory 114 | value: the-config 115 | - name: deleteExisting 116 | value: 'true' 117 | - name: verbose 118 | value: 'false' 119 | taskRef: 120 | kind: ClusterTask 121 | name: git-clone 122 | workspaces: 123 | - name: output 124 | workspace: source 125 | - name: extract-digest-from-build 126 | params: 127 | - name: image-digest-path 128 | value: the-source/$(params.git-context)/target 129 | runAfter: 130 | - build-image 131 | taskRef: 132 | kind: Task 133 | name: extract-quarkus-digest 134 | workspaces: 135 | - name: source 136 | workspace: source 137 | - name: git-update-config 138 | params: 139 | - name: CURRENT_IMAGE 140 | value: "$(params.image-name)" 141 | - name: NEW_IMAGE 142 | value: $(params.image-name) 143 | - name: NEW_DIGEST 144 | value: $(tasks.extract-digest-from-build.results.DIGEST) 145 | - name: KUSTOMIZATION_PATH 146 | value: $(params.kustomize-dir) 147 | runAfter: 148 | - extract-digest-from-build 149 | taskRef: 150 | kind: Task 151 | name: git-update-deployment 152 | workspaces: 153 | - name: workspace 154 | workspace: source 155 | workspaces: 156 | - name: source 157 | - name: maven-settings 158 | -------------------------------------------------------------------------------- /gitops/tekton/pipelines/stage-release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Pipeline 3 | metadata: 4 | name: stage-pipeline 5 | spec: 6 | params: 7 | - name: git-url 8 | type: string 9 | description: the url of the config repo to clone 10 | default: 'https://github.com/wpernath/person-service-config.git' 11 | - name: git-revision 12 | type: string 13 | description: the revision of the config repo to clone 14 | default: main 15 | - name: target-image 16 | type: string 17 | description: The target image name of the config (without tag/digest) 18 | default: quay.io/wpernath/person-service 19 | 20 | - name: kustomize-dev 21 | type: string 22 | description: Kustomize target dev 23 | default: config/overlays/dev 24 | - name: kustomize-stage 25 | type: string 26 | description: kustomize target stage 27 | default: config/overlays/stage 28 | - name: release-name 29 | type: string 30 | description: Release name will be used as image tag in quay.io and feature branch in Git 31 | tasks: 32 | - name: git-clone 33 | params: 34 | - name: url 35 | value: $(params.git-url) 36 | - name: deleteExisting 37 | value: 'true' 38 | - name: verbose 39 | value: 'true' 40 | - name: revision 41 | value: $(params.git-revision) 42 | - name: subdirectory 43 | value: the-config 44 | taskRef: 45 | kind: ClusterTask 46 | name: git-clone 47 | workspaces: 48 | - name: output 49 | workspace: shared-workspace 50 | - name: git-branch 51 | params: 52 | - name: BASE_IMAGE 53 | value: >- 54 | docker.io/alpine/git:v2.26.2@sha256:23618034b0be9205d9cc0846eb711b12ba4c9b468efdd8a59aac1d7b1a23363f 55 | - name: GIT_SCRIPT 56 | value: cd the-config && git checkout -b release-$(params.release-name) 57 | runAfter: 58 | - git-clone 59 | taskRef: 60 | kind: ClusterTask 61 | name: git-cli 62 | workspaces: 63 | - name: source 64 | workspace: shared-workspace 65 | - name: input 66 | workspace: shared-workspace 67 | - name: extract-digest 68 | params: 69 | - name: kustomize-dir 70 | value: the-config/$(params.kustomize-dev) 71 | runAfter: 72 | - git-branch 73 | taskRef: 74 | kind: Task 75 | name: extract-kustomize-digest 76 | workspaces: 77 | - name: source 78 | workspace: shared-workspace 79 | 80 | - name: tag-image 81 | params: 82 | - name: srcImageURL 83 | value: >- 84 | docker://$(params.target-image)@$(tasks.extract-digest.results.DIGEST) 85 | - name: destImageURL 86 | value: >- 87 | docker://$(params.target-image):$(params.release-name) 88 | - name: srcTLSverify 89 | value: 'false' 90 | - name: destTLSverify 91 | value: 'false' 92 | runAfter: 93 | - extract-digest 94 | taskRef: 95 | kind: ClusterTask 96 | name: skopeo-copy 97 | workspaces: 98 | - name: images-url 99 | workspace: shared-workspace 100 | 101 | - name: update-release 102 | params: 103 | - name: kustomize-dir 104 | value: $(params.kustomize-stage) 105 | - name: release-name 106 | value: "$(params.release-name)" 107 | - name: old-image 108 | value: "$(params.target-image):latest" 109 | - name: new-image 110 | value: "$(params.target-image)" 111 | - name: digest 112 | value: "$(tasks.extract-digest.results.DIGEST)" 113 | runAfter: 114 | - tag-image 115 | taskRef: 116 | kind: Task 117 | name: create-release 118 | workspaces: 119 | - name: workspace 120 | workspace: shared-workspace 121 | 122 | workspaces: 123 | - name: shared-workspace 124 | optional: false 125 | -------------------------------------------------------------------------------- /gitops/tekton/tasks/bash-task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: bash 5 | labels: 6 | app.kubernetes.io/version: "0.4" 7 | annotations: 8 | tekton.dev/pipelines.minVersion: "0.12.1" 9 | tekton.dev/tags: build-tool 10 | spec: 11 | description: >- 12 | This task can be used to execute kustomze build scripts and to apply the changes via oc apply -f 13 | workspaces: 14 | - name: source 15 | description: The workspace holding the cloned and compiled quarkus source. 16 | params: 17 | - name: script 18 | description: Where should kustomize look for kustomization in source? 19 | steps: 20 | - name: script 21 | image: quay.io/wpernath/kustomize-ubi:latest 22 | workingDir: $(workspaces.source.path) 23 | script: | 24 | echo 25 | ls -al /tekton/creds 26 | 27 | echo 28 | ls -al ~/.docker/config.json 29 | cat ~/.docker/config.json 30 | 31 | echo 32 | #ls -al ~/.ssh 33 | #ls -al ~/.gitconfig 34 | #cat ~/.gitconfig 35 | 36 | $(params.script) 37 | ls -al ~/.ssh 38 | -------------------------------------------------------------------------------- /gitops/tekton/tasks/create-release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | annotations: 5 | tekton.dev/pipelines.minVersion: 0.12.1 6 | tekton.dev/tags: git 7 | name: create-release 8 | labels: 9 | app.kubernetes.io/version: '0.2' 10 | operator.tekton.dev/provider-type: community 11 | spec: 12 | description: This Task can be used to update image digest in a Git repo using kustomize. It requires a secret with credentials for accessing the git repo. 13 | params: 14 | - name: release-name 15 | description: The tag of the release. There is a branch with release-$(release-name) available 16 | - name: old-image 17 | - name: new-image 18 | - name: digest 19 | - name: kustomize-dir 20 | 21 | workspaces: 22 | - description: The workspace consisting of maven project. 23 | name: workspace 24 | 25 | results: 26 | - name: commit 27 | description: The commit SHA 28 | 29 | steps: 30 | - name: update-digest 31 | image: quay.io/wpernath/kustomize-ubi:latest 32 | workingDir: $(workspaces.workspace.path)/the-config/$(params.kustomize-dir) 33 | script: | 34 | kustomize edit set image $(params.old-image)=$(params.new-image):$(params.release-name)@$(params.digest) 35 | 36 | echo "##########################" 37 | echo "### kustomization.yaml ###" 38 | echo "##########################" 39 | cat kustomization.yaml 40 | 41 | - name: git-commit 42 | image: docker.io/alpine/git:v2.26.2 43 | workingDir: $(workspaces.workspace.path)/the-config 44 | script: | 45 | git config user.email "wpernath@redhat.com" 46 | git config user.name "My Tekton Bot" 47 | 48 | git status 49 | git add $(params.kustomize-dir)/kustomization.yaml 50 | git commit -m "[ci] Branch for $(params.release-name) release" 51 | 52 | 53 | git push --set-upstream origin release-$(params.release-name) 54 | 55 | RESULT_SHA="$(git rev-parse HEAD | tr -d '\n')" 56 | EXIT_CODE="$?" 57 | if [ "$EXIT_CODE" != 0 ] 58 | then 59 | exit $EXIT_CODE 60 | fi 61 | # Make sure we don't add a trailing newline to the result! 62 | echo -n "$RESULT_SHA" > $(results.commit.path) -------------------------------------------------------------------------------- /gitops/tekton/tasks/extract-digest-from-kustomize-task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: extract-kustomize-digest 5 | labels: 6 | app.kubernetes.io/version: "0.1" 7 | annotations: 8 | tekton.dev/pipelines.minVersion: "0.12.1" 9 | tekton.dev/tags: build-tool 10 | 11 | spec: 12 | params: 13 | - name: kustomize-dir 14 | description: Where to extract the DIGEST from. 15 | 16 | results: 17 | - name: DIGEST 18 | description: The image digest of the last quarkus maven build with JIB image creation 19 | 20 | workspaces: 21 | - name: source 22 | description: The workspace holding the cloned and compiled quarkus source. 23 | 24 | description: >- 25 | This task can be used to extract any Image DIGEST from a Kustomization.yaml file. 26 | 27 | steps: 28 | - name: extract-digest 29 | image: quay.io/wpernath/kustomize-ubi:latest 30 | script: | 31 | # yq eval '.images[0].digest' $(workspaces.source.path)/$(params.kustomize-dir)/kustomization.yaml 32 | 33 | DIGEST=$(yq eval '.images[0].digest' $(workspaces.source.path)/$(params.kustomize-dir)/kustomization.yaml) 34 | echo " DIGEST: $DIGEST" 35 | echo 36 | echo -n $DIGEST > $(results.DIGEST.path) 37 | 38 | 39 | -------------------------------------------------------------------------------- /gitops/tekton/tasks/extract-digest-task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: extract-quarkus-digest 5 | labels: 6 | app.kubernetes.io/version: "0.1" 7 | annotations: 8 | tekton.dev/pipelines.minVersion: "0.12.1" 9 | tekton.dev/tags: build-tool 10 | 11 | spec: 12 | params: 13 | - name: image-digest-path 14 | default: target 15 | results: 16 | - name: DIGEST 17 | description: The image digest of the last quarkus maven build with JIB image creation 18 | 19 | workspaces: 20 | - name: source 21 | description: The workspace holding the cloned and compiled quarkus source. 22 | 23 | description: >- 24 | This task can be used to extract the image digest of a quarkus maven build. Whenever you're using JIB to create the app image, 25 | quarkus will produce a target/jib-image.digest file. The content of this file will be put into DIGEST result 26 | steps: 27 | - name: extract-digest 28 | image: quay.io/wpernath/kustomize-ubi:latest 29 | script: | 30 | DIGEST=$(cat $(workspaces.source.path)/$(params.image-digest-path)/jib-image.digest) 31 | echo " DIGEST: $DIGEST" 32 | echo 33 | echo -n $DIGEST > $(results.DIGEST.path) 34 | 35 | 36 | -------------------------------------------------------------------------------- /gitops/tekton/tasks/git-update-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | annotations: 5 | tekton.dev/pipelines.minVersion: 0.12.1 6 | tekton.dev/tags: git 7 | name: git-update-deployment 8 | labels: 9 | app.kubernetes.io/version: '0.2' 10 | operator.tekton.dev/provider-type: community 11 | spec: 12 | description: This Task can be used to update image digest in a Git repo using kustomize. It requires a secret with credentials for accessing the git repo. 13 | params: 14 | - name: CURRENT_IMAGE 15 | type: string 16 | - name: NEW_IMAGE 17 | type: string 18 | - name: NEW_DIGEST 19 | type: string 20 | - name: KUSTOMIZATION_PATH 21 | type: string 22 | 23 | workspaces: 24 | - description: The workspace consisting of maven project. 25 | name: workspace 26 | 27 | results: 28 | - name: commit 29 | description: The commit SHA 30 | 31 | steps: 32 | - name: update-digest 33 | image: quay.io/wpernath/kustomize-ubi:latest 34 | workingDir: $(workspaces.workspace.path)/the-config 35 | script: | 36 | cd $(params.KUSTOMIZATION_PATH) 37 | kustomize edit set image $(params.CURRENT_IMAGE)=$(params.NEW_IMAGE)@$(params.NEW_DIGEST) 38 | 39 | cat kustomization.yaml 40 | 41 | - name: git-commit 42 | image: docker.io/alpine/git:v2.26.2 43 | workingDir: $(workspaces.workspace.path)/the-config 44 | script: | 45 | git config user.email "wpernath@redhat.com" 46 | git config user.name "My Tekton Bot" 47 | 48 | git add $(params.KUSTOMIZATION_PATH)/kustomization.yaml 49 | git commit -am "[ci] Image digest updated" 50 | 51 | git push origin HEAD:main 52 | 53 | RESULT_SHA="$(git rev-parse HEAD | tr -d '\n')" 54 | EXIT_CODE="$?" 55 | if [ "$EXIT_CODE" != 0 ] 56 | then 57 | exit $EXIT_CODE 58 | fi 59 | # Make sure we don't add a trailing newline to the result! 60 | echo -n "$RESULT_SHA" > $(results.commit.path) 61 | -------------------------------------------------------------------------------- /gitops/tekton/tasks/maven-task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: maven-caching 5 | labels: 6 | app.kubernetes.io/version: "0.4" 7 | annotations: 8 | tekton.dev/pipelines.minVersion: "0.12.1" 9 | tekton.dev/tags: build-tool 10 | spec: 11 | description: >- 12 | This Task can be used to run a Maven build. The only difference to 13 | the original one from github.com/tektoncd/catalog is that it uses an 14 | optional local maven repo path to cache all the dependencies. And it requires a 15 | a maven-settings workspace. 16 | workspaces: 17 | - name: source 18 | description: The workspace consisting of maven project. 19 | optional: false 20 | - name: maven-settings 21 | description: >- 22 | The workspace consisting of the custom maven settings 23 | provided by the user. 24 | optional: false 25 | params: 26 | - name: GOALS 27 | description: maven goals to run 28 | type: array 29 | default: 30 | - "package" 31 | - name: CONTEXT_DIR 32 | type: string 33 | description: >- 34 | The context directory within the repository for sources on 35 | which we want to execute maven goals. 36 | default: "." 37 | steps: 38 | - name: mvn-goals 39 | image: gcr.io/cloud-builders/mvn@sha256:57523fc43394d6d9d2414ee8d1c85ed7a13460cbb268c3cd16d28cfb3859e641 40 | workingDir: $(workspaces.source.path)/$(params.CONTEXT_DIR) 41 | command: ["/usr/bin/mvn"] 42 | args: 43 | - -B 44 | - -s 45 | - $(workspaces.maven-settings.path)/settings.xml 46 | - -Dmaven.repo.local=$(workspaces.source.path)/MAVEN_MIRROR 47 | - "$(params.GOALS)" -------------------------------------------------------------------------------- /helm-chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: person-service 3 | description: A helm chart for the basic person-service used as example in the book 4 | home: https://github.com/wpernath/book-example 5 | type: application 6 | version: 0.0.8 7 | appVersion: "v1.0.0-test2" 8 | sources: 9 | - https://github.com/wpernath/book-example 10 | maintainers: 11 | - name: Wanja Pernath 12 | email: wpernath@redhat.com 13 | -------------------------------------------------------------------------------- /helm-chart/README.md: -------------------------------------------------------------------------------- 1 | # Getting GitOps. 2 | ## Helm Chart sample 3 | This example is being discussed in chapter 3 of the book. -------------------------------------------------------------------------------- /helm-chart/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | # Release NOTES.txt of person-service helm chart 2 | - Chart name: {{ .Chart.Name }} 3 | - Chart description: {{ .Chart.Description }} 4 | - Chart version: {{ .Chart.Version }} 5 | - App version: {{ .Chart.AppVersion }} 6 | 7 | ## NOTES on ThIS VERSION 8 | Everything is incompatible with the old version! BE AWARE!!!!! 9 | 10 | 11 | ## Build and install this helm chart 12 | ```bash 13 | $ helm package helm-chart -u 14 | $ helm install person-service 15 | $ helm upgrade person-service 16 | ``` 17 | 18 | ## To learn more about this relase 19 | ```bash 20 | $ helm list 21 | $ helm history person-service 22 | ``` 23 | 24 | ## Version history 25 | - 0.0.1 Initial release 26 | - 0.0.2 release with some fixed bugs 27 | - 0.0.3 release with image coming from quay.io and better parameter substitution 28 | - 0.0.4 added NOTES.txt and a configmap 29 | - 0.0.5 added a batch Job for post-install and post-upgrade 30 | - 0.0.8 changed everything and it's now shiny new. 31 | 32 | -------------------------------------------------------------------------------- /helm-chart/templates/config-map.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Release.Name }}-config 5 | data: 6 | APP_GREETING: |- 7 | {{ .Values.config.greeting | default "Yeah, it's openshift time" }} 8 | -------------------------------------------------------------------------------- /helm-chart/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: {{ .Release.Name }}-deployment 7 | spec: 8 | progressDeadlineSeconds: 600 9 | replicas: {{ .Values.deployment.replicas | default 1 }} 10 | selector: 11 | matchLabels: 12 | deployment: {{ .Release.Name }} 13 | strategy: 14 | rollingUpdate: 15 | maxSurge: 25% 16 | maxUnavailable: 25% 17 | type: RollingUpdate 18 | template: 19 | metadata: 20 | labels: 21 | deployment: {{ .Release.Name }} 22 | spec: 23 | containers: 24 | - image: "{{ .Values.deployment.image }}:{{ .Values.deployment.version }}" 25 | imagePullPolicy: IfNotPresent 26 | name: person-service 27 | ports: 28 | - containerPort: 8080 29 | protocol: TCP 30 | envFrom: 31 | - configMapRef: 32 | name: {{ .Release.Name }}-config 33 | resources: {} 34 | terminationMessagePath: /dev/termination-log 35 | terminationMessagePolicy: File 36 | {{- if .Values.deployment.includeHealthChecks }} 37 | readinessProbe: 38 | httpGet: 39 | path: /q/health/ready 40 | port: 8080 41 | scheme: HTTP 42 | timeoutSeconds: 1 43 | periodSeconds: 10 44 | successThreshold: 1 45 | failureThreshold: 3 46 | livenessProbe: 47 | httpGet: 48 | path: /q/health/live 49 | port: 8080 50 | scheme: HTTP 51 | timeoutSeconds: 2 52 | periodSeconds: 10 53 | successThreshold: 1 54 | failureThreshold: 3 55 | {{- end }} 56 | dnsPolicy: ClusterFirst 57 | restartPolicy: Always 58 | schedulerName: default-scheduler 59 | securityContext: {} 60 | terminationGracePeriodSeconds: 30 61 | -------------------------------------------------------------------------------- /helm-chart/templates/post-install-hook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: "{{ .Release.Name }}-job" 5 | labels: 6 | app.kubernetes.io/managed-by: {{ .Release.Service | quote }} 7 | app.kubernetes.io/instance: {{ .Chart.Name | quote }} 8 | app.kubernetes.io/version: {{ .Chart.AppVersion }} 9 | "helm.sh/chart": "{{ .Chart.Name }}-{{ .Chart.Version }}" 10 | annotations: 11 | # This is what defines this resource as a hook. Without this line, the 12 | # job is considered part of the release. 13 | "helm.sh/hook": post-install,post-upgrade 14 | "helm.sh/hook-weight": "-5" 15 | "helm.sh/hook-delete-policy": before-hook-creation, hook-succeeded 16 | spec: 17 | ttlSecondsAfterFinished: 60 18 | template: 19 | metadata: 20 | name: {{ .Chart.Name }} 21 | labels: 22 | "helm.sh/chart": "{{ .Chart.Name }}-{{ .Chart.Version }}" 23 | spec: 24 | restartPolicy: Never 25 | containers: 26 | - name: post-install-job 27 | image: "registry.access.redhat.com/ubi8/ubi-minimal:latest" 28 | command: 29 | - /bin/sh 30 | - -c 31 | - | 32 | echo "WELCOME TO '{{ .Chart.Name }}-{{ .Chart.Version }}' " 33 | echo "-------------------------------------------------" 34 | echo "Here we could now do initialization work." 35 | echo "Like filling our DB with some data or what's so ever" 36 | echo "..." 37 | 38 | sleep 10 39 | 40 | 41 | -------------------------------------------------------------------------------- /helm-chart/templates/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: {{ .Release.Name }}-route 7 | spec: 8 | port: 9 | targetPort: 8080-tcp 10 | to: 11 | kind: Service 12 | name: {{ .Release.Name }}-service 13 | weight: 100 14 | wildcardPolicy: None 15 | -------------------------------------------------------------------------------- /helm-chart/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: {{ .Release.Name }}-service 7 | spec: 8 | ports: 9 | - name: 8080-tcp 10 | port: 8080 11 | protocol: TCP 12 | targetPort: 8080 13 | selector: 14 | deployment: {{ .Release.Name }} 15 | sessionAffinity: None 16 | type: ClusterIP 17 | -------------------------------------------------------------------------------- /helm-chart/values.yaml: -------------------------------------------------------------------------------- 1 | deployment: 2 | image: quay.io/wpernath/person-service 3 | version: v1.0.0-test 4 | replicas: 4 5 | includeHealthChecks: false 6 | 7 | config: 8 | greeting: 'Hello dear new hires from our partners.' 9 | 10 | -------------------------------------------------------------------------------- /kube-operator/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | bin 9 | target 10 | 11 | # editor and IDE paraphernalia 12 | .idea 13 | *.swp 14 | *.swo 15 | *~ 16 | -------------------------------------------------------------------------------- /kube-operator/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM quay.io/operator-framework/helm-operator:v1.13.1 3 | 4 | ENV HOME=/opt/helm 5 | COPY watches.yaml ${HOME}/watches.yaml 6 | COPY helm-charts ${HOME}/helm-charts 7 | WORKDIR ${HOME} 8 | -------------------------------------------------------------------------------- /kube-operator/Makefile: -------------------------------------------------------------------------------- 1 | # VERSION defines the project version for the bundle. 2 | # Update this value when you upgrade the version of your project. 3 | # To re-generate a bundle for another specific version without changing the standard setup, you can: 4 | # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) 5 | # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) 6 | VERSION ?= 0.0.3 7 | 8 | # CHANNELS define the bundle channels used in the bundle. 9 | # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") 10 | # To re-generate a bundle for other specific channels without changing the standard setup, you can: 11 | # - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable) 12 | # - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable") 13 | ifneq ($(origin CHANNELS), undefined) 14 | BUNDLE_CHANNELS := --channels=$(CHANNELS) 15 | endif 16 | 17 | # DEFAULT_CHANNEL defines the default channel used in the bundle. 18 | # Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable") 19 | # To re-generate a bundle for any other default channel without changing the default setup, you can: 20 | # - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable) 21 | # - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable") 22 | ifneq ($(origin DEFAULT_CHANNEL), undefined) 23 | BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) 24 | endif 25 | BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) 26 | 27 | # IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images. 28 | # This variable is used to construct full image tags for bundle and catalog images. 29 | # 30 | # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both 31 | # wanja.org/person-service-operator-bundle:$VERSION and wanja.org/person-service-operator-catalog:$VERSION. 32 | IMAGE_TAG_BASE ?= quay.io/wpernath/person-service-operator 33 | 34 | # BUNDLE_IMG defines the image:tag used for the bundle. 35 | # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) 36 | BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) 37 | 38 | # Image URL to use all building/pushing image targets 39 | IMG ?= $(IMAGE_TAG_BASE):$(VERSION) 40 | 41 | all: docker-build 42 | 43 | ##@ General 44 | 45 | # The help target prints out all targets with their descriptions organized 46 | # beneath their categories. The categories are represented by '##@' and the 47 | # target descriptions by '##'. The awk commands is responsible for reading the 48 | # entire set of makefiles included in this invocation, looking for lines of the 49 | # file as xyz: ## something, and then pretty-format the target and help. Then, 50 | # if there's a line with ##@ something, that gets pretty-printed as a category. 51 | # More info on the usage of ANSI control characters for terminal formatting: 52 | # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters 53 | # More info on the awk command: 54 | # http://linuxcommand.org/lc3_adv_awk.php 55 | 56 | help: ## Display this help. 57 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 58 | 59 | ##@ Build 60 | 61 | run: helm-operator ## Run against the configured Kubernetes cluster in ~/.kube/config 62 | $(HELM_OPERATOR) run 63 | 64 | docker-build: ## Build docker image with the manager. 65 | docker build -t ${IMG} . 66 | 67 | docker-push: ## Push docker image with the manager. 68 | docker push ${IMG} 69 | 70 | ##@ Deployment 71 | 72 | install: kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. 73 | $(KUSTOMIZE) build config/crd | kubectl apply -f - 74 | 75 | uninstall: kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. 76 | $(KUSTOMIZE) build config/crd | kubectl delete -f - 77 | 78 | deploy: kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. 79 | cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} 80 | $(KUSTOMIZE) build config/default | kubectl apply -f - 81 | 82 | undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. 83 | $(KUSTOMIZE) build config/default | kubectl delete -f - 84 | 85 | OS := $(shell uname -s | tr '[:upper:]' '[:lower:]') 86 | ARCH := $(shell uname -m | sed 's/x86_64/amd64/') 87 | 88 | .PHONY: kustomize 89 | KUSTOMIZE = $(shell pwd)/bin/kustomize 90 | kustomize: ## Download kustomize locally if necessary. 91 | ifeq (,$(wildcard $(KUSTOMIZE))) 92 | ifeq (,$(shell which kustomize 2>/dev/null)) 93 | @{ \ 94 | set -e ;\ 95 | mkdir -p $(dir $(KUSTOMIZE)) ;\ 96 | curl -sSLo - https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.8.7/kustomize_v3.8.7_$(OS)_$(ARCH).tar.gz | \ 97 | tar xzf - -C bin/ ;\ 98 | } 99 | else 100 | KUSTOMIZE = $(shell which kustomize) 101 | endif 102 | endif 103 | 104 | .PHONY: helm-operator 105 | HELM_OPERATOR = $(shell pwd)/bin/helm-operator 106 | helm-operator: ## Download helm-operator locally if necessary, preferring the $(pwd)/bin path over global if both exist. 107 | ifeq (,$(wildcard $(HELM_OPERATOR))) 108 | ifeq (,$(shell which helm-operator 2>/dev/null)) 109 | @{ \ 110 | set -e ;\ 111 | mkdir -p $(dir $(HELM_OPERATOR)) ;\ 112 | curl -sSLo $(HELM_OPERATOR) https://github.com/operator-framework/operator-sdk/releases/download/v1.13.1/helm-operator_$(OS)_$(ARCH) ;\ 113 | chmod +x $(HELM_OPERATOR) ;\ 114 | } 115 | else 116 | HELM_OPERATOR = $(shell which helm-operator) 117 | endif 118 | endif 119 | 120 | .PHONY: bundle 121 | bundle: kustomize ## Generate bundle manifests and metadata, then validate generated files. 122 | operator-sdk generate kustomize manifests -q 123 | cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG) 124 | $(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) 125 | operator-sdk bundle validate ./bundle 126 | 127 | .PHONY: bundle-build 128 | bundle-build: ## Build the bundle image. 129 | docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . 130 | 131 | .PHONY: bundle-push 132 | bundle-push: ## Push the bundle image. 133 | $(MAKE) docker-push IMG=$(BUNDLE_IMG) 134 | 135 | .PHONY: opm 136 | OPM = ./bin/opm 137 | opm: ## Download opm locally if necessary. 138 | ifeq (,$(wildcard $(OPM))) 139 | ifeq (,$(shell which opm 2>/dev/null)) 140 | @{ \ 141 | set -e ;\ 142 | mkdir -p $(dir $(OPM)) ;\ 143 | curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.1/$(OS)-$(ARCH)-opm ;\ 144 | chmod +x $(OPM) ;\ 145 | } 146 | else 147 | OPM = $(shell which opm) 148 | endif 149 | endif 150 | 151 | # A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). 152 | # These images MUST exist in a registry and be pull-able. 153 | BUNDLE_IMGS ?= $(BUNDLE_IMG) 154 | 155 | # The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). 156 | CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION) 157 | 158 | # Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. 159 | ifneq ($(origin CATALOG_BASE_IMG), undefined) 160 | FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) 161 | endif 162 | 163 | # Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. 164 | # This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: 165 | # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator 166 | .PHONY: catalog-build 167 | catalog-build: opm ## Build a catalog image. 168 | $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) 169 | 170 | # Push the catalog image. 171 | .PHONY: catalog-push 172 | catalog-push: ## Push a catalog image. 173 | $(MAKE) docker-push IMG=$(CATALOG_IMG) 174 | -------------------------------------------------------------------------------- /kube-operator/PROJECT: -------------------------------------------------------------------------------- 1 | domain: wanja.org 2 | layout: 3 | - helm.sdk.operatorframework.io/v1 4 | plugins: 5 | manifests.sdk.operatorframework.io/v2: {} 6 | scorecard.sdk.operatorframework.io/v2: {} 7 | projectName: person-service-operator 8 | resources: 9 | - api: 10 | crdVersion: v1 11 | namespaced: true 12 | domain: wanja.org 13 | group: charts 14 | kind: PersonService 15 | version: v1alpha1 16 | version: "3" 17 | -------------------------------------------------------------------------------- /kube-operator/README.md: -------------------------------------------------------------------------------- 1 | # Getting GitOps. 2 | ## Kubernetes Operator sample 3 | This example is being discussed in chapter 3 of the book. -------------------------------------------------------------------------------- /kube-operator/bundle.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | # Core bundle labels. 4 | LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 5 | LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ 6 | LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ 7 | LABEL operators.operatorframework.io.bundle.package.v1=person-service-operator 8 | LABEL operators.operatorframework.io.bundle.channels.v1=alpha 9 | LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.13.1 10 | LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 11 | LABEL operators.operatorframework.io.metrics.project_layout=helm.sdk.operatorframework.io/v1 12 | 13 | # Labels for testing. 14 | LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 15 | LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ 16 | 17 | # Copy files to locations specified by labels. 18 | COPY bundle/manifests /manifests/ 19 | COPY bundle/metadata /metadata/ 20 | COPY bundle/tests/scorecard /tests/scorecard/ 21 | -------------------------------------------------------------------------------- /kube-operator/bundle/manifests/charts.wanja.org_personservices.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | creationTimestamp: null 5 | name: personservices.charts.wanja.org 6 | spec: 7 | group: charts.wanja.org 8 | names: 9 | kind: PersonService 10 | listKind: PersonServiceList 11 | plural: personservices 12 | singular: personservice 13 | scope: Namespaced 14 | versions: 15 | - name: v1alpha1 16 | schema: 17 | openAPIV3Schema: 18 | description: PersonService is the Schema for the personservices API 19 | properties: 20 | apiVersion: 21 | description: 'APIVersion defines the versioned schema of this representation 22 | of an object. Servers should convert recognized schemas to the latest 23 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 24 | type: string 25 | kind: 26 | description: 'Kind is a string value representing the REST resource this 27 | object represents. Servers may infer this from the endpoint the client 28 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | description: Spec defines the desired state of PersonService 34 | type: object 35 | x-kubernetes-preserve-unknown-fields: true 36 | status: 37 | description: Status defines the observed state of PersonService 38 | type: object 39 | x-kubernetes-preserve-unknown-fields: true 40 | type: object 41 | served: true 42 | storage: true 43 | subresources: 44 | status: {} 45 | status: 46 | acceptedNames: 47 | kind: "" 48 | plural: "" 49 | conditions: null 50 | storedVersions: null 51 | -------------------------------------------------------------------------------- /kube-operator/bundle/manifests/person-service-operator-controller-manager-metrics-service_v1_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | control-plane: controller-manager 7 | name: person-service-operator-controller-manager-metrics-service 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | protocol: TCP 13 | targetPort: https 14 | selector: 15 | control-plane: controller-manager 16 | status: 17 | loadBalancer: {} 18 | -------------------------------------------------------------------------------- /kube-operator/bundle/manifests/person-service-operator-manager-config_v1_configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | controller_manager_config.yaml: | 4 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 5 | kind: ControllerManagerConfig 6 | health: 7 | healthProbeBindAddress: :8081 8 | metrics: 9 | bindAddress: 127.0.0.1:8080 10 | 11 | leaderElection: 12 | leaderElect: true 13 | resourceName: 811c9dc5.wanja.org 14 | kind: ConfigMap 15 | metadata: 16 | name: person-service-operator-manager-config 17 | -------------------------------------------------------------------------------- /kube-operator/bundle/manifests/person-service-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | name: person-service-operator-metrics-reader 6 | rules: 7 | - nonResourceURLs: 8 | - /metrics 9 | verbs: 10 | - get 11 | -------------------------------------------------------------------------------- /kube-operator/bundle/manifests/person-service-operator.clusterserviceversion.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: ClusterServiceVersion 3 | metadata: 4 | annotations: 5 | alm-examples: |- 6 | [ 7 | { 8 | "apiVersion": "charts.wanja.org/v1alpha1", 9 | "kind": "PersonService", 10 | "metadata": { 11 | "name": "personservice-sample" 12 | }, 13 | "spec": { 14 | "config": { 15 | "greeting": "Operator to developer, what's going on out there?" 16 | }, 17 | "deployment": { 18 | "image": "quay.io/wpernath/person-service", 19 | "includeHealthChecks": false, 20 | "replicas": 2, 21 | "version": "v1.0.0-test" 22 | } 23 | } 24 | } 25 | ] 26 | capabilities: Basic Install 27 | operators.operatorframework.io/builder: operator-sdk-v1.13.1 28 | operators.operatorframework.io/project_layout: helm.sdk.operatorframework.io/v1 29 | name: person-service-operator.v0.0.3 30 | namespace: placeholder 31 | spec: 32 | apiservicedefinitions: {} 33 | customresourcedefinitions: 34 | owned: 35 | - kind: PersonService 36 | name: personservices.charts.wanja.org 37 | version: v1alpha1 38 | description: This is a helm based operator for the PersonService 39 | displayName: PersonService 40 | icon: 41 | - base64data: "" 42 | mediatype: "" 43 | install: 44 | spec: 45 | clusterPermissions: 46 | - rules: 47 | - apiGroups: 48 | - "" 49 | resources: 50 | - namespaces 51 | verbs: 52 | - get 53 | - apiGroups: 54 | - "" 55 | resources: 56 | - secrets 57 | verbs: 58 | - '*' 59 | - apiGroups: 60 | - "" 61 | resources: 62 | - events 63 | verbs: 64 | - create 65 | - apiGroups: 66 | - charts.wanja.org 67 | resources: 68 | - personservices 69 | - personservices/status 70 | - personservices/finalizers 71 | verbs: 72 | - create 73 | - delete 74 | - get 75 | - list 76 | - patch 77 | - update 78 | - watch 79 | - apiGroups: 80 | - "" 81 | resources: 82 | - configmaps 83 | - services 84 | verbs: 85 | - '*' 86 | - apiGroups: 87 | - apps 88 | resources: 89 | - deployments 90 | verbs: 91 | - '*' 92 | - apiGroups: 93 | - route.openshift.io 94 | resources: 95 | - routes 96 | verbs: 97 | - '*' 98 | - apiGroups: 99 | - authentication.k8s.io 100 | resources: 101 | - tokenreviews 102 | verbs: 103 | - create 104 | - apiGroups: 105 | - authorization.k8s.io 106 | resources: 107 | - subjectaccessreviews 108 | verbs: 109 | - create 110 | serviceAccountName: person-service-operator-controller-manager 111 | deployments: 112 | - name: person-service-operator-controller-manager 113 | spec: 114 | replicas: 1 115 | selector: 116 | matchLabels: 117 | control-plane: controller-manager 118 | strategy: {} 119 | template: 120 | metadata: 121 | labels: 122 | control-plane: controller-manager 123 | spec: 124 | containers: 125 | - args: 126 | - --secure-listen-address=0.0.0.0:8443 127 | - --upstream=http://127.0.0.1:8080/ 128 | - --logtostderr=true 129 | - --v=10 130 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 131 | name: kube-rbac-proxy 132 | ports: 133 | - containerPort: 8443 134 | name: https 135 | protocol: TCP 136 | resources: {} 137 | - args: 138 | - --health-probe-bind-address=:8081 139 | - --metrics-bind-address=127.0.0.1:8080 140 | - --leader-elect 141 | - --leader-election-id=person-service-operator 142 | image: quay.io/wpernath/person-service-operator:0.0.3 143 | livenessProbe: 144 | httpGet: 145 | path: /healthz 146 | port: 8081 147 | initialDelaySeconds: 15 148 | periodSeconds: 20 149 | name: manager 150 | readinessProbe: 151 | httpGet: 152 | path: /readyz 153 | port: 8081 154 | initialDelaySeconds: 5 155 | periodSeconds: 10 156 | resources: 157 | limits: 158 | cpu: 200m 159 | memory: 100Mi 160 | requests: 161 | cpu: 100m 162 | memory: 60Mi 163 | securityContext: 164 | allowPrivilegeEscalation: false 165 | securityContext: 166 | runAsNonRoot: true 167 | serviceAccountName: person-service-operator-controller-manager 168 | terminationGracePeriodSeconds: 10 169 | permissions: 170 | - rules: 171 | - apiGroups: 172 | - "" 173 | resources: 174 | - configmaps 175 | verbs: 176 | - get 177 | - list 178 | - watch 179 | - create 180 | - update 181 | - patch 182 | - delete 183 | - apiGroups: 184 | - coordination.k8s.io 185 | resources: 186 | - leases 187 | verbs: 188 | - get 189 | - list 190 | - watch 191 | - create 192 | - update 193 | - patch 194 | - delete 195 | - apiGroups: 196 | - "" 197 | resources: 198 | - events 199 | verbs: 200 | - create 201 | - patch 202 | serviceAccountName: person-service-operator-controller-manager 203 | strategy: deployment 204 | installModes: 205 | - supported: false 206 | type: OwnNamespace 207 | - supported: false 208 | type: SingleNamespace 209 | - supported: false 210 | type: MultiNamespace 211 | - supported: true 212 | type: AllNamespaces 213 | keywords: 214 | - person-service 215 | - helm 216 | - operator 217 | links: 218 | - name: Person Service Operator 219 | url: https://person-service-operator.domain 220 | maintainers: 221 | - email: wanja@redhat.com 222 | name: wanja 223 | maturity: alpha 224 | provider: 225 | name: Wanja Pernath 226 | version: 0.0.3 227 | -------------------------------------------------------------------------------- /kube-operator/bundle/metadata/annotations.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | # Core bundle annotations. 3 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1 4 | operators.operatorframework.io.bundle.manifests.v1: manifests/ 5 | operators.operatorframework.io.bundle.metadata.v1: metadata/ 6 | operators.operatorframework.io.bundle.package.v1: person-service-operator 7 | operators.operatorframework.io.bundle.channels.v1: alpha 8 | operators.operatorframework.io.metrics.builder: operator-sdk-v1.13.1 9 | operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 10 | operators.operatorframework.io.metrics.project_layout: helm.sdk.operatorframework.io/v1 11 | 12 | # Annotations for testing. 13 | operators.operatorframework.io.test.mediatype.v1: scorecard+v1 14 | operators.operatorframework.io.test.config.v1: tests/scorecard/ 15 | -------------------------------------------------------------------------------- /kube-operator/bundle/tests/scorecard/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: 8 | - entrypoint: 9 | - scorecard-test 10 | - basic-check-spec 11 | image: quay.io/operator-framework/scorecard-test:v1.13.1 12 | labels: 13 | suite: basic 14 | test: basic-check-spec-test 15 | storage: 16 | spec: 17 | mountPath: {} 18 | - entrypoint: 19 | - scorecard-test 20 | - olm-bundle-validation 21 | image: quay.io/operator-framework/scorecard-test:v1.13.1 22 | labels: 23 | suite: olm 24 | test: olm-bundle-validation-test 25 | storage: 26 | spec: 27 | mountPath: {} 28 | - entrypoint: 29 | - scorecard-test 30 | - olm-crds-have-validation 31 | image: quay.io/operator-framework/scorecard-test:v1.13.1 32 | labels: 33 | suite: olm 34 | test: olm-crds-have-validation-test 35 | storage: 36 | spec: 37 | mountPath: {} 38 | - entrypoint: 39 | - scorecard-test 40 | - olm-crds-have-resources 41 | image: quay.io/operator-framework/scorecard-test:v1.13.1 42 | labels: 43 | suite: olm 44 | test: olm-crds-have-resources-test 45 | storage: 46 | spec: 47 | mountPath: {} 48 | - entrypoint: 49 | - scorecard-test 50 | - olm-spec-descriptors 51 | image: quay.io/operator-framework/scorecard-test:v1.13.1 52 | labels: 53 | suite: olm 54 | test: olm-spec-descriptors-test 55 | storage: 56 | spec: 57 | mountPath: {} 58 | - entrypoint: 59 | - scorecard-test 60 | - olm-status-descriptors 61 | image: quay.io/operator-framework/scorecard-test:v1.13.1 62 | labels: 63 | suite: olm 64 | test: olm-status-descriptors-test 65 | storage: 66 | spec: 67 | mountPath: {} 68 | storage: 69 | spec: 70 | mountPath: {} 71 | -------------------------------------------------------------------------------- /kube-operator/config/crd/bases/charts.wanja.org_personservices.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: personservices.charts.wanja.org 6 | spec: 7 | group: charts.wanja.org 8 | names: 9 | kind: PersonService 10 | listKind: PersonServiceList 11 | plural: personservices 12 | singular: personservice 13 | scope: Namespaced 14 | versions: 15 | - name: v1alpha1 16 | schema: 17 | openAPIV3Schema: 18 | description: PersonService is the Schema for the personservices API 19 | properties: 20 | apiVersion: 21 | description: 'APIVersion defines the versioned schema of this representation 22 | of an object. Servers should convert recognized schemas to the latest 23 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 24 | type: string 25 | kind: 26 | description: 'Kind is a string value representing the REST resource this 27 | object represents. Servers may infer this from the endpoint the client 28 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 29 | type: string 30 | metadata: 31 | type: object 32 | spec: 33 | description: Spec defines the desired state of PersonService 34 | type: object 35 | x-kubernetes-preserve-unknown-fields: true 36 | status: 37 | description: Status defines the observed state of PersonService 38 | type: object 39 | x-kubernetes-preserve-unknown-fields: true 40 | type: object 41 | served: true 42 | storage: true 43 | subresources: 44 | status: {} 45 | -------------------------------------------------------------------------------- /kube-operator/config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/charts.wanja.org_personservices.yaml 6 | #+kubebuilder:scaffold:crdkustomizeresource 7 | -------------------------------------------------------------------------------- /kube-operator/config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: person-service-operator-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: person-service-operator- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | bases: 16 | - ../crd 17 | - ../rbac 18 | - ../manager 19 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 20 | #- ../prometheus 21 | 22 | patchesStrategicMerge: 23 | # Protect the /metrics endpoint by putting it behind auth. 24 | # If you want your controller-manager to expose the /metrics 25 | # endpoint w/o any authn/z, please comment the following line. 26 | - manager_auth_proxy_patch.yaml 27 | 28 | # Mount the controller config file for loading manager configurations 29 | # through a ComponentConfig type 30 | #- manager_config_patch.yaml 31 | -------------------------------------------------------------------------------- /kube-operator/config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | protocol: TCP 22 | name: https 23 | - name: manager 24 | args: 25 | - "--health-probe-bind-address=:8081" 26 | - "--metrics-bind-address=127.0.0.1:8080" 27 | - "--leader-elect" 28 | - "--leader-election-id=person-service-operator" 29 | -------------------------------------------------------------------------------- /kube-operator/config/default/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | args: 12 | - "--config=controller_manager_config.yaml" 13 | volumeMounts: 14 | - name: manager-config 15 | mountPath: /controller_manager_config.yaml 16 | subPath: controller_manager_config.yaml 17 | volumes: 18 | - name: manager-config 19 | configMap: 20 | name: manager-config 21 | -------------------------------------------------------------------------------- /kube-operator/config/manager/controller_manager_config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 2 | kind: ControllerManagerConfig 3 | health: 4 | healthProbeBindAddress: :8081 5 | metrics: 6 | bindAddress: 127.0.0.1:8080 7 | 8 | leaderElection: 9 | leaderElect: true 10 | resourceName: 811c9dc5.wanja.org 11 | -------------------------------------------------------------------------------- /kube-operator/config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | configMapGenerator: 8 | - files: 9 | - controller_manager_config.yaml 10 | name: manager-config 11 | apiVersion: kustomize.config.k8s.io/v1beta1 12 | kind: Kustomization 13 | images: 14 | - name: controller 15 | newName: quay.io/wpernath/person-service-operator 16 | newTag: 0.0.3 17 | -------------------------------------------------------------------------------- /kube-operator/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 | securityContext: 26 | runAsNonRoot: true 27 | containers: 28 | - args: 29 | - --leader-elect 30 | - --leader-election-id=person-service-operator 31 | image: controller:latest 32 | name: manager 33 | securityContext: 34 | allowPrivilegeEscalation: false 35 | livenessProbe: 36 | httpGet: 37 | path: /healthz 38 | port: 8081 39 | initialDelaySeconds: 15 40 | periodSeconds: 20 41 | readinessProbe: 42 | httpGet: 43 | path: /readyz 44 | port: 8081 45 | initialDelaySeconds: 5 46 | periodSeconds: 10 47 | resources: 48 | limits: 49 | cpu: 200m 50 | memory: 100Mi 51 | requests: 52 | cpu: 100m 53 | memory: 60Mi 54 | serviceAccountName: controller-manager 55 | terminationGracePeriodSeconds: 10 56 | -------------------------------------------------------------------------------- /kube-operator/config/manifests/bases/person-service-operator.clusterserviceversion.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: ClusterServiceVersion 3 | metadata: 4 | annotations: 5 | alm-examples: '[]' 6 | capabilities: Basic Install 7 | name: person-service-operator.v0.0.0 8 | namespace: placeholder 9 | spec: 10 | apiservicedefinitions: {} 11 | customresourcedefinitions: {} 12 | description: This is a helm based operator for the PersonService 13 | displayName: PersonService 14 | icon: 15 | - base64data: "" 16 | mediatype: "" 17 | install: 18 | spec: 19 | deployments: null 20 | strategy: "" 21 | installModes: 22 | - supported: false 23 | type: OwnNamespace 24 | - supported: false 25 | type: SingleNamespace 26 | - supported: false 27 | type: MultiNamespace 28 | - supported: true 29 | type: AllNamespaces 30 | keywords: 31 | - person-service 32 | - helm 33 | - operator 34 | links: 35 | - name: Person Service Operator 36 | url: https://person-service-operator.domain 37 | maintainers: 38 | - email: wanja@redhat.com 39 | name: wanja 40 | maturity: alpha 41 | provider: 42 | name: Wanja Pernath 43 | version: 0.0.0 44 | -------------------------------------------------------------------------------- /kube-operator/config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - bases/person-service-operator.clusterserviceversion.yaml 5 | - ../default 6 | - ../samples 7 | - ../scorecard 8 | -------------------------------------------------------------------------------- /kube-operator/config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /kube-operator/config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | scheme: https 15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 16 | tlsConfig: 17 | insecureSkipVerify: true 18 | selector: 19 | matchLabels: 20 | control-plane: controller-manager 21 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | protocol: TCP 13 | targetPort: https 14 | selector: 15 | control-plane: controller-manager 16 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # Comment the following 4 lines if you want to disable 13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 14 | # which protects your /metrics endpoint. 15 | - auth_proxy_service.yaml 16 | - auth_proxy_role.yaml 17 | - auth_proxy_role_binding.yaml 18 | - auth_proxy_client_clusterrole.yaml 19 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - coordination.k8s.io 21 | resources: 22 | - leases 23 | verbs: 24 | - get 25 | - list 26 | - watch 27 | - create 28 | - update 29 | - patch 30 | - delete 31 | - apiGroups: 32 | - "" 33 | resources: 34 | - events 35 | verbs: 36 | - create 37 | - patch 38 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/personservice_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit personservices. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: personservice-editor-role 6 | rules: 7 | - apiGroups: 8 | - charts.wanja.org 9 | resources: 10 | - personservices 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - charts.wanja.org 21 | resources: 22 | - personservices/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/personservice_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view personservices. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: personservice-viewer-role 6 | rules: 7 | - apiGroups: 8 | - charts.wanja.org 9 | resources: 10 | - personservices 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - charts.wanja.org 17 | resources: 18 | - personservices/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: manager-role 5 | rules: 6 | ## 7 | ## Base operator rules 8 | ## 9 | # We need to get namespaces so the operator can read namespaces to ensure they exist 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - namespaces 14 | verbs: 15 | - get 16 | # We need to manage Helm release secrets 17 | - apiGroups: 18 | - "" 19 | resources: 20 | - secrets 21 | verbs: 22 | - "*" 23 | # We need to create events on CRs about things happening during reconciliation 24 | - apiGroups: 25 | - "" 26 | resources: 27 | - events 28 | verbs: 29 | - create 30 | 31 | ## 32 | ## Rules for charts.wanja.org/v1alpha1, Kind: PersonService 33 | ## 34 | - apiGroups: 35 | - charts.wanja.org 36 | resources: 37 | - personservices 38 | - personservices/status 39 | - personservices/finalizers 40 | verbs: 41 | - create 42 | - delete 43 | - get 44 | - list 45 | - patch 46 | - update 47 | - watch 48 | - verbs: 49 | - "*" 50 | apiGroups: 51 | - "" 52 | resources: 53 | - "configmaps" 54 | - "services" 55 | - verbs: 56 | - "*" 57 | apiGroups: 58 | - "apps" 59 | resources: 60 | - "deployments" 61 | - verbs: 62 | - "*" 63 | apiGroups: 64 | - "route.openshift.io" 65 | resources: 66 | - "routes" 67 | 68 | #+kubebuilder:scaffold:rules 69 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /kube-operator/config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /kube-operator/config/samples/charts_v1alpha1_personservice.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: charts.wanja.org/v1alpha1 2 | kind: PersonService 3 | metadata: 4 | name: personservice-sample 5 | spec: 6 | # Default values copied from /helm-charts/person-service/values.yaml 7 | config: 8 | greeting: Operator to developer, what's going on out there? 9 | deployment: 10 | image: quay.io/wpernath/person-service 11 | includeHealthChecks: false 12 | replicas: 2 13 | version: v1.0.0-test 14 | 15 | 16 | -------------------------------------------------------------------------------- /kube-operator/config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples you want in your CSV to this file as resources ## 2 | resources: 3 | - charts_v1alpha1_personservice.yaml 4 | #+kubebuilder:scaffold:manifestskustomizesamples 5 | -------------------------------------------------------------------------------- /kube-operator/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 | -------------------------------------------------------------------------------- /kube-operator/config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | patchesJson6902: 4 | - path: patches/basic.config.yaml 5 | target: 6 | group: scorecard.operatorframework.io 7 | version: v1alpha3 8 | kind: Configuration 9 | name: config 10 | - path: patches/olm.config.yaml 11 | target: 12 | group: scorecard.operatorframework.io 13 | version: v1alpha3 14 | kind: Configuration 15 | name: config 16 | #+kubebuilder:scaffold:patchesJson6902 17 | -------------------------------------------------------------------------------- /kube-operator/config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - basic-check-spec 7 | image: quay.io/operator-framework/scorecard-test:v1.13.1 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /kube-operator/config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:v1.13.1 8 | labels: 9 | suite: olm 10 | test: olm-bundle-validation-test 11 | - op: add 12 | path: /stages/0/tests/- 13 | value: 14 | entrypoint: 15 | - scorecard-test 16 | - olm-crds-have-validation 17 | image: quay.io/operator-framework/scorecard-test:v1.13.1 18 | labels: 19 | suite: olm 20 | test: olm-crds-have-validation-test 21 | - op: add 22 | path: /stages/0/tests/- 23 | value: 24 | entrypoint: 25 | - scorecard-test 26 | - olm-crds-have-resources 27 | image: quay.io/operator-framework/scorecard-test:v1.13.1 28 | labels: 29 | suite: olm 30 | test: olm-crds-have-resources-test 31 | - op: add 32 | path: /stages/0/tests/- 33 | value: 34 | entrypoint: 35 | - scorecard-test 36 | - olm-spec-descriptors 37 | image: quay.io/operator-framework/scorecard-test:v1.13.1 38 | labels: 39 | suite: olm 40 | test: olm-spec-descriptors-test 41 | - op: add 42 | path: /stages/0/tests/- 43 | value: 44 | entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:v1.13.1 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /kube-operator/helm-charts/person-service/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: v1.0.0-test 3 | description: A helm chart for the basic person-service used as example in the book 4 | home: https://github.com/wpernath/book-example 5 | maintainers: 6 | - email: wpernath@redhat.com 7 | name: Wanja Pernath 8 | name: person-service 9 | sources: 10 | - https://github.com/wpernath/book-example 11 | type: application 12 | version: 0.0.5 13 | -------------------------------------------------------------------------------- /kube-operator/helm-charts/person-service/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | # Release NOTES.txt of person-service helm chart 2 | - Chart name: {{ .Chart.Name }} 3 | - Chart description: {{ .Chart.Description }} 4 | - Chart version: {{ .Chart.Version }} 5 | - App version: {{ .Chart.AppVersion }} 6 | 7 | ## Build and install this helm chart 8 | ```bash 9 | $ helm package helm-chart -u 10 | $ helm install person-service 11 | $ helm upgrade person-service 12 | ``` 13 | 14 | ## To learn more about this relase 15 | ```bash 16 | $ helm list 17 | $ helm history person-service 18 | ``` 19 | 20 | ## Version history 21 | - 0.0.1 Initial release 22 | - 0.0.2 release with some fixed bugs 23 | - 0.0.3 release with image coming from quay.io and better parameter substitution 24 | - 0.0.4 added NOTES.txt and a configmap 25 | - 0.0.5 added a batch Job for post-install and post-upgrade 26 | - 0.0.6 Helm as part of an operator. Removed post-upgrade job 27 | 28 | 29 | -------------------------------------------------------------------------------- /kube-operator/helm-charts/person-service/templates/config-map.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: app-config 5 | data: 6 | APP_GREETING: |- 7 | {{ .Values.config.greeting | default "Yeah, it's openshift time" }} 8 | -------------------------------------------------------------------------------- /kube-operator/helm-charts/person-service/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | progressDeadlineSeconds: 600 9 | replicas: {{ .Values.deployment.replicas | default 1 }} 10 | selector: 11 | matchLabels: 12 | deployment: person-service 13 | strategy: 14 | rollingUpdate: 15 | maxSurge: 25% 16 | maxUnavailable: 25% 17 | type: RollingUpdate 18 | template: 19 | metadata: 20 | labels: 21 | deployment: person-service 22 | spec: 23 | containers: 24 | - image: "{{ .Values.deployment.image }}:{{ .Values.deployment.version }}" 25 | imagePullPolicy: IfNotPresent 26 | name: person-service 27 | ports: 28 | - containerPort: 8080 29 | protocol: TCP 30 | envFrom: 31 | - configMapRef: 32 | name: app-config 33 | resources: {} 34 | terminationMessagePath: /dev/termination-log 35 | terminationMessagePolicy: File 36 | {{- if .Values.deployment.includeHealthChecks }} 37 | readinessProbe: 38 | httpGet: 39 | path: /q/health/ready 40 | port: 8080 41 | scheme: HTTP 42 | timeoutSeconds: 1 43 | periodSeconds: 10 44 | successThreshold: 1 45 | failureThreshold: 3 46 | livenessProbe: 47 | httpGet: 48 | path: /q/health/live 49 | port: 8080 50 | scheme: HTTP 51 | timeoutSeconds: 2 52 | periodSeconds: 10 53 | successThreshold: 1 54 | failureThreshold: 3 55 | {{- end }} 56 | dnsPolicy: ClusterFirst 57 | restartPolicy: Always 58 | schedulerName: default-scheduler 59 | securityContext: {} 60 | terminationGracePeriodSeconds: 30 61 | -------------------------------------------------------------------------------- /kube-operator/helm-charts/person-service/templates/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | port: 9 | targetPort: 8080-tcp 10 | to: 11 | kind: Service 12 | name: person-service 13 | weight: 100 14 | wildcardPolicy: None 15 | -------------------------------------------------------------------------------- /kube-operator/helm-charts/person-service/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | ports: 9 | - name: 8080-tcp 10 | port: 8080 11 | protocol: TCP 12 | targetPort: 8080 13 | selector: 14 | deployment: person-service 15 | sessionAffinity: None 16 | type: ClusterIP 17 | -------------------------------------------------------------------------------- /kube-operator/helm-charts/person-service/values.yaml: -------------------------------------------------------------------------------- 1 | deployment: 2 | image: quay.io/wpernath/person-service 3 | version: v1.0.0-test 4 | replicas: 2 5 | includeHealthChecks: false 6 | 7 | config: 8 | greeting: 'We are on a newer version now!' 9 | 10 | -------------------------------------------------------------------------------- /kube-operator/watches.yaml: -------------------------------------------------------------------------------- 1 | # Use the 'create api' subcommand to add watches to this file. 2 | - group: charts.wanja.org 3 | version: v1alpha1 4 | kind: PersonService 5 | chart: helm-charts/person-service 6 | #+kubebuilder:scaffold:watch 7 | -------------------------------------------------------------------------------- /kustomize-ext/README.md: -------------------------------------------------------------------------------- 1 | # Getting GitOps. 2 | ## More Kustomize sample 3 | This example is being discussed in chapter 2 of the book. -------------------------------------------------------------------------------- /kustomize-ext/base/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | progressDeadlineSeconds: 600 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | deployment: person-service 13 | strategy: 14 | rollingUpdate: 15 | maxSurge: 25% 16 | maxUnavailable: 25% 17 | type: RollingUpdate 18 | template: 19 | metadata: 20 | labels: 21 | deployment: person-service 22 | spec: 23 | containers: 24 | - image: image-registry.openshift-image-registry.svc:5000/book-dev/person-service:latest 25 | imagePullPolicy: IfNotPresent 26 | name: person-service 27 | ports: 28 | - containerPort: 8080 29 | protocol: TCP 30 | envFrom: 31 | - configMapRef: 32 | name: app-config 33 | resources: {} 34 | terminationMessagePath: /dev/termination-log 35 | terminationMessagePolicy: File 36 | dnsPolicy: ClusterFirst 37 | restartPolicy: Always 38 | schedulerName: default-scheduler 39 | securityContext: {} 40 | terminationGracePeriodSeconds: 30 41 | -------------------------------------------------------------------------------- /kustomize-ext/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | commonLabels: 5 | org: wanja.org 6 | 7 | resources: 8 | - deployment.yaml 9 | - service.yaml 10 | - route.yaml 11 | -------------------------------------------------------------------------------- /kustomize-ext/base/postgres.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgres-operator.crunchydata.com/v1beta1 2 | kind: PostgresCluster 3 | metadata: 4 | name: wanja 5 | spec: 6 | openshift: true 7 | image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:centos8-13.5-0 8 | postgresVersion: 13 9 | instances: 10 | - name: instance1 11 | dataVolumeClaimSpec: 12 | accessModes: 13 | - "ReadWriteOnce" 14 | resources: 15 | requests: 16 | storage: 1Gi 17 | backups: 18 | pgbackrest: 19 | image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:centos8-2.36-0 20 | repos: 21 | - name: repo1 22 | volume: 23 | volumeClaimSpec: 24 | accessModes: 25 | - "ReadWriteOnce" 26 | resources: 27 | requests: 28 | storage: 1Gi -------------------------------------------------------------------------------- /kustomize-ext/base/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | port: 9 | targetPort: 8080-tcp 10 | to: 11 | kind: Service 12 | name: person-service 13 | weight: 100 14 | wildcardPolicy: None 15 | -------------------------------------------------------------------------------- /kustomize-ext/base/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | ports: 9 | - name: 8080-tcp 10 | port: 8080 11 | protocol: TCP 12 | targetPort: 8080 13 | selector: 14 | deployment: person-service 15 | sessionAffinity: None 16 | type: ClusterIP 17 | -------------------------------------------------------------------------------- /kustomize-ext/overlays/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base 5 | 6 | namePrefix: dev- 7 | commonLabels: 8 | variant: development 9 | 10 | 11 | # replace the image tag of the container with latest 12 | images: 13 | - name: image-registry.openshift-image-registry.svc:5000/book-dev/person-service:latest 14 | newName: quay.io/wpernath/person-service 15 | 16 | # generate a configmap 17 | configMapGenerator: 18 | - name: app-config 19 | literals: 20 | - APP_GREETING=We are in DEVELOPMENT mode 21 | 22 | # this patch needs to be done, because kustomize does not change 23 | # the route target service name 24 | patches: 25 | - patch: |- 26 | - op: replace 27 | path: /spec/to/name 28 | value: dev-person-service 29 | target: 30 | kind: Route 31 | -------------------------------------------------------------------------------- /kustomize-ext/overlays/stage/apply-health-checks.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: person-service 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: person-service 10 | readinessProbe: 11 | httpGet: 12 | path: /q/health/ready 13 | port: 8080 14 | scheme: HTTP 15 | timeoutSeconds: 1 16 | periodSeconds: 10 17 | successThreshold: 1 18 | failureThreshold: 3 19 | livenessProbe: 20 | httpGet: 21 | path: /q/health/live 22 | port: 8080 23 | scheme: HTTP 24 | timeoutSeconds: 2 25 | periodSeconds: 10 26 | successThreshold: 1 27 | failureThreshold: 3 28 | -------------------------------------------------------------------------------- /kustomize-ext/overlays/stage/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namePrefix: stage- 2 | commonLabels: 3 | variant: stage 4 | 5 | bases: 6 | - ../../base 7 | 8 | # in stage we have 2 replicas 9 | replicas: 10 | - name: person-service 11 | count: 2 12 | 13 | # replace the image tag of the container with stage 14 | images: 15 | - name: image-registry.openshift-image-registry.svc:5000/book-dev/person-service:latest 16 | newName: quay.io/wpernath/person-service 17 | newTag: v1.0.1-test 18 | 19 | # generate a configmap 20 | configMapGenerator: 21 | - name: app-config 22 | literals: 23 | - APP_GREETING=We are in STAGING mode 24 | 25 | # apply some patches 26 | patches: 27 | # apply health checks to deployment 28 | - path: apply-health-checks.yaml 29 | target: 30 | version: v1 31 | kind: Deployment 32 | name: person-service 33 | 34 | # this patch needs to be done, because kustomize does not change 35 | # the route target service name 36 | - patch: |- 37 | - op: replace 38 | path: /spec/to/name 39 | value: stage-person-service 40 | target: 41 | kind: Route 42 | 43 | -------------------------------------------------------------------------------- /kustomize/README.md: -------------------------------------------------------------------------------- 1 | # Getting GitOps. 2 | ## Kustomize sample 3 | This example is being discussed in chapter 2 of the book. -------------------------------------------------------------------------------- /kustomize/base/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | progressDeadlineSeconds: 600 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | deployment: person-service 13 | strategy: 14 | rollingUpdate: 15 | maxSurge: 25% 16 | maxUnavailable: 25% 17 | type: RollingUpdate 18 | template: 19 | metadata: 20 | labels: 21 | deployment: person-service 22 | spec: 23 | containers: 24 | - image: image-registry.openshift-image-registry.svc:5000/book-dev/person-service:latest 25 | imagePullPolicy: IfNotPresent 26 | name: person-service 27 | ports: 28 | - containerPort: 8080 29 | protocol: TCP 30 | resources: {} 31 | terminationMessagePath: /dev/termination-log 32 | terminationMessagePolicy: File 33 | dnsPolicy: ClusterFirst 34 | restartPolicy: Always 35 | schedulerName: default-scheduler 36 | securityContext: {} 37 | terminationGracePeriodSeconds: 30 38 | -------------------------------------------------------------------------------- /kustomize/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | commonLabels: 5 | org: wanja.org 6 | 7 | resources: 8 | - deployment.yaml 9 | - service.yaml 10 | - route.yaml 11 | -------------------------------------------------------------------------------- /kustomize/base/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | port: 9 | targetPort: 8080-tcp 10 | to: 11 | kind: Service 12 | name: person-service 13 | weight: 100 14 | wildcardPolicy: None 15 | -------------------------------------------------------------------------------- /kustomize/base/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | ports: 9 | - name: 8080-tcp 10 | port: 8080 11 | protocol: TCP 12 | targetPort: 8080 13 | selector: 14 | deployment: person-service 15 | sessionAffinity: None 16 | type: ClusterIP 17 | -------------------------------------------------------------------------------- /kustomize/overlays/dev/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: person-service 5 | spec: 6 | replicas: 1 7 | template: 8 | spec: 9 | containers: 10 | - image: image-registry.openshift-image-registry.svc:5000/book-dev/person-service:latest 11 | imagePullPolicy: Always 12 | name: person-service 13 | ports: 14 | - containerPort: 8080 15 | protocol: TCP 16 | env: 17 | - name: APP_GREETING 18 | value: 'Hey, this is the DEVELOPMENT environment of the App' 19 | -------------------------------------------------------------------------------- /kustomize/overlays/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namePrefix: dev- 2 | 3 | commonLabels: 4 | variant: development 5 | 6 | commonAnnotations: 7 | # note: We are on dev now 8 | stage: development 9 | 10 | bases: 11 | - ../../base 12 | 13 | patchesStrategicMerge: 14 | - deployment.yaml 15 | - route.yaml -------------------------------------------------------------------------------- /kustomize/overlays/dev/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | name: person-service 5 | spec: 6 | port: 7 | targetPort: 8080-tcp 8 | to: 9 | kind: Service 10 | name: dev-person-service 11 | weight: 100 12 | wildcardPolicy: None 13 | 14 | -------------------------------------------------------------------------------- /kustomize/overlays/stage/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: person-service 5 | spec: 6 | replicas: 2 7 | template: 8 | spec: 9 | containers: 10 | - image: image-registry.openshift-image-registry.svc:5000/book-dev/person-service:latest 11 | imagePullPolicy: Always 12 | name: person-service 13 | ports: 14 | - containerPort: 8080 15 | protocol: TCP 16 | env: 17 | - name: APP_GREETING 18 | value: 'Hey, this is the STAGING environment of the App' 19 | -------------------------------------------------------------------------------- /kustomize/overlays/stage/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namePrefix: staging- 2 | 3 | commonLabels: 4 | variant: staging 5 | 6 | commonAnnotations: 7 | note: We are on staging now 8 | stage: staging 9 | 10 | bases: 11 | - ../../base 12 | 13 | patchesStrategicMerge: 14 | - deployment.yaml 15 | - route.yaml -------------------------------------------------------------------------------- /kustomize/overlays/stage/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | name: person-service 5 | spec: 6 | port: 7 | targetPort: 8080-tcp 8 | to: 9 | kind: Service 10 | name: staging-person-service 11 | weight: 100 12 | wildcardPolicy: None 13 | 14 | -------------------------------------------------------------------------------- /ocp-template/README.md: -------------------------------------------------------------------------------- 1 | # Getting GitOps. 2 | ## OpenShift Templates sample 3 | This example is being discussed in chapter 2 of the book. -------------------------------------------------------------------------------- /ocp-template/service-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: template.openshift.io/v1 2 | kind: Template 3 | name: service-template 4 | metadata: 5 | name: service-template 6 | annotation: 7 | tags: java 8 | iconClass: icon-rh-openjdk 9 | openshift.io/display-name: The person service template 10 | description: This Template creates a new service 11 | objects: 12 | - apiVersion: route.openshift.io/v1 13 | kind: Route 14 | metadata: 15 | labels: 16 | app: ${APPLICATION_NAME} 17 | name: ${APPLICATION_NAME} 18 | spec: 19 | port: 20 | targetPort: 8080-tcp 21 | to: 22 | kind: Service 23 | name: ${APPLICATION_NAME} 24 | weight: 100 25 | wildcardPolicy: None 26 | - apiVersion: v1 27 | kind: Service 28 | metadata: 29 | labels: 30 | app: ${APPLICATION_NAME} 31 | name: ${APPLICATION_NAME} 32 | spec: 33 | ports: 34 | - name: 8080-tcp 35 | port: 8080 36 | protocol: TCP 37 | targetPort: 8080 38 | selector: 39 | deployment: ${APPLICATION_NAME} 40 | sessionAffinity: None 41 | type: ClusterIP 42 | - apiVersion: apps/v1 43 | kind: Deployment 44 | metadata: 45 | labels: 46 | app: ${APPLICATION_NAME} 47 | name: ${APPLICATION_NAME} 48 | spec: 49 | progressDeadlineSeconds: 600 50 | replicas: 1 51 | selector: 52 | matchLabels: 53 | deployment: ${APPLICATION_NAME} 54 | strategy: 55 | rollingUpdate: 56 | maxSurge: 25% 57 | maxUnavailable: 25% 58 | type: RollingUpdate 59 | template: 60 | metadata: 61 | labels: 62 | deployment: ${APPLICATION_NAME} 63 | spec: 64 | containers: 65 | - image: ${IMAGE_REF} 66 | imagePullPolicy: IfNotPresent 67 | name: ${APPLICATION_NAME} 68 | ports: 69 | - containerPort: 8080 70 | protocol: TCP 71 | resources: {} 72 | terminationMessagePath: /dev/termination-log 73 | terminationMessagePolicy: File 74 | dnsPolicy: ClusterFirst 75 | restartPolicy: Always 76 | schedulerName: default-scheduler 77 | securityContext: {} 78 | terminationGracePeriodSeconds: 30 79 | 80 | parameters: 81 | - name: APPLICATION_NAME 82 | description: The name of the application you'd like to create 83 | displayName: Application Name 84 | required: true 85 | value: person-service 86 | - name: IMAGE_REF 87 | description: The full image path 88 | displayName: Container Image 89 | required: true 90 | value: image-registry.openshift-image-registry.svc:5000/book-dev/person-service:latest -------------------------------------------------------------------------------- /person-service/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /person-service/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /person-service/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /person-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /person-service/.s2i/environment: -------------------------------------------------------------------------------- 1 | ARTIFACT_COPY_ARGS=-p -r quarkus-app/* quarkus-app/quarkus-run.jar 2 | 3 | -------------------------------------------------------------------------------- /person-service/README.md: -------------------------------------------------------------------------------- 1 | # Getting GitOps. 2 | ## The source code of the example service 3 | 4 | This folder contains the Java sources of the Quarkus example. If you want to deploy it on OpenShift, please make sure to first install a PostgreSQL server, either via Crunchy Data Operator or by instantiating the template `postgresql-persistent`. 5 | 6 | ```bash 7 | $ oc new-app postgresql-persistent \ 8 | -p POSTGRESQL_USER=wanja \ 9 | -p POSTGRESQL_PASSWORD=wanja \ 10 | -p POSTGRESQL_DATABASE=wanjadb \ 11 | -p DATABASE_SERVICE_NAME=wanjaserver 12 | ``` 13 | 14 | The complete setup and structure of this example is being discussed in chapter 1 of the book. Please have a look there. -------------------------------------------------------------------------------- /person-service/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /person-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.wanja.demo 6 | person-service 7 | 1.6.0 8 | 9 | 3.8.1 10 | true 11 | 11 12 | 11 13 | UTF-8 14 | UTF-8 15 | quarkus-bom 16 | io.quarkus.platform 17 | 2.12.2.Final 18 | 3.0.0-M5 19 | 20 | 21 | 22 | 23 | ${quarkus.platform.group-id} 24 | ${quarkus.platform.artifact-id} 25 | ${quarkus.platform.version} 26 | pom 27 | import 28 | 29 | 30 | 31 | 32 | 33 | io.quarkus 34 | quarkus-arc 35 | 36 | 37 | io.quarkus 38 | quarkus-resteasy 39 | 40 | 41 | io.quarkus 42 | quarkus-hibernate-orm-panache 43 | 44 | 45 | io.quarkus 46 | quarkus-jdbc-postgresql 47 | 48 | 49 | io.quarkus 50 | quarkus-resteasy-jsonb 51 | 52 | 53 | io.quarkus 54 | quarkus-container-image-jib 55 | 56 | 57 | io.quarkus 58 | quarkus-openshift 59 | 60 | 61 | io.quarkus 62 | quarkus-junit5 63 | test 64 | 65 | 66 | io.rest-assured 67 | rest-assured 68 | test 69 | 70 | 71 | 72 | 73 | 74 | ${quarkus.platform.group-id} 75 | quarkus-maven-plugin 76 | ${quarkus.platform.version} 77 | true 78 | 79 | 80 | 81 | build 82 | generate-code 83 | generate-code-tests 84 | 85 | 86 | 87 | 88 | 89 | maven-compiler-plugin 90 | ${compiler-plugin.version} 91 | 92 | ${maven.compiler.parameters} 93 | 94 | 95 | 96 | maven-surefire-plugin 97 | ${surefire-plugin.version} 98 | 99 | 100 | org.jboss.logmanager.LogManager 101 | ${maven.home} 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | native 110 | 111 | 112 | native 113 | 114 | 115 | 116 | 117 | 118 | maven-failsafe-plugin 119 | ${surefire-plugin.version} 120 | 121 | 122 | 123 | integration-test 124 | verify 125 | 126 | 127 | 128 | ${project.build.directory}/${project.build.finalName}-runner 129 | org.jboss.logmanager.LogManager 130 | ${maven.home} 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | native 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /person-service/src/main/docker/Dockerfile.jvm: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.jvm -t quay.io/wpernath/person-service . 11 | # 12 | # Then, tag the image to something meaningful: 13 | # 14 | # docker tag quay.io/wpernath/person-service:latest quay.io/wpernath/person-service:v1.0.0-test 15 | 16 | # Then, push the image to quay.io 17 | # docker push quay.io/wpernath/person-service -a 18 | # 19 | ### 20 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 21 | 22 | ARG JAVA_PACKAGE=java-11-openjdk-headless 23 | ARG RUN_JAVA_VERSION=1.3.8 24 | ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' 25 | # Install java and the run-java script 26 | # Also set up permissions for user `1001` 27 | RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ 28 | && microdnf update \ 29 | && microdnf clean all \ 30 | && mkdir /deployments \ 31 | && chown 1001 /deployments \ 32 | && chmod "g+rwX" /deployments \ 33 | && chown 1001:root /deployments \ 34 | && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ 35 | && chown 1001 /deployments/run-java.sh \ 36 | && chmod 540 /deployments/run-java.sh \ 37 | && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security 38 | 39 | # Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. 40 | ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" 41 | # We make four distinct layers so if there are application changes the library layers can be re-used 42 | COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/ 43 | COPY --chown=1001 target/quarkus-app/*.jar /deployments/ 44 | COPY --chown=1001 target/quarkus-app/app/ /deployments/app/ 45 | COPY --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/ 46 | 47 | EXPOSE 8080 48 | USER 1001 49 | 50 | ENTRYPOINT [ "/deployments/run-java.sh" ] 51 | 52 | -------------------------------------------------------------------------------- /person-service/src/main/docker/Dockerfile.legacy-jar: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Dquarkus.package.type=legacy-jar 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/person-service-legacy-jar . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/person-service-legacy-jar 15 | # 16 | # If you want to include the debug port into your docker image 17 | # you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5005 18 | # 19 | # Then run the container using : 20 | # 21 | # docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/person-service-legacy-jar 22 | # 23 | ### 24 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 25 | 26 | ARG JAVA_PACKAGE=java-11-openjdk-headless 27 | ARG RUN_JAVA_VERSION=1.3.8 28 | ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' 29 | # Install java and the run-java script 30 | # Also set up permissions for user `1001` 31 | RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ 32 | && microdnf update \ 33 | && microdnf clean all \ 34 | && mkdir /deployments \ 35 | && chown 1001 /deployments \ 36 | && chmod "g+rwX" /deployments \ 37 | && chown 1001:root /deployments \ 38 | && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ 39 | && chown 1001 /deployments/run-java.sh \ 40 | && chmod 540 /deployments/run-java.sh \ 41 | && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/conf/security/java.security 42 | 43 | # Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. 44 | ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" 45 | COPY target/lib/* /deployments/lib/ 46 | COPY target/*-runner.jar /deployments/app.jar 47 | 48 | EXPOSE 8080 49 | USER 1001 50 | 51 | ENTRYPOINT [ "/deployments/run-java.sh" ] 52 | -------------------------------------------------------------------------------- /person-service/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/person-service . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/person-service 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 28 | -------------------------------------------------------------------------------- /person-service/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/person-service . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/person-service 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /person-service/src/main/java/org/wanja/book/quarkus/HelloResource.java: -------------------------------------------------------------------------------- 1 | package org.wanja.book.quarkus; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.Produces; 6 | import javax.ws.rs.core.MediaType; 7 | 8 | import org.eclipse.microprofile.config.inject.ConfigProperty; 9 | 10 | @Path("/hello") 11 | public class HelloResource { 12 | @ConfigProperty(name = "app.greeting") 13 | String greeting; 14 | 15 | @GET 16 | @Produces(MediaType.TEXT_PLAIN) 17 | public String hello() { 18 | return greeting; 19 | } 20 | } -------------------------------------------------------------------------------- /person-service/src/main/java/org/wanja/book/quarkus/Person.java: -------------------------------------------------------------------------------- 1 | package org.wanja.book.quarkus; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | 6 | import io.quarkus.hibernate.orm.panache.PanacheEntity; 7 | 8 | @Entity 9 | public class Person extends PanacheEntity { 10 | @Column(name = "first_name") 11 | public String firstName; 12 | 13 | @Column(name = "last_name") 14 | public String lastName; 15 | public String salutation; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /person-service/src/main/java/org/wanja/book/quarkus/PersonResource.java: -------------------------------------------------------------------------------- 1 | package org.wanja.book.quarkus; 2 | 3 | import java.util.List; 4 | 5 | import javax.transaction.Transactional; 6 | import javax.ws.rs.Consumes; 7 | import javax.ws.rs.GET; 8 | import javax.ws.rs.POST; 9 | import javax.ws.rs.PUT; 10 | import javax.ws.rs.DELETE; 11 | import javax.ws.rs.Path; 12 | import javax.ws.rs.Produces; 13 | import javax.ws.rs.WebApplicationException; 14 | import javax.ws.rs.core.MediaType; 15 | import javax.ws.rs.core.Response; 16 | 17 | import org.jboss.resteasy.annotations.jaxrs.PathParam; 18 | 19 | import io.quarkus.panache.common.Sort; 20 | 21 | @Path("/person") 22 | @Consumes(MediaType.APPLICATION_JSON) 23 | @Produces(MediaType.APPLICATION_JSON) 24 | public class PersonResource { 25 | 26 | @GET 27 | public List getAll() throws Exception { 28 | return Person.findAll(Sort.ascending("last_name")).list(); 29 | } 30 | 31 | @GET 32 | @Path("{id}") 33 | public Person getPerson(@PathParam Long id) throws Exception { 34 | if( id == null ) throw new WebApplicationException("id must not be NULL"); 35 | Person p = Person.findById(id); 36 | if( p == null ) throw new WebApplicationException("There is no person with id = " + id); 37 | return p; 38 | } 39 | 40 | @POST 41 | @Transactional 42 | public Response create(Person p) { 43 | if (p == null || p.id != null) 44 | throw new WebApplicationException("id != null"); 45 | p.persist(); 46 | return Response.ok(p).status(200).build(); 47 | } 48 | 49 | @PUT 50 | @Transactional 51 | @Path("{id}") 52 | public Person update(@PathParam Long id, Person p) { 53 | Person entity = Person.findById(id); 54 | if (entity == null) { 55 | throw new WebApplicationException("Person with id of " + id + " does not exist.", 404); 56 | } 57 | if(p.salutation != null ) entity.salutation = p.salutation; 58 | if(p.firstName != null ) entity.firstName = p.firstName; 59 | if(p.lastName != null) entity.lastName = p.lastName; 60 | return entity; 61 | } 62 | 63 | @DELETE 64 | @Path("{id}") 65 | @Transactional 66 | public Response delete(@PathParam Long id) { 67 | Person entity = Person.findById(id); 68 | if (entity == null) { 69 | throw new WebApplicationException("Person with id of " + id + " does not exist.", 404); 70 | } 71 | entity.delete(); 72 | return Response.status(204).build(); 73 | } 74 | 75 | @GET 76 | @Path("count") 77 | public Long count() { 78 | return Person.count(); 79 | } 80 | 81 | @GET 82 | @Path("search/{salutation}") 83 | public List getPersonBySalutation(@PathParam String salutation) throws Exception { 84 | List lst = Person.find("salutation", salutation).list(); 85 | return lst; 86 | } 87 | 88 | } 89 | 90 | -------------------------------------------------------------------------------- /person-service/src/main/resources/META-INF/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | person-service - 1.7.0 6 | 108 | 109 | 110 | 111 | 114 | 115 |
116 |
117 |

Person-Service v1.7.0

118 | 119 |

HELLO AND WELCOME

120 | 121 |

This page is served by Quarkus. The source is in 122 | src/main/resources/META-INF/resources/index.html.

123 | 124 |

What are your next steps?

125 | 126 |

If not already done, run the application in dev mode using: ./mvnw compile quarkus:dev. 127 |

128 |
    129 |
  • Your static assets are located in src/main/resources/META-INF/resources.
  • 130 |
  • Configure your application in src/main/resources/application.properties.
  • 131 |
  • Quarkus now ships with a Dev UI (available in dev mode only)
  • 132 |
  • Play with the provided code located in src/main/java:
  • 133 |
134 |
135 |

RESTEasy JAX-RS

136 |

Easily start your RESTful Web Services

137 |

@Path: /hello

138 |

@Path: /person

139 |

Related guide section...

140 |
141 | 142 |
143 |
144 |
145 |

Person-Service

146 |
    147 |
  • GroupId: org.wanja.demo
  • 148 |
  • ArtifactId: person-service
  • 149 |
  • Version: 1.6.0
  • 150 |
  • Quarkus Version: 2.7.5.Final
  • 151 |
  • quay.io: https://quay.io/wpernath/person-service
  • 152 |
153 |
154 |
155 |

Do you like Quarkus?

156 |
    157 |
  • Go give it a star on GitHub.
  • 158 |
159 |
160 |
161 |

More reading

162 | 168 |
169 |
170 |
171 | 172 | -------------------------------------------------------------------------------- /person-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # BUILD SETTINGS 2 | # Packaging the app 3 | quarkus.container-image.builder=jib 4 | quarkus.native.container-build=true 5 | quarkus.container-image.image=quay.io/wpernath/person-service 6 | quarkus.container-image.build=false 7 | quarkus.container-image.push=false 8 | 9 | # Kubernetes / OpenShift settings 10 | quarkus.openshift.route.expose=true 11 | quarkus.openshift.deployment-kind=deployment 12 | 13 | # resource limits 14 | quarkus.openshift.resources.requests.memory=128Mi 15 | quarkus.openshift.resources.requests.cpu=250m 16 | quarkus.openshift.resources.limits.memory=256Mi 17 | quarkus.openshift.resources.limits.cpu=500m 18 | 19 | 20 | # RUNTIME SETTINGS 21 | # standard hibernate ORM 22 | quarkus.hibernate-orm.log.format-sql=true 23 | quarkus.hibernate-orm.log.sql=true 24 | 25 | # only when we are developing 26 | %dev.quarkus.hibernate-orm.database.generation=drop-and-create 27 | %dev.quarkus.hibernate-orm.sql-load-script=import.sql 28 | 29 | # only in production 30 | %prod.quarkus.hibernate-orm.database.generation=update 31 | %prod.quarkus.hibernate-orm.sql-load-script=no-file 32 | 33 | # Datssource settings... note, we only set those props in prod mode 34 | quarkus.datasource.db-kind=postgresql 35 | %prod.quarkus.datasource.jdbc.max-size=8 36 | %prod.quarkus.datasource.jdbc.min-size=0 37 | 38 | # Properties are coming from the CrunchyData PostgresCluster instance 39 | # Once provisioned, it creates a Secret called wanja-pguser-wanja 40 | # it contains user, password, host and dbname properties which are 41 | # being mapped in Deployment as DB_user, DB_password etc. ... 42 | %prod.quarkus.datasource.username=${DB_user:wanja} 43 | %prod.quarkus.datasource.password=${DB_password:wanja} 44 | %prod.quarkus.datasource.jdbc.url=jdbc:postgresql://${DB_host:wanjaserver}/${DB_dbname:wanjadb} 45 | 46 | 47 | # My app settings 48 | app.greeting=This is the default greeting from application.properties -------------------------------------------------------------------------------- /person-service/src/main/resources/import.sql: -------------------------------------------------------------------------------- 1 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Doro', 'Pesch', 'Mrs'); 2 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Bobby', 'Brown', 'Mr'); 3 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Elvis', 'Presley', 'Mr'); 4 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Curt', 'Cobain', 'Mr'); 5 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Nina', 'Hagen', 'Mrs'); 6 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Jimmi', 'Henrix', 'Mr'); 7 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Janis', 'Joplin', 'Mrs'); 8 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Joe', 'Cocker', 'Mr'); 9 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Alice', 'Cooper', 'Mr'); 10 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Bruce', 'Springsteen', 'Mr'); 11 | insert into person(id, first_name, last_name, salutation) values (nextval('hibernate_sequence'), 'Eric', 'Clapton', 'Mr'); 12 | -------------------------------------------------------------------------------- /person-service/src/test/java/org/wanja/book/quarkus/HelloResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.wanja.book.quarkus; 2 | 3 | import io.quarkus.test.junit.QuarkusTest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static io.restassured.RestAssured.given; 7 | import static org.hamcrest.CoreMatchers.is; 8 | 9 | import org.eclipse.microprofile.config.inject.ConfigProperty; 10 | 11 | @QuarkusTest 12 | public class HelloResourceTest { 13 | 14 | @ConfigProperty(name = "app.greeting") 15 | String greeting; 16 | 17 | @Test 18 | public void testHelloEndpoint() { 19 | given() 20 | .when().get("/hello") 21 | .then() 22 | .statusCode(200) 23 | .body(is(greeting)); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /person-service/src/test/java/org/wanja/book/quarkus/NativeHelloResourceIT.java: -------------------------------------------------------------------------------- 1 | package org.wanja.book.quarkus; 2 | 3 | import io.quarkus.test.junit.NativeImageTest; 4 | 5 | @NativeImageTest 6 | public class NativeHelloResourceIT extends HelloResourceTest { 7 | 8 | // Execute the same tests but in native mode. 9 | } -------------------------------------------------------------------------------- /raw-kubernetes/README.md: -------------------------------------------------------------------------------- 1 | # Getting GitOps. 2 | ## Raw Kubernetes files sample 3 | This example is being discussed in chapter 2 of the book. -------------------------------------------------------------------------------- /raw-kubernetes/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | deployment: person-service 12 | strategy: 13 | rollingUpdate: 14 | maxSurge: 25% 15 | maxUnavailable: 25% 16 | type: RollingUpdate 17 | template: 18 | metadata: 19 | labels: 20 | deployment: person-service 21 | spec: 22 | containers: 23 | - image: image-registry.openshift-image-registry.svc:5000/book-dev/person-service:latest 24 | imagePullPolicy: IfNotPresent 25 | name: person-service 26 | ports: 27 | - containerPort: 8080 28 | protocol: TCP 29 | restartPolicy: Always 30 | -------------------------------------------------------------------------------- /raw-kubernetes/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | port: 9 | targetPort: 8080-tcp 10 | to: 11 | kind: Service 12 | name: person-service 13 | weight: 100 14 | wildcardPolicy: None 15 | -------------------------------------------------------------------------------- /raw-kubernetes/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: person-service 6 | name: person-service 7 | spec: 8 | ports: 9 | - name: 8080-tcp 10 | port: 8080 11 | protocol: TCP 12 | targetPort: 8080 13 | selector: 14 | deployment: person-service 15 | sessionAffinity: None 16 | type: ClusterIP 17 | -------------------------------------------------------------------------------- /tekton/README.md: -------------------------------------------------------------------------------- 1 | # Getting GitOps. 2 | ## Tekton sample 3 | This example is about Tekton / OpenShift Pipelines and is being discussed in chapter 4 of the book. Please also have a look at the script `pipeline.sh`. It installs all the necessary Tasks and resources if you call it with the `init` parameter: 4 | 5 | ```bash 6 | $ pipeline.sh init 7 | configmap/maven-settings configured 8 | persistentvolumeclaim/maven-repo-pvc configured 9 | persistentvolumeclaim/builder-pvc configured 10 | task.tekton.dev/kustomize configured 11 | task.tekton.dev/maven-caching configured 12 | pipeline.tekton.dev/build-and-push-image configured 13 | ``` 14 | 15 | You can start the pipeline by executing 16 | ```bash 17 | $ pipeline.sh start -u wpernath -p 18 | pipelinerun.tekton.dev/build-and-push-image-run-20211125-163308 created 19 | ``` 20 | -------------------------------------------------------------------------------- /tekton/infra/maven-artifact-cache-pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: maven-repo-pvc 5 | spec: 6 | resources: 7 | requests: 8 | storage: 5Gi 9 | volumeMode: Filesystem 10 | accessModes: 11 | - ReadWriteOnce 12 | persistentVolumeReclaimPolicy: Delete 13 | --- 14 | apiVersion: v1 15 | kind: PersistentVolumeClaim 16 | metadata: 17 | name: builder-pvc 18 | spec: 19 | resources: 20 | requests: 21 | storage: 5Gi 22 | volumeMode: Filesystem 23 | accessModes: 24 | - ReadWriteOnce 25 | persistentVolumeReclaimPolicy: Delete 26 | -------------------------------------------------------------------------------- /tekton/infra/maven-settings-cm.yaml: -------------------------------------------------------------------------------- 1 | kind: ConfigMap 2 | apiVersion: v1 3 | metadata: 4 | name: maven-settings 5 | data: 6 | settings.xml: | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | http://nexus.ci.svc:8081/repository/maven-public 19 | * 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tekton/pipeline.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This starts the pipeline new-pipeline with a given 3 | 4 | set -e -u -o pipefail 5 | declare -r SCRIPT_DIR=$(cd -P $(dirname $0) && pwd) 6 | declare COMMAND="help" 7 | 8 | GIT_URL=https://github.com/wpernath/book-example.git 9 | GIT_REVISION=main 10 | PIPELINE=build-and-push-image 11 | CONTEXT_DIR=the-source 12 | IMAGE_NAME=quay.io/wpernath/person-service 13 | IMAGE_USER=wpernath 14 | IMAGE_PASSWORD= 15 | TARGET_NAMESPACE=book-tekton 16 | 17 | valid_command() { 18 | local fn=$1; shift 19 | [[ $(type -t "$fn") == "function" ]] 20 | } 21 | 22 | info() { 23 | printf "\n# INFO: $@\n" 24 | } 25 | 26 | err() { 27 | printf "\n# ERROR: $1\n" 28 | exit 1 29 | } 30 | 31 | command.help() { 32 | cat <<-EOF 33 | Starts a new pipeline in current kubernetes context 34 | 35 | Usage: 36 | pipeline.sh [command] [options] 37 | 38 | Examples: 39 | pipeline.sh init 40 | pipeline.sh start -u wpernath -p -t art-tekton 41 | pipeline.sh logs 42 | 43 | COMMANDS: 44 | init creates ConfigMap, Tasks and Pipelines into current context 45 | start starts the given pipeline 46 | logs shows logs of the last pipeline run 47 | help Help about this command 48 | 49 | OPTIONS: 50 | -u, --registry-user User to store the image into quay.io ($IMAGE_USER) 51 | -p, --registry-password Password to store the image into quay.io ($IMAGE_PASSWORD) 52 | -c, --context-dir Which context-dir to user ($CONTEXT_DIR) 53 | -t, --target-namespace Which target namespace to start the app ($TARGET_NAMESPACE) 54 | -g, --git-repo Which quarkus repository to clone ($GIT_URL) 55 | -r, --git-revision Which git revision to use ($GIT_REVISION) 56 | 57 | EOF 58 | } 59 | 60 | while (( "$#" )); do 61 | case "$1" in 62 | start|logs|init) 63 | COMMAND=$1 64 | shift 65 | ;; 66 | -c|--context-dir) 67 | CONTEXT_DIR=$2 68 | shift 2 69 | ;; 70 | -t|--target-namespace) 71 | TARGET_NAMESPACE=$2 72 | shift 2 73 | ;; 74 | -u|--registry-user) 75 | IMAGE_USER=$2 76 | shift 2 77 | ;; 78 | -p|--registry-password) 79 | IMAGE_PASSWORD=$2 80 | shift 2 81 | ;; 82 | -g|--git-repo) 83 | GIT_URL=$2 84 | shift 2 85 | ;; 86 | -r|--git-revision) 87 | GIT_REVISION=$2 88 | shift 2 89 | ;; 90 | -l|--pipeline) 91 | PIPELINE=$2 92 | shift 2 93 | ;; 94 | --) 95 | shift 96 | break 97 | ;; 98 | -*|--*) 99 | command.help 100 | err "Error: Unsupported flag $1" 101 | ;; 102 | *) 103 | break 104 | esac 105 | done 106 | 107 | 108 | command.init() { 109 | # This script imports the necessary files into the current project 110 | pwd 111 | oc apply -f infra/maven-settings-cm.yaml 112 | oc apply -f infra/maven-artifact-cache-pvc.yaml 113 | 114 | oc apply -f tasks/kustomize-task.yaml 115 | oc apply -f tasks/maven-task.yaml 116 | 117 | oc apply -f pipelines/tekton-pipeline.yaml 118 | } 119 | 120 | 121 | command.logs() { 122 | tkn pr logs -f -L 123 | } 124 | 125 | command.start() { 126 | cat > /tmp/pipelinerun.yaml <<-EOF 127 | apiVersion: tekton.dev/v1beta1 128 | kind: PipelineRun 129 | metadata: 130 | name: $PIPELINE-run-$(date "+%Y%m%d-%H%M%S") 131 | spec: 132 | params: 133 | - name: git-url 134 | value: '$GIT_URL' 135 | - name: git-revision 136 | value: $GIT_REVISION 137 | - name: context-dir 138 | value: $CONTEXT_DIR 139 | - name: image-name 140 | value: $IMAGE_NAME 141 | - name: image-username 142 | value: $IMAGE_USER 143 | - name: image-password 144 | value: $IMAGE_PASSWORD 145 | - name: target-namespace 146 | value: $TARGET_NAMESPACE 147 | workspaces: 148 | - name: shared-workspace 149 | persistentVolumeClaim: 150 | claimName: builder-pvc 151 | - configMap: 152 | name: maven-settings 153 | name: maven-settings 154 | pipelineRef: 155 | name: $PIPELINE 156 | serviceAccountName: pipeline 157 | EOF 158 | 159 | oc apply -f /tmp/pipelinerun.yaml 160 | } 161 | 162 | main() { 163 | local fn="command.$COMMAND" 164 | valid_command "$fn" || { 165 | command.help 166 | err "invalid command '$COMMAND'" 167 | } 168 | 169 | cd $SCRIPT_DIR 170 | $fn 171 | return $? 172 | } 173 | 174 | main 175 | -------------------------------------------------------------------------------- /tekton/pipelines/tekton-pipeline-test.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Pipeline 3 | metadata: 4 | name: new-test-pipeline 5 | spec: 6 | params: 7 | - default: 'https://github.com/wpernath/quarkus-simple.git' 8 | description: Source to the GIT 9 | name: git-url 10 | type: string 11 | - default: main 12 | description: revision to be used 13 | name: git-revision 14 | type: string 15 | - name: image-name 16 | type: string 17 | description: the name of the target image including registry 18 | default: quay.io/wpernath/quarkus-simple-wow:latest 19 | - name: image-username 20 | type: string 21 | description: the username you use to access the registry 22 | - name: image-password 23 | type: string 24 | description: The password you use to access the registry 25 | - name: target-namespace 26 | description: The name of the namespace to apply the result to 27 | tasks: 28 | - name: git-clone 29 | params: 30 | - name: url 31 | value: $(params.git-url) 32 | - name: deleteExisting 33 | value: 'true' 34 | - name: verbose 35 | value: 'true' 36 | - name: revision 37 | value: $(params.git-revision) 38 | taskRef: 39 | kind: ClusterTask 40 | name: git-clone 41 | workspaces: 42 | - name: output 43 | workspace: shared-workspace 44 | - name: package 45 | params: 46 | - name: GOALS 47 | value: 48 | - package 49 | - '-DskipTests' 50 | - name: PROXY_PROTOCOL 51 | value: http 52 | - name: CONTEXT_DIR 53 | value: . 54 | runAfter: 55 | - git-clone 56 | taskRef: 57 | kind: Task 58 | name: maven-caching 59 | workspaces: 60 | - name: source 61 | workspace: shared-workspace 62 | - name: maven-repo 63 | workspace: maven-repo 64 | - name: maven-settings 65 | workspace: maven-settings 66 | - name: apply-kustomize 67 | params: 68 | - name: kustomize-dir 69 | value: kustomize_ext/overlays/dev 70 | - name: target-namespace 71 | value: $(params.target-namespace) 72 | - name: image-name 73 | value: $(params.image-name) 74 | taskRef: 75 | kind: Task 76 | name: kustomize 77 | runAfter: 78 | - package 79 | workspaces: 80 | - name: source 81 | workspace: shared-workspace 82 | workspaces: 83 | - name: shared-workspace 84 | optional: false 85 | - name: maven-settings 86 | optional: false 87 | - name: maven-repo 88 | optional: false 89 | -------------------------------------------------------------------------------- /tekton/pipelines/tekton-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Pipeline 3 | metadata: 4 | name: build-and-push-image 5 | spec: 6 | params: 7 | - default: 'https://github.com/wpernath/book-example.git' 8 | description: Source to the GIT 9 | name: git-url 10 | type: string 11 | - default: main 12 | description: revision to be used 13 | name: git-revision 14 | type: string 15 | - name: context-dir 16 | default: "the-source" 17 | type: string 18 | description: Where to checkout the source relative to the workspace 19 | - name: image-name 20 | type: string 21 | description: the name of the target image including registry 22 | default: quay.io/wpernath/person-service 23 | - name: image-username 24 | type: string 25 | description: the username you use to access the registry 26 | - name: image-password 27 | type: string 28 | description: The password you use to access the registry 29 | - name: target-namespace 30 | description: The name of the namespace to apply the result to 31 | tasks: 32 | - name: git-clone 33 | params: 34 | - name: url 35 | value: $(params.git-url) 36 | - name: deleteExisting 37 | value: 'true' 38 | - name: verbose 39 | value: 'true' 40 | - name: revision 41 | value: $(params.git-revision) 42 | - name: subdirectory 43 | value: $(params.context-dir) 44 | taskRef: 45 | kind: ClusterTask 46 | name: git-clone 47 | workspaces: 48 | - name: output 49 | workspace: shared-workspace 50 | - name: package 51 | params: 52 | - name: GOALS 53 | value: 54 | - package 55 | - '-DskipTests' 56 | - '-Dquarkus.container-image.push=false' 57 | - '-Dquarkus.container-image.build=false' 58 | - name: CONTEXT_DIR 59 | value: "$(params.context-dir)/person-service" 60 | runAfter: 61 | - git-clone 62 | taskRef: 63 | kind: Task 64 | name: maven-caching 65 | workspaces: 66 | - name: source 67 | workspace: shared-workspace 68 | - name: maven-settings 69 | workspace: maven-settings 70 | - name: build-and-push-image 71 | params: 72 | - name: GOALS 73 | value: 74 | - package 75 | - '-DskipTests' 76 | - '-Dquarkus.container-image.push=true' 77 | - '-Dquarkus.container-image.build=true' 78 | - '-Dquarkus.container-image.builder=jib' 79 | - '-Dquarkus.container-image.image=$(params.image-name)' 80 | - '-Dquarkus.container-image.username=$(params.image-username)' 81 | - '-Dquarkus.container-image.password=$(params.image-password)' 82 | - name: CONTEXT_DIR 83 | value: "$(params.context-dir)/person-service" 84 | runAfter: 85 | - package 86 | taskRef: 87 | kind: Task 88 | name: maven-caching 89 | workspaces: 90 | - name: source 91 | workspace: shared-workspace 92 | - name: maven-settings 93 | workspace: maven-settings 94 | - name: apply-kustomize 95 | params: 96 | - name: kustomize-dir 97 | value: $(params.context-dir)/kustomize-ext/overlays/dev 98 | - name: target-namespace 99 | value: $(params.target-namespace) 100 | - name: image-name 101 | value: $(params.image-name) 102 | - name: image-digest-path 103 | value: $(params.context-dir)/person-service/target 104 | taskRef: 105 | kind: Task 106 | name: kustomize 107 | runAfter: 108 | - build-and-push-image 109 | workspaces: 110 | - name: source 111 | workspace: shared-workspace 112 | workspaces: 113 | - name: shared-workspace 114 | optional: false 115 | - name: maven-settings 116 | optional: false 117 | -------------------------------------------------------------------------------- /tekton/tasks/bash-task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: bash 5 | labels: 6 | app.kubernetes.io/version: "0.4" 7 | annotations: 8 | tekton.dev/pipelines.minVersion: "0.12.1" 9 | tekton.dev/tags: build-tool 10 | spec: 11 | description: >- 12 | This task can be used to execute kustomze build scripts and to apply the changes via oc apply -f 13 | workspaces: 14 | - name: source 15 | description: The workspace holding the cloned and compiled quarkus source. 16 | params: 17 | - name: script 18 | description: Where should kustomize look for kustomization in source? 19 | steps: 20 | - name: script 21 | image: quay.io/wpernath/kustomize-ubi:latest 22 | workingDir: $(workspaces.source.path) 23 | script: | 24 | echo 25 | ls -al /tekton/creds 26 | 27 | echo 28 | ls -al ~/.docker/config.json 29 | cat ~/.docker/config.json 30 | 31 | echo 32 | #ls -al ~/.ssh 33 | #ls -al ~/.gitconfig 34 | #cat ~/.gitconfig 35 | 36 | $(params.script) 37 | ls -al ~/.ssh 38 | -------------------------------------------------------------------------------- /tekton/tasks/kustomize-task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: kustomize 5 | labels: 6 | app.kubernetes.io/version: "0.4" 7 | annotations: 8 | tekton.dev/pipelines.minVersion: "0.12.1" 9 | tekton.dev/tags: build-tool 10 | spec: 11 | description: >- 12 | This task can be used to execute kustomze build scripts and to apply the changes via oc apply -f 13 | workspaces: 14 | - name: source 15 | description: The workspace holding the cloned and compiled quarkus source. 16 | params: 17 | - name: kustomize-dir 18 | description: Where should kustomize look for kustomization in source? 19 | - name: target-namespace 20 | description: Where to apply the kustomization to 21 | - name: image-name 22 | description: Which image to use. Kustomize is taking care of it 23 | - name: image-digest-path 24 | description: The path of the jib-image.digest file generated by quarkus package 25 | steps: 26 | - name: build 27 | image: quay.io/wpernath/kustomize-ubi:latest 28 | workingDir: $(workspaces.source.path)/$(params.kustomize-dir) 29 | script: | 30 | ls -al $(workspaces.source.path) 31 | echo "kustomize-dir: $(params.kustomize-dir)" 32 | echo "image-digest : $(params.image-digest-path)" 33 | 34 | echo 35 | 36 | ls -l $(workspaces.source.path)/$(params.image-digest-path) 37 | 38 | echo 39 | DIGEST=$(cat $(workspaces.source.path)/$(params.image-digest-path)/jib-image.digest) 40 | echo "Calculated DIGEST: $DIGEST" 41 | echo 42 | 43 | kustomize edit set image quay.io/wpernath/person-service:latest=$(params.image-name)@$DIGEST 44 | 45 | kustomize build . 46 | kustomize build . > ./kustomized.yaml 47 | 48 | - name: apply 49 | image: 'image-registry.openshift-image-registry.svc:5000/openshift/cli:latest' 50 | workingDir: $(workspaces.source.path)/$(params.kustomize-dir) 51 | script: | 52 | oc apply -f ./kustomized.yaml -n $(params.target-namespace) 53 | 54 | -------------------------------------------------------------------------------- /tekton/tasks/maven-task.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Task 3 | metadata: 4 | name: maven-caching 5 | labels: 6 | app.kubernetes.io/version: "0.4" 7 | annotations: 8 | tekton.dev/pipelines.minVersion: "0.12.1" 9 | tekton.dev/tags: build-tool 10 | spec: 11 | description: >- 12 | This Task can be used to run a Maven build. The only difference to 13 | the original one from github.com/tektoncd/catalog is that it uses an 14 | optional local maven repo path to cache all the dependencies. And it requires a 15 | a maven-settings workspace. 16 | workspaces: 17 | - name: source 18 | description: The workspace consisting of maven project. 19 | optional: false 20 | - name: maven-settings 21 | description: >- 22 | The workspace consisting of the custom maven settings 23 | provided by the user. 24 | optional: false 25 | params: 26 | - name: GOALS 27 | description: maven goals to run 28 | type: array 29 | default: 30 | - "package" 31 | - name: CONTEXT_DIR 32 | type: string 33 | description: >- 34 | The context directory within the repository for sources on 35 | which we want to execute maven goals. 36 | default: "." 37 | steps: 38 | - name: mvn-goals 39 | image: gcr.io/cloud-builders/mvn@sha256:57523fc43394d6d9d2414ee8d1c85ed7a13460cbb268c3cd16d28cfb3859e641 40 | workingDir: $(workspaces.source.path)/$(params.CONTEXT_DIR) 41 | command: ["/usr/bin/mvn"] 42 | args: 43 | - -B 44 | - -s 45 | - $(workspaces.maven-settings.path)/settings.xml 46 | - -Dmaven.repo.local=$(workspaces.source.path)/MAVEN_MIRROR 47 | - "$(params.GOALS)" --------------------------------------------------------------------------------