├── .gitignore ├── README.md ├── bootstrap.sh ├── bootstrap └── argocd │ └── apps │ └── overlays │ ├── aws.cluster │ ├── kustomization.yaml │ └── values.yaml │ ├── local.home │ ├── kustomization.yaml │ └── values.yaml │ └── rhpds │ ├── kustomization.yaml │ └── values.yaml ├── clusters ├── aws.cluster │ └── overlays │ │ ├── cicd │ │ ├── kustomization.yaml │ │ └── pipeline-rolebinding.yaml │ │ ├── dev │ │ └── kustomization.yaml │ │ ├── pipelinerun │ │ └── kustomization.yaml │ │ ├── prod │ │ └── kustomization.yaml │ │ └── test │ │ └── kustomization.yaml ├── local.home │ └── overlays │ │ ├── cicd │ │ ├── kustomization.yaml │ │ ├── patch-pvc-block.yaml │ │ └── pipeline-rolebinding.yaml │ │ ├── dev │ │ └── kustomization.yaml │ │ ├── pipelinerun │ │ ├── apps │ │ │ └── kustomization.yaml │ │ ├── push-to-prod-client │ │ │ └── kustomization.yaml │ │ └── push-to-prod-server │ │ │ └── kustomization.yaml │ │ ├── prod │ │ └── kustomization.yaml │ │ └── test │ │ └── kustomization.yaml └── rhpds │ └── overlays │ ├── cicd │ ├── kustomization.yaml │ └── pipeline-rolebinding.yaml │ ├── dev │ └── kustomization.yaml │ ├── pipelinerun │ ├── apps │ │ └── kustomization.yaml │ ├── push-to-prod-client │ │ └── kustomization.yaml │ └── push-to-prod-server │ │ └── kustomization.yaml │ ├── prod │ └── kustomization.yaml │ └── test │ └── kustomization.yaml ├── components ├── apps │ ├── client │ │ └── base │ │ │ ├── client-deployment.yaml │ │ │ ├── client-route.yaml │ │ │ ├── client-service.yaml │ │ │ ├── config │ │ │ └── config.js │ │ │ └── kustomization.yaml │ ├── database │ │ └── base │ │ │ ├── config │ │ │ ├── 90-init-database.sh │ │ │ ├── import.sql │ │ │ └── schema.sql │ │ │ ├── db-deployment.yaml │ │ │ ├── db-pvc.yaml │ │ │ ├── db-secret.yaml │ │ │ ├── db-service.yaml │ │ │ └── kustomization.yaml │ ├── monitor │ │ └── base │ │ │ ├── grafana-quarkus-dashboard.yaml │ │ │ ├── kustomization.yaml │ │ │ └── quarkus-dashboard.json │ ├── server │ │ └── base │ │ │ ├── config │ │ │ └── application.properties │ │ │ ├── default-view-rolebinding.yaml │ │ │ ├── kustomization.yaml │ │ │ ├── server-deployment.yaml │ │ │ ├── server-route.yaml │ │ │ ├── server-service.yaml │ │ │ └── server-sm.yaml │ └── slack-message-handler │ │ └── base │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ ├── route.yaml │ │ └── service.yaml └── tekton │ ├── pipelines │ ├── client │ │ └── base │ │ │ ├── client-pipeline.yaml │ │ │ └── kustomization.yaml │ ├── push-prod-pr │ │ └── base │ │ │ ├── gitops-manifests-pvc.yaml │ │ │ ├── kustomization.yaml │ │ │ └── push-prod-pr-pipeline.yaml │ └── server │ │ └── base │ │ ├── config │ │ ├── newman-dev-env.json │ │ ├── newman-prod-env.json │ │ ├── newman-test-env.json │ │ └── settings.xml │ │ ├── kustomization.yaml │ │ ├── secrets │ │ └── maven-repo-creds.yaml │ │ ├── server-pipeline.yaml │ │ └── server-post-prod-pipeline.yaml │ ├── tasks │ └── base │ │ ├── dependency-cache-pvc.yaml │ │ ├── kustomization.yaml │ │ ├── m2-cache-pvc.yaml │ │ ├── npm-cache-pvc.yaml │ │ ├── task-binary-s2i.yaml │ │ ├── task-buildah.yaml │ │ ├── task-create-commit-list.yaml │ │ ├── task-create-pr.yaml │ │ ├── task-deploy.yaml │ │ ├── task-git.yaml │ │ ├── task-kustomize.yaml │ │ ├── task-npm-quality.yaml │ │ ├── task-npm.yaml │ │ ├── task-push-image.yaml │ │ ├── task-run-pipeline.yaml │ │ ├── task-send-to-webhook-slack.yaml │ │ ├── task-tekton.yaml │ │ ├── task-update-image.yaml │ │ ├── task-variables.yaml │ │ └── task-yq.yaml │ └── triggers │ └── base │ ├── argocd-notification-triggerbinding.yaml │ ├── client-eventlistener.yaml │ ├── client-triggertemplate.yaml │ ├── kustomization.yaml │ ├── server-eventlistener.yaml │ ├── server-post-prod-eventlistener.yaml │ ├── server-post-prod-triggertemplate.yaml │ ├── server-triggertemplate.yaml │ ├── slack-message-eventlistener.yaml │ ├── slack-message-triggerbinding.yaml │ └── slack-message-triggertemplate.yaml ├── docs ├── img │ ├── argocd.png │ ├── cicd-flow.png │ ├── client-server-database.png │ ├── monitoring.png │ ├── screenshot.png │ ├── tekton-rerun.png │ └── topology.png └── swagger │ ├── openapi-3.0.json │ └── swagger-2.0.json ├── environments └── overlays │ ├── cicd │ ├── acs-external-secret.yaml │ ├── docker-external-secret.yaml │ ├── github-external-secret.yaml │ ├── gitops-network-policy.yaml │ ├── kustomization.yaml │ ├── prod-network-policy.yaml │ ├── setup-local-credentials-job.yaml │ ├── slack-deployments-webhook-external-secret.yaml │ └── workspace-template-cm.yaml │ ├── dev │ ├── cicd-networkpolicy.yaml │ └── kustomization.yaml │ ├── monitor │ ├── README.md │ └── kustomization.yaml │ ├── prod │ ├── cicd-networkpolicy.yaml │ ├── cicd-pipeline-view.yaml │ ├── kustomization.yaml │ └── post-sync-pipeline-job.yaml │ └── test │ ├── cicd-networkpolicy.yaml │ └── kustomization.yaml ├── pipelinerun.sh └── scripts ├── acm ├── 1-apply-channel.sh ├── 2-apply-dev.sh ├── 3-apply-test.sh ├── 4-apply-prod.sh └── 5-spply-cicd.sh ├── apply-home.sh ├── apply-ocplab.sh ├── apply-pipelinerun.sh ├── apply.sh └── siege.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .dockerconfigjson 2 | .vscode 3 | optional 4 | charts 5 | *eso-token* 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Introduction 2 | 3 | This is an OpenShift demo showing how to do GitOps in a kubernetes way using tools like [ArgoCD](https://argoproj.github.io/argo-cd/) and [Kustomize](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/). The demo application is a three tier application using React for the front-end with Quarkus providing APIs as the back-end. The back-end was originally written in PHP and then ported to Quarkus. The application itself is a simple product catalog: 4 | 5 | ![alt text](https://raw.githubusercontent.com/gnunn-gitops/product-catalog/master/docs/img/screenshot.png) 6 | 7 | The topology view in OpenShift shows the three tiers of the application: 8 | 9 | ![alt text](https://raw.githubusercontent.com/gnunn-gitops/product-catalog/master/docs/img/topology.png) 10 | 11 | ### Running demo locally 12 | 13 | To run the demo locally on your laptop, you will need to have a MySQL or MariaDB database available. You will need to create a product database using the SQL in this repo and then update the quarkus application.properties file to reflect the location of the database. 14 | 15 | The following repos will need to be cloned: 16 | 17 | * [product-catalog-client](https://github.com/gnunn-gitops/product-catalog-client) 18 | * [product-catalog-server](https://github.com/gnunn-gitops/product-catalog-server) 19 | 20 | 21 | To run the quarkus application, execute ```mvn quarkus:dev``` from the root directory. 22 | 23 | To run the client application, go into the client directory and run ```npm run start```. 24 | 25 | ### Install on OpenShift 26 | 27 | This application makes heavy use of Kustomize and ArgoCD to deploy the application in a GitOps manner. I had originally thought to make this demo 28 | consumeable for others but as I've extended it to more and more infrastructure (github/slack/etc) it's becoming challenging. At this point this repo 29 | is more of a reference example then a demo someone else can run. 30 | 31 | If you really want to to deploy this application into your own cluster, 32 | you will need to create a new repo and setup Kustomize overlays that point to this repo. Since Kustomize supports referencing remote resources you do not need 33 | to fork this repo, a new one will suffice. 34 | 35 | This project requires Nexus and SonarQube be available, this project used to deploy them directly but I have opted to move these as a separate deployment so it can be shared amongst multiple projects. To see how I deploy them visit the [dev-tools](https://github.com/gnunn-gitops/dev-tools) repository. 36 | 37 | *Deprecated* In order to make this easier, I had created a [product-catalog-template](https://github.com/gnunn-gitops/product-catalog-template) . It includes detailed instructions with regards to pre-requisities and what needs to be modified to deploy the demo in your own cluster. 38 | 39 | Once deployed in your cluster under ArgoCD it should appear as follows: 40 | 41 | ![alt text](https://raw.githubusercontent.com/gnunn-gitops/product-catalog/master/docs/img/argocd.png) 42 | 43 | ### Test CI/CD Pipelines 44 | 45 | The demo uses OpenShift Pipelines to build the client and server images for the application. The demo does not install PipelineRun objects via ArgoCD since these objects are transitory and not meant to be managed by a GitOps tool. To load the initial PipelineRun objects, use the following command: 46 | 47 | ``` 48 | oc apply -k manifests/tekton/pipelineruns/client/base 49 | oc apply -k manifests/tekton/pipelineruns/server/base 50 | ``` 51 | 52 | To test the pipelines are actually taking changes, you can add a logo to the product catalog. The code to do this is commented out and can be found in the [nav.jsx](https://github.com/gnunn1/quarkus-product-catalog/blob/master/client/src/js/components/layouts/nav.jsx#L45) file. 53 | 54 | Once you make the code change, start the client pipeline. Note that in OpenShift Pipelines the GUI does not support creating a new PipelineRunTask with a workspace, if you want to drive it from a GUI go into the PipelineRuns and simple rerun an existing one. 55 | 56 | ![alt text](https://raw.githubusercontent.com/gnunn-gitops/product-catalog/master/docs/img/tekton-rerun.png) 57 | 58 | ### Test Prod Pipeline 59 | 60 | The demo uses a pipeline called ```push-prod-pr``` that creates a pull request in github. When the pull request is merged ArgoCD will see the change in git and automatically deploy the updated image for you. The client and server pipelines can run the push-prod-pr automatically if you set the ```push-to-prod``` parameter to true to have it trigger the pipeline automatically. The default for this parameter is false. 61 | 62 | For the server pipeline the process has been enhanced so that a post-sync hook in ArgoCD will trigger a pipeline to run an integration test after the deployment and send a notification of the status to a slack channel. This process is depicted in the diagram below. 63 | 64 | ![alt text](https://raw.githubusercontent.com/gnunn-gitops/product-catalog/master/docs/img/cicd-flow.png) 65 | 66 | To execute the pipelines you will need to create pipelinerun objects, base versions are available in ```manifests/tekton/pipelineruns```. A script is also available at ```scripts/apply-pipelineruns.sh``` to load the client and server pipelineruns however you will need to modify the pipelineruns to reflect your cluster and enterprise registry. 67 | 68 | ### Enterprise Registry 69 | 70 | The demo is used and tested primarily with an enterprise registry, in my case I use quay.io. See the [product-catalog-template](https://github.com/gnunn-gitops/product-catalog-template) for more information on this. 71 | 72 | The demo uses the git commit hash to tag the client and server images in the registry, when using quay.io it is highly recommended to deploy the Container Security Operator so that quay vulnerability scans are shown. From a demo perspective, I find showing how older images have more vulnerabilities highlights the benefits of using Red Hat base images. 73 | 74 | ### Monitoring 75 | 76 | A basic monitoring system is installed as part of the demo, as a pre-requisite it requires [monitoring for user-defined projects](https://docs.openshift.com/container-platform/4.6/monitoring/enabling-monitoring-for-user-defined-projects.html) to be enabled in OpenShift. 77 | 78 | This demo deploys grafana into the ```product-catalog-monitor``` namespace along with a simple dashboard for the server application. The dashboard tracks JVM metrics as well as API calls to the server, with no load the API call metrics will be flat and that is normal: 79 | 80 | ![alt text](https://raw.githubusercontent.com/gnunn-gitops/product-catalog/master/docs/img/monitoring.png) 81 | 82 | There's a sample siege script that can be used to drive some load if desired under scripts, you will need to update the script to reflect the endpoints in your cluster. 83 | 84 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | if [ $# -lt 1 ]; then 2 | echo "No overlay specified, please specify an overlay from bootstrap/overlays" 3 | exit 1 4 | else 5 | OVERLAY=$1 6 | echo "Deploying product catalog to cluster ${OVERLAY}" 7 | fi 8 | kustomize build bootstrap/argocd/apps/overlays/${OVERLAY} --enable-helm | oc apply -f - -------------------------------------------------------------------------------- /bootstrap/argocd/apps/overlays/aws.cluster/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-gitops 5 | 6 | helmCharts: 7 | - name: argocd-app-of-app 8 | version: 0.2.6 9 | repo: https://gnunn-gitops.github.io/helm-charts 10 | valuesFile: values.yaml 11 | namespace: openshift-gitops 12 | releaseName: product-catalog -------------------------------------------------------------------------------- /bootstrap/argocd/apps/overlays/aws.cluster/values.yaml: -------------------------------------------------------------------------------- 1 | default: 2 | app: 3 | enabled: true 4 | enableAutoSync: true 5 | autoSyncPrune: false 6 | project: apps-product-catalog 7 | labels: 8 | gitops.openshift.io/controllerNamespace: gitops 9 | destination: 10 | namespace: product-catalog-gitops 11 | server: https://kubernetes.default.svc 12 | source: 13 | repoURL: https://github.com/gnunn-gitops/product-catalog 14 | targetRevision: main 15 | 16 | applications: 17 | 18 | product-catalog-dev: 19 | destination: 20 | namespace: product-catalog-dev 21 | source: 22 | path: clusters/aws.cluster/overlays/dev 23 | 24 | product-catalog-test: 25 | destination: 26 | namespace: product-catalog-test 27 | source: 28 | path: clusters/aws.cluster/overlays/test 29 | 30 | product-catalog-prod: 31 | destination: 32 | namespace: product-catalog-prod 33 | source: 34 | path: clusters/aws.cluster/overlays/prod 35 | 36 | product-catalog-cicd: 37 | destination: 38 | namespace: product-catalog-cicd 39 | source: 40 | path: clusters/aws.cluster/overlays/cicd 41 | 42 | product-catalog-monitor: 43 | destination: 44 | namespace: product-catalog-monitor 45 | source: 46 | path: environments/overlays/monitor -------------------------------------------------------------------------------- /bootstrap/argocd/apps/overlays/local.home/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-gitops 5 | 6 | helmCharts: 7 | - name: argocd-app-of-app 8 | version: 0.4.0 9 | repo: https://gnunn-gitops.github.io/helm-charts 10 | valuesFile: values.yaml 11 | namespace: openshift-gitops 12 | releaseName: product-catalog 13 | -------------------------------------------------------------------------------- /bootstrap/argocd/apps/overlays/local.home/values.yaml: -------------------------------------------------------------------------------- 1 | default: 2 | app: 3 | enabled: true 4 | enableAutoSync: true 5 | autoSyncPrune: false 6 | project: product-catalog 7 | labels: 8 | gitops.openshift.io/controllerNamespace: gitops 9 | destination: 10 | namespace: product-catalog-gitops 11 | name: in-cluster 12 | source: 13 | repoURL: https://github.com/gnunn-gitops/product-catalog 14 | targetRevision: main 15 | 16 | applications: 17 | 18 | product-catalog-dev: 19 | labels: 20 | app.kubernetes.io/name: product-catalog 21 | destination: 22 | namespace: product-catalog-dev 23 | source: 24 | path: clusters/local.home/overlays/dev 25 | 26 | product-catalog-test: 27 | labels: 28 | app.kubernetes.io/name: product-catalog 29 | destination: 30 | namespace: product-catalog-test 31 | source: 32 | path: clusters/local.home/overlays/test 33 | 34 | product-catalog-prod: 35 | labels: 36 | app.kubernetes.io/name: product-catalog 37 | annotations: 38 | notifications.argoproj.io/subscribe.on-sync-succeeded.server-post-prod: "" 39 | destination: 40 | namespace: product-catalog-prod 41 | source: 42 | path: clusters/local.home/overlays/prod 43 | 44 | product-catalog-cicd: 45 | destination: 46 | namespace: product-catalog-cicd 47 | source: 48 | path: clusters/local.home/overlays/cicd 49 | 50 | product-catalog-monitor: 51 | destination: 52 | namespace: product-catalog-monitor 53 | source: 54 | path: environments/overlays/monitor 55 | -------------------------------------------------------------------------------- /bootstrap/argocd/apps/overlays/rhpds/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-gitops 5 | 6 | helmCharts: 7 | - name: argocd-app-of-app 8 | version: 0.2.6 9 | repo: https://gnunn-gitops.github.io/helm-charts 10 | valuesFile: values.yaml 11 | namespace: openshift-gitops 12 | releaseName: product-catalog -------------------------------------------------------------------------------- /bootstrap/argocd/apps/overlays/rhpds/values.yaml: -------------------------------------------------------------------------------- 1 | default: 2 | app: 3 | enabled: true 4 | enableAutoSync: true 5 | autoSyncPrune: false 6 | project: apps-product-catalog 7 | labels: 8 | gitops.openshift.io/controllerNamespace: gitops 9 | destination: 10 | namespace: product-catalog-gitops 11 | server: https://kubernetes.default.svc 12 | source: 13 | repoURL: https://github.com/gnunn-gitops/product-catalog 14 | targetRevision: main 15 | 16 | applications: 17 | 18 | product-catalog-dev: 19 | destination: 20 | namespace: product-catalog-dev 21 | source: 22 | path: clusters/rhpds/overlays/dev 23 | 24 | product-catalog-test: 25 | destination: 26 | namespace: product-catalog-test 27 | source: 28 | path: clusters/rhpds/overlays/test 29 | 30 | product-catalog-prod: 31 | destination: 32 | namespace: product-catalog-prod 33 | source: 34 | path: clusters/rhpds/overlays/prod 35 | 36 | product-catalog-cicd: 37 | destination: 38 | namespace: product-catalog-cicd 39 | source: 40 | path: clusters/rhpds/overlays/cicd 41 | 42 | product-catalog-monitor: 43 | destination: 44 | namespace: product-catalog-monitor 45 | source: 46 | path: environments/overlays/monitor -------------------------------------------------------------------------------- /clusters/aws.cluster/overlays/cicd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | bases: 5 | - ../../../../environments/overlays/cicd 6 | 7 | resources: 8 | - pipeline-rolebinding.yaml -------------------------------------------------------------------------------- /clusters/aws.cluster/overlays/cicd/pipeline-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: cicd-pipeline-edit 5 | namespace: product-catalog-dev 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: edit 10 | subjects: 11 | - kind: ServiceAccount 12 | name: pipeline 13 | namespace: product-catalog-cicd 14 | --- 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: RoleBinding 17 | metadata: 18 | name: cicd-pipeline-edit 19 | namespace: product-catalog-test 20 | roleRef: 21 | apiGroup: rbac.authorization.k8s.io 22 | kind: ClusterRole 23 | name: edit 24 | subjects: 25 | - kind: ServiceAccount 26 | name: pipeline 27 | namespace: product-catalog-cicd 28 | -------------------------------------------------------------------------------- /clusters/aws.cluster/overlays/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | images: 5 | - name: quay.io/gnunn/client 6 | newTag: latest 7 | - name: quay.io/gnunn/server 8 | newName: quay.io/gnunn/server 9 | newTag: f08be80-110331912 10 | resources: 11 | - ../../../../environments/overlays/dev -------------------------------------------------------------------------------- /clusters/aws.cluster/overlays/pipelinerun/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-cicd 5 | 6 | resources: 7 | - ../../../../components/tekton/pipelineruns/client/base 8 | - ../../../../components/tekton/pipelineruns/server/base 9 | 10 | patches: 11 | - target: 12 | kind: PipelineRun 13 | patch: |- 14 | - op: add 15 | path: /spec/params/- 16 | value: 17 | name: cluster 18 | value: aws.cluster 19 | -------------------------------------------------------------------------------- /clusters/aws.cluster/overlays/prod/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | images: 5 | - name: quay.io/gnunn/client 6 | newTag: latest 7 | - name: quay.io/gnunn/server 8 | newName: quay.io/gnunn/server 9 | newTag: f08be80-110331912 10 | resources: 11 | - ../../../../environments/overlays/prod -------------------------------------------------------------------------------- /clusters/aws.cluster/overlays/test/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | images: 5 | - name: quay.io/gnunn/client 6 | newTag: latest 7 | - name: quay.io/gnunn/server 8 | newName: quay.io/gnunn/server 9 | newTag: f08be80-110331912 10 | resources: 11 | - ../../../../environments/overlays/test -------------------------------------------------------------------------------- /clusters/local.home/overlays/cicd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | bases: 5 | - ../../../../environments/overlays/cicd 6 | 7 | resources: 8 | - pipeline-rolebinding.yaml 9 | 10 | patches: 11 | - target: 12 | kind: TriggerTemplate 13 | name: server-post-prod 14 | patch: |- 15 | - op: add 16 | path: /spec/resourcetemplates/0/spec/workspaces/- 17 | value: 18 | name: slack-secret 19 | secret: 20 | secretName: slack-deployments-webhook 21 | - target: 22 | kind: TriggerTemplate 23 | name: server 24 | patch: |- 25 | - op: add 26 | path: /spec/resourcetemplates/0/spec/params/- 27 | value: 28 | name: cluster 29 | value: local.home 30 | - op: add 31 | path: /spec/resourcetemplates/0/spec/workspaces/- 32 | value: 33 | name: acs-central 34 | secret: 35 | secretName: roxsecrets 36 | - op: add 37 | path: /spec/resourcetemplates/0/spec/workspaces/- 38 | value: 39 | name: slack-secret 40 | secret: 41 | secretName: slack-deployments-webhook 42 | - target: 43 | kind: TriggerTemplate 44 | name: client 45 | patch: |- 46 | - op: add 47 | path: /spec/resourcetemplates/0/spec/params/- 48 | value: 49 | name: cluster 50 | value: local.home 51 | - op: add 52 | path: /spec/resourcetemplates/0/spec/workspaces/- 53 | value: 54 | name: acs-central 55 | secret: 56 | secretName: roxsecrets 57 | - op: add 58 | path: /spec/resourcetemplates/0/spec/workspaces/- 59 | value: 60 | name: slack-secret 61 | secret: 62 | secretName: slack-deployments-webhook 63 | 64 | # generatorOptions: 65 | # disableNameSuffixHash: true 66 | 67 | # secretGenerator: 68 | # - name: dest-docker-config 69 | # type: kubernetes.io/dockerconfigjson 70 | # files: 71 | # - secrets/.dockerconfigjson 72 | 73 | # patchesJson6902: 74 | # - path: patch-pvc-block.yaml 75 | # target: 76 | # group: "" 77 | # kind: PersistentVolumeClaim 78 | # name: m2-cache 79 | # version: v1 80 | # - path: patch-pvc-block.yaml 81 | # target: 82 | # group: "" 83 | # kind: PersistentVolumeClaim 84 | # name: npm-cache 85 | # version: v1 -------------------------------------------------------------------------------- /clusters/local.home/overlays/cicd/patch-pvc-block.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/storageClassName 3 | value: iscsi -------------------------------------------------------------------------------- /clusters/local.home/overlays/cicd/pipeline-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: cicd-pipeline-edit 5 | namespace: product-catalog-dev 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: edit 10 | subjects: 11 | - kind: ServiceAccount 12 | name: pipeline 13 | namespace: product-catalog-cicd 14 | --- 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: RoleBinding 17 | metadata: 18 | name: cicd-pipeline-edit 19 | namespace: product-catalog-test 20 | roleRef: 21 | apiGroup: rbac.authorization.k8s.io 22 | kind: ClusterRole 23 | name: edit 24 | subjects: 25 | - kind: ServiceAccount 26 | name: pipeline 27 | namespace: product-catalog-cicd 28 | --- 29 | apiVersion: rbac.authorization.k8s.io/v1 30 | kind: RoleBinding 31 | metadata: 32 | name: cicd-pipeline-edit 33 | namespace: product-catalog-prod 34 | roleRef: 35 | apiGroup: rbac.authorization.k8s.io 36 | kind: ClusterRole 37 | name: view 38 | subjects: 39 | - kind: ServiceAccount 40 | name: github-pipeline 41 | namespace: product-catalog-cicd -------------------------------------------------------------------------------- /clusters/local.home/overlays/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | images: 5 | - name: quay.io/gnunn/client 6 | newName: quay.io/gnunn/client 7 | newTag: fff9592-2fbdBT1Wxzx9uBS8uCeR2uyIsyr 8 | - name: quay.io/gnunn/server 9 | newName: quay.io/gnunn/server 10 | newTag: a5eaf2d-2vXnMar5rd141i41lJwrpmbYidT 11 | 12 | resources: 13 | - ../../../../environments/overlays/dev 14 | -------------------------------------------------------------------------------- /clusters/local.home/overlays/pipelinerun/apps/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-cicd 5 | 6 | resources: 7 | - ../../../../../components/tekton/pipelineruns/client/base 8 | - ../../../../../components/tekton/pipelineruns/server/base 9 | 10 | patches: 11 | - target: 12 | kind: PipelineRun 13 | patch: |- 14 | - op: add 15 | path: /spec/params/- 16 | value: 17 | name: cluster 18 | value: local.home 19 | - op: add 20 | path: /spec/workspaces/- 21 | value: 22 | name: slack-secret 23 | secret: 24 | secretName: slack-deployments-webhook 25 | - op: add 26 | path: /spec/workspaces/- 27 | value: 28 | name: acs-central 29 | secret: 30 | secretName: roxsecrets -------------------------------------------------------------------------------- /clusters/local.home/overlays/pipelinerun/push-to-prod-client/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../../../components/tekton/pipelineruns/push-prod-pr/base 3 | 4 | patches: 5 | - target: 6 | kind: PipelineRun 7 | patch: |- 8 | - op: add 9 | path: /spec/params/- 10 | value: 11 | name: cluster 12 | value: local.home 13 | - op: add 14 | path: /spec/params/- 15 | value: 16 | name: app 17 | value: client 18 | - op: add 19 | path: /spec/params/- 20 | value: 21 | name: image_dest_url 22 | value: quay.io/gnunn/client 23 | - op: add 24 | path: /spec/params/- 25 | value: 26 | name: image_dest_tag 27 | value: 7f192d4-1427400123 28 | - op: add 29 | path: /spec/params/- 30 | value: 31 | name: sonarqube_host 32 | value: sonarqube-dev-tools.apps.home.ocplab.com 33 | - op: add 34 | path: /spec/params/- 35 | value: 36 | name: acs_image_scan_results 37 | value: https://central-stackrox.apps.home.ocplab.com/main/vulnerability-management/images/7f192d4-1427400123 38 | 39 | -------------------------------------------------------------------------------- /clusters/local.home/overlays/pipelinerun/push-to-prod-server/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../../../components/tekton/pipelineruns/push-prod-pr/base 3 | 4 | patches: 5 | - target: 6 | kind: PipelineRun 7 | patch: |- 8 | - op: add 9 | path: /spec/params/- 10 | value: 11 | name: cluster 12 | value: local.home 13 | - op: add 14 | path: /spec/params/- 15 | value: 16 | name: app 17 | value: server 18 | - op: add 19 | path: /spec/params/- 20 | value: 21 | name: image_dest_url 22 | value: quay.io/gnunn/server 23 | - op: add 24 | path: /spec/params/- 25 | value: 26 | name: image_dest_tag 27 | value: 7f192d4-1427400123 28 | - op: add 29 | path: /spec/params/- 30 | value: 31 | name: sonarqube_host 32 | value: sonarqube-dev-tools.apps.home.ocplab.com 33 | - op: add 34 | path: /spec/params/- 35 | value: 36 | name: acs_image_scan_results 37 | value: https://central-stackrox.apps.home.ocplab.com/main/vulnerability-management/images/7f192d4-1427400123 38 | 39 | -------------------------------------------------------------------------------- /clusters/local.home/overlays/prod/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | images: 5 | - name: quay.io/gnunn/client 6 | newName: quay.io/gnunn/client 7 | newTag: 573225d-2Wwj1vjyzni1zhDinJsSfUs3y4y 8 | - name: quay.io/gnunn/server 9 | newName: quay.io/gnunn/server 10 | newTag: a5eaf2d-2vXnMar5rd141i41lJwrpmbYidT 11 | resources: 12 | - ../../../../environments/overlays/prod 13 | -------------------------------------------------------------------------------- /clusters/local.home/overlays/test/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | images: 5 | - name: quay.io/gnunn/client 6 | newName: quay.io/gnunn/client 7 | newTag: fff9592-2fbdBT1Wxzx9uBS8uCeR2uyIsyr 8 | - name: quay.io/gnunn/server 9 | newName: quay.io/gnunn/server 10 | newTag: a5eaf2d-2vXnMar5rd141i41lJwrpmbYidT 11 | resources: 12 | - ../../../../environments/overlays/test 13 | -------------------------------------------------------------------------------- /clusters/rhpds/overlays/cicd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | bases: 5 | - ../../../../environments/overlays/cicd 6 | 7 | resources: 8 | - pipeline-rolebinding.yaml 9 | 10 | patches: 11 | - target: 12 | kind: TriggerTemplate 13 | name: server 14 | patch: |- 15 | - op: add 16 | path: /spec/resourcetemplates/0/spec/params/- 17 | value: 18 | name: cluster 19 | value: local.home 20 | - op: add 21 | path: /spec/resourcetemplates/0/spec/params/- 22 | value: 23 | name: acs-secret 24 | value: roxsecrets 25 | - target: 26 | kind: TriggerTemplate 27 | name: client 28 | patch: |- 29 | - op: add 30 | path: /spec/resourcetemplates/0/spec/params/- 31 | value: 32 | name: cluster 33 | value: local.home 34 | - op: add 35 | path: /spec/resourcetemplates/0/spec/params/- 36 | value: 37 | name: acs-secret 38 | value: roxsecrets 39 | 40 | # generatorOptions: 41 | # disableNameSuffixHash: true 42 | 43 | # secretGenerator: 44 | # - name: dest-docker-config 45 | # type: kubernetes.io/dockerconfigjson 46 | # files: 47 | # - secrets/.dockerconfigjson 48 | 49 | # patchesJson6902: 50 | # - path: patch-pvc-block.yaml 51 | # target: 52 | # group: "" 53 | # kind: PersistentVolumeClaim 54 | # name: m2-cache 55 | # version: v1 56 | # - path: patch-pvc-block.yaml 57 | # target: 58 | # group: "" 59 | # kind: PersistentVolumeClaim 60 | # name: npm-cache 61 | # version: v1 -------------------------------------------------------------------------------- /clusters/rhpds/overlays/cicd/pipeline-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: cicd-pipeline-edit 5 | namespace: product-catalog-dev 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: edit 10 | subjects: 11 | - kind: ServiceAccount 12 | name: pipeline 13 | namespace: product-catalog-cicd 14 | --- 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: RoleBinding 17 | metadata: 18 | name: cicd-pipeline-edit 19 | namespace: product-catalog-test 20 | roleRef: 21 | apiGroup: rbac.authorization.k8s.io 22 | kind: ClusterRole 23 | name: edit 24 | subjects: 25 | - kind: ServiceAccount 26 | name: pipeline 27 | namespace: product-catalog-cicd 28 | -------------------------------------------------------------------------------- /clusters/rhpds/overlays/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | images: 5 | - name: quay.io/gnunn/client 6 | newName: quay.io/gnunn/client 7 | newTag: b8a2b2a-4074585724 8 | - name: quay.io/gnunn/server 9 | newName: quay.io/gnunn/server 10 | newTag: 7f192d4-1427400123 11 | resources: 12 | - ../../../../environments/overlays/dev 13 | -------------------------------------------------------------------------------- /clusters/rhpds/overlays/pipelinerun/apps/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-cicd 5 | 6 | resources: 7 | - ../../../../../components/tekton/pipelineruns/client/base 8 | - ../../../../../components/tekton/pipelineruns/server/base 9 | 10 | patches: 11 | - target: 12 | kind: PipelineRun 13 | patch: |- 14 | - op: add 15 | path: /spec/params/- 16 | value: 17 | name: cluster 18 | value: local.home 19 | - op: add 20 | path: /spec/params/- 21 | value: 22 | name: acs-secret 23 | value: roxsecrets 24 | - op: add 25 | path: /spec/params/- 26 | value: 27 | name: slack-secret 28 | value: slack-deployments-webhook -------------------------------------------------------------------------------- /clusters/rhpds/overlays/pipelinerun/push-to-prod-client/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../../../components/tekton/pipelineruns/push-prod-pr/base 3 | 4 | patches: 5 | - target: 6 | kind: PipelineRun 7 | patch: |- 8 | - op: add 9 | path: /spec/params/- 10 | value: 11 | name: cluster 12 | value: local.home 13 | - op: add 14 | path: /spec/params/- 15 | value: 16 | name: app 17 | value: client 18 | - op: add 19 | path: /spec/params/- 20 | value: 21 | name: image_dest_url 22 | value: quay.io/gnunn/client 23 | - op: add 24 | path: /spec/params/- 25 | value: 26 | name: image_dest_tag 27 | value: 7f192d4-1427400123 28 | - op: add 29 | path: /spec/params/- 30 | value: 31 | name: sonarqube_host 32 | value: sonarqube-dev-tools.apps.cluster-xjv8v.xjv8v.sandbox1202.opentlc.com 33 | - op: add 34 | path: /spec/params/- 35 | value: 36 | name: acs_image_scan_results 37 | value: https://central-stackrox.apps.cluster-xjv8v.xjv8v.sandbox1202.opentlc.com/vulnerability-management/images/7f192d4-1427400123 38 | 39 | -------------------------------------------------------------------------------- /clusters/rhpds/overlays/pipelinerun/push-to-prod-server/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../../../../components/tekton/pipelineruns/push-prod-pr/base 3 | 4 | patches: 5 | - target: 6 | kind: PipelineRun 7 | patch: |- 8 | - op: add 9 | path: /spec/params/- 10 | value: 11 | name: cluster 12 | value: local.home 13 | - op: add 14 | path: /spec/params/- 15 | value: 16 | name: app 17 | value: server 18 | - op: add 19 | path: /spec/params/- 20 | value: 21 | name: image_dest_url 22 | value: quay.io/gnunn/server 23 | - op: add 24 | path: /spec/params/- 25 | value: 26 | name: image_dest_tag 27 | value: 7f192d4-1427400123 28 | - op: add 29 | path: /spec/params/- 30 | value: 31 | name: sonarqube_host 32 | value: sonarqube-dev-tools.apps.cluster-xjv8v.xjv8v.sandbox1202.opentlc.com 33 | - op: add 34 | path: /spec/params/- 35 | value: 36 | name: acs_image_scan_results 37 | value: https://central-stackrox.apps.cluster-xjv8v.xjv8v.sandbox1202.opentlc.com/main/vulnerability-management/images/7f192d4-1427400123 38 | 39 | -------------------------------------------------------------------------------- /clusters/rhpds/overlays/prod/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | images: 5 | - name: quay.io/gnunn/client 6 | newName: quay.io/gnunn/client 7 | newTag: b8a2b2a-135791192 8 | - name: quay.io/gnunn/server 9 | newName: quay.io/gnunn/server 10 | newTag: 7f192d4-806338677 11 | resources: 12 | - ../../../../environments/overlays/prod 13 | -------------------------------------------------------------------------------- /clusters/rhpds/overlays/test/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | images: 5 | - name: quay.io/gnunn/client 6 | newName: quay.io/gnunn/client 7 | newTag: b8a2b2a-4074585724 8 | - name: quay.io/gnunn/server 9 | newName: quay.io/gnunn/server 10 | newTag: 7f192d4-1427400123 11 | resources: 12 | - ../../../../environments/overlays/test 13 | -------------------------------------------------------------------------------- /components/apps/client/base/client-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: client 5 | annotations: 6 | app.openshift.io/connects-to: server 7 | app.openshift.io/vcs-ref: master 8 | app.openshift.io/vcs-uri: 'https://github.com/gnunn-gitops/product-catalog-client' 9 | labels: 10 | app: client 11 | app.kubernetes.io/name: client 12 | app.kubernetes.io/component: frontend 13 | app.kubernetes.io/instance: client 14 | app.openshift.io/runtime: nodejs 15 | app.kubernetes.io/part-of: product-catalog 16 | spec: 17 | selector: 18 | matchLabels: 19 | name: client 20 | replicas: 1 21 | template: 22 | metadata: 23 | labels: 24 | name: client 25 | spec: 26 | containers: 27 | - name: client 28 | image: quay.io/gnunn/client:latest 29 | imagePullPolicy: Always 30 | ports: 31 | - containerPort: 8080 32 | protocol: TCP 33 | name: http 34 | # volumeMounts: 35 | # - mountPath: /opt/app-root/src/config.js 36 | # name: config-data 37 | # subPath: config.js 38 | resources: 39 | requests: 40 | memory: "50Mi" 41 | cpu: "50m" 42 | limits: 43 | memory: "100Mi" 44 | livenessProbe: 45 | httpGet: 46 | path: /health.html 47 | port: 8080 48 | initialDelaySeconds: 3 49 | periodSeconds: 10 50 | readinessProbe: 51 | httpGet: 52 | path: /health.html 53 | port: 8080 54 | scheme: HTTP 55 | timeoutSeconds: 1 56 | periodSeconds: 10 57 | successThreshold: 1 58 | failureThreshold: 3 59 | terminationMessagePath: /dev/termination-log 60 | terminationMessagePolicy: File 61 | # volumes: 62 | # - configMap: 63 | # name: client 64 | # name: config-data 65 | -------------------------------------------------------------------------------- /components/apps/client/base/client-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | name: client 5 | labels: 6 | app: client 7 | app.kubernetes.io/name: client 8 | app.kubernetes.io/component: frontend 9 | app.kubernetes.io/instance: client 10 | endpoint: client 11 | spec: 12 | tls: 13 | termination: edge 14 | insecureEdgeTerminationPolicy: Redirect 15 | port: 16 | targetPort: http 17 | to: 18 | kind: Service 19 | name: client 20 | -------------------------------------------------------------------------------- /components/apps/client/base/client-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: client 5 | labels: 6 | app: client 7 | app.kubernetes.io/name: client 8 | app.kubernetes.io/component: frontend 9 | app.kubernetes.io/instance: client 10 | spec: 11 | ports: 12 | - name: http 13 | port: 8080 14 | selector: 15 | name: client 16 | sessionAffinity: None 17 | type: ClusterIP -------------------------------------------------------------------------------- /components/apps/client/base/config/config.js: -------------------------------------------------------------------------------- 1 | window.ENV = { 2 | "API_URL":"http://localhost:8080", 3 | "API_KEY":" 43fbc9b33302db65d58553e27d1679ac" 4 | } -------------------------------------------------------------------------------- /components/apps/client/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | # generatorOptions: 5 | # disableNameSuffixHash: true 6 | # labels: 7 | # app.kubernetes.io/part-of: product-catalog 8 | 9 | # configMapGenerator: 10 | # - name: client 11 | # files: 12 | # - config/config.js 13 | 14 | resources: 15 | - client-service.yaml 16 | - client-route.yaml 17 | - client-deployment.yaml -------------------------------------------------------------------------------- /components/apps/database/base/config/90-init-database.sh: -------------------------------------------------------------------------------- 1 | init_database() { 2 | local thisdir 3 | local init_data_file 4 | thisdir=$(dirname ${BASH_SOURCE[0]}) 5 | 6 | init_data_file=$(readlink -f ${thisdir}/../mysql-data/schema.sql) 7 | log_info "Initializing the database schema from file ${init_data_file}..." 8 | mysql $mysql_flags ${MYSQL_DATABASE} < ${init_data_file} 9 | 10 | init_data_file=$(readlink -f ${thisdir}/../mysql-data/import.sql) 11 | log_info "Initializing the database data from file ${init_data_file}..." 12 | mysql $mysql_flags ${MYSQL_DATABASE} < ${init_data_file} 13 | } 14 | 15 | if ! [ -v MYSQL_RUNNING_AS_SLAVE ] && $MYSQL_DATADIR_FIRST_INIT ; then 16 | init_database 17 | fi -------------------------------------------------------------------------------- /components/apps/database/base/config/import.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `categories` (`id`, `name`, `description`, `created`, `modified`) VALUES 2 | (1, 'Smartphone', 'Not a stupid phone', '2015-08-02 23:56:46', '2016-12-20 06:51:25'), 3 | (2, 'Tablet', 'A small smartphone-laptop mix', '2015-08-02 23:56:46', '2016-12-20 06:51:42'), 4 | (3, 'Ultrabook', 'Ultra portable and powerful laptop', '2016-12-20 13:51:15', '2016-12-20 06:51:52'); 5 | 6 | INSERT INTO `products` (`id`, `name`, `description`, `price`, `category_id`, `created`, `modified`) VALUES 7 | (1, 'ASUS Zenbook 3', 'The most powerful and ultraportable Zenbook ever', 1799, 3, '2016-12-20 13:53:00', '2016-12-20 06:53:00'), 8 | (2, 'Dell XPS 13', 'Super powerful and portable ultrabook with ultra thin bezel infinity display', 2199, 3, '2016-12-20 13:53:34', '2016-12-20 06:53:34'), 9 | (3, 'Samsung Galaxy S7', 'Define what a phone can do', 649, 1, '2016-12-20 13:54:16', '2016-12-20 06:54:16'), 10 | (4, 'Samsung Galaxy Tab S2', 'Latest Generation of Samsung Galaxy Tab Series tablet', 599, 2, '2016-12-20 13:54:43', '2016-12-20 06:54:43'), 11 | (5, 'Apple iPad Pro', 'Powerful, thin, and light tablet from Apple', 899, 2, '2016-12-20 13:55:02', '2016-12-20 06:55:02'), 12 | (6, 'Google Pixel', 'World''s leading smartphone camera, first phone by Google.', 649, 1, '2016-12-20 13:55:23', '2016-12-20 06:55:23'), 13 | (7, 'Oneplus 3T', 'Never Settle', 439, 1, '2016-12-20 13:59:06', '2016-12-20 06:59:06'), 14 | (8, 'Asus Zenfone 3 Deluxe', 'Super powerful and gorgeously designed phablet', 799, 1, '2016-12-20 13:59:37', '2016-12-20 06:59:37'), 15 | (9, 'Xiaomi Mi Mix', 'Bezelless. Powerful. Gorgeous.', 699, 1, '2016-12-20 14:00:20', '2016-12-20 07:00:20'), 16 | (10, 'Huawei P9', 'First Leica Branded Dual-camera Smartphone', 499, 1, '2016-12-20 14:00:45', '2016-12-20 07:00:45'), 17 | (11, 'Xiaomi Mi 5S', 'First Xiaomi smartphone to come with light-sensitive camera', 349, 1, '2016-12-20 14:01:40', '2016-12-20 07:10:14'), 18 | (12, 'LG V20', 'Superb dual camera, Space-grade Aluminum build, fantastic sound quality', 749, 1, '2016-12-20 14:02:28', '2016-12-20 07:02:28'); 19 | 20 | INSERT INTO `users` (`id`, `email`, `password_hash`, `salt`, `iteration_count`, `created_at`) VALUES 21 | (1, 'demo@demo.com', '/TVyvAPSryEfGlyEFmNq14Q/prbJU7U=', 'YYONLJPUCmUeISgDxyTREg==', 10, '2019-11-23 02:54:42'); 22 | -------------------------------------------------------------------------------- /components/apps/database/base/config/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS `categories`; 2 | 3 | CREATE TABLE `categories` ( 4 | `id` int(11) NOT NULL AUTO_INCREMENT, 5 | `created` date DEFAULT NULL, 6 | `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 7 | `modified` datetime(6) DEFAULT NULL, 8 | `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL, 9 | PRIMARY KEY (`id`) 10 | ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 11 | 12 | -- 13 | -- Table structure for table `products` 14 | -- 15 | 16 | DROP TABLE IF EXISTS `products`; 17 | 18 | CREATE TABLE `products` ( 19 | `id` int(11) NOT NULL AUTO_INCREMENT, 20 | `created` date DEFAULT NULL, 21 | `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 22 | `modified` datetime(6) DEFAULT NULL, 23 | `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL, 24 | `price` double NOT NULL, 25 | `category_id` int(11) NOT NULL, 26 | PRIMARY KEY (`id`), 27 | KEY `FKog2rp4qthbtt2lfyhfo32lsw9` (`category_id`), 28 | CONSTRAINT `FKog2rp4qthbtt2lfyhfo32lsw9` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) 29 | ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 30 | 31 | -- 32 | -- Table structure for table `users` 33 | -- 34 | 35 | DROP TABLE IF EXISTS `users`; 36 | 37 | CREATE TABLE `users` ( 38 | `id` int(11) NOT NULL AUTO_INCREMENT, 39 | `created_at` datetime(6) DEFAULT NULL, 40 | `email` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 41 | `iteration_count` int(11) DEFAULT NULL, 42 | `password_hash` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 43 | `salt` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 44 | PRIMARY KEY (`id`) 45 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -------------------------------------------------------------------------------- /components/apps/database/base/db-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: productdb 5 | labels: 6 | app: productdb 7 | app.kubernetes.io/name: productdb 8 | app.kubernetes.io/component: database 9 | app.kubernetes.io/part-of: product-catalog 10 | app.kubernetes.io/instance: database 11 | app.openshift.io/runtime: mariadb 12 | spec: 13 | replicas: 1 14 | selector: 15 | matchLabels: 16 | name: productdb 17 | strategy: 18 | type: Recreate 19 | template: 20 | metadata: 21 | annotations: 22 | alpha.image.policy.openshift.io/resolve-names: '*' 23 | labels: 24 | name: productdb 25 | spec: 26 | containers: 27 | - name: productdb 28 | image: registry.redhat.io/rhel8/mariadb-103:1 29 | imagePullPolicy: Always 30 | resources: 31 | requests: 32 | memory: "100Mi" 33 | cpu: "100m" 34 | limits: 35 | memory: "250Mi" 36 | ports: 37 | - containerPort: 3306 38 | env: 39 | - name: MYSQL_USER 40 | valueFrom: 41 | secretKeyRef: 42 | name: productdb 43 | key: database-user 44 | - name: MYSQL_PASSWORD 45 | valueFrom: 46 | secretKeyRef: 47 | name: productdb 48 | key: database-password 49 | - name: MYSQL_ROOT_PASSWORD 50 | valueFrom: 51 | secretKeyRef: 52 | name: productdb 53 | key: database-root-password 54 | - name: MYSQL_DATABASE 55 | valueFrom: 56 | secretKeyRef: 57 | name: productdb 58 | key: database-name 59 | readinessProbe: 60 | exec: 61 | command: 62 | - /bin/sh 63 | - -i 64 | - -c 65 | - MYSQL_PWD="$MYSQL_PASSWORD" mysql -h 127.0.0.1 -u $MYSQL_USER -D $MYSQL_DATABASE 66 | -e 'SELECT 1' 67 | initialDelaySeconds: 5 68 | timeoutSeconds: 1 69 | livenessProbe: 70 | initialDelaySeconds: 30 71 | tcpSocket: 72 | port: 3306 73 | timeoutSeconds: 1 74 | volumeMounts: 75 | - mountPath: /var/lib/mysql/data 76 | name: productdb-data 77 | - mountPath: /opt/app-root/src/mysql-init/90-init-data.sh 78 | name: productdb-init 79 | subPath: 90-init-database.sh 80 | - mountPath: /opt/app-root/src/mysql-data/import.sql 81 | name: productdb-init 82 | subPath: import.sql 83 | - mountPath: /opt/app-root/src/mysql-data/schema.sql 84 | name: productdb-init 85 | subPath: schema.sql 86 | volumes: 87 | - name: productdb-data 88 | persistentVolumeClaim: 89 | claimName: productdb 90 | - configMap: 91 | name: productdb-init 92 | name: productdb-init -------------------------------------------------------------------------------- /components/apps/database/base/db-pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: productdb 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: "1Gi" -------------------------------------------------------------------------------- /components/apps/database/base/db-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | annotations: 5 | template.openshift.io/expose-database_name: '{.data[''database-name'']}' 6 | template.openshift.io/expose-password: '{.data[''database-password'']}' 7 | template.openshift.io/expose-root_password: '{.data[''database-root-password'']}' 8 | template.openshift.io/expose-username: '{.data[''database-user'']}' 9 | labels: 10 | app: server 11 | app.kubernetes.io/name: productdb 12 | app.kubernetes.io/component: database 13 | app.kubernetes.io/instance: productdb 14 | app.kubernetes.io/part-of: product-catalog 15 | name: productdb 16 | stringData: 17 | database-name: productdb 18 | database-password: Demo1234 19 | database-root-password: Demo1234 20 | database-user: productdb 21 | -------------------------------------------------------------------------------- /components/apps/database/base/db-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: productdb 5 | labels: 6 | app: server 7 | app.kubernetes.io/name: productdb 8 | app.kubernetes.io/component: database 9 | app.kubernetes.io/instance: productdb 10 | app.kubernetes.io/part-of: product-catalog 11 | spec: 12 | ports: 13 | - name: tcp 14 | port: 3306 15 | selector: 16 | name: productdb -------------------------------------------------------------------------------- /components/apps/database/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | labels: 7 | app.kubernetes.io/part-of: product-catalog 8 | 9 | configMapGenerator: 10 | - name: productdb-init 11 | files: 12 | - config/90-init-database.sh 13 | - config/import.sql 14 | - config/schema.sql 15 | 16 | resources: 17 | - db-pvc.yaml 18 | - db-secret.yaml 19 | - db-service.yaml 20 | - db-deployment.yaml -------------------------------------------------------------------------------- /components/apps/monitor/base/grafana-quarkus-dashboard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: grafana.integreatly.org/v1beta1 2 | kind: GrafanaDashboard 3 | metadata: 4 | name: quarkus-dashboard 5 | labels: 6 | app: grafana 7 | spec: 8 | instanceSelector: 9 | matchLabels: 10 | instance: grafana 11 | folder: product-catalog 12 | url: https://raw.githubusercontent.com/gnunn-gitops/product-catalog/main/components/apps/monitor/base/quarkus-dashboard.json 13 | -------------------------------------------------------------------------------- /components/apps/monitor/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - grafana-quarkus-dashboard.yaml -------------------------------------------------------------------------------- /components/apps/server/base/config/application.properties: -------------------------------------------------------------------------------- 1 | # Product Datasource 2 | quarkus.datasource.db-kind=mariadb 3 | quarkus.datasource.jdbc.url=jdbc:mariadb://productdb:3306/productdb 4 | quarkus.datasource.username=productdb 5 | quarkus.datasource.password=Demo1234 6 | quarkus.datasource.jdbc.min-size=1 7 | quarkus.datasource.jdbc.max-size=5 8 | quarkus.hibernate-orm.database.generation=none 9 | 10 | # CORS 11 | quarkus.http.cors=true 12 | -------------------------------------------------------------------------------- /components/apps/server/base/default-view-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: default-view 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: view 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default -------------------------------------------------------------------------------- /components/apps/server/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | labels: 7 | app.kubernetes.io/part-of: product-catalog 8 | 9 | configMapGenerator: 10 | - name: server 11 | files: 12 | - config/application.properties 13 | 14 | resources: 15 | - default-view-rolebinding.yaml 16 | - server-service.yaml 17 | - server-route.yaml 18 | - server-deployment.yaml 19 | - server-sm.yaml -------------------------------------------------------------------------------- /components/apps/server/base/server-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Note not currently working, pods constantly terminating and restarting 2 | # Using DC for now but preserved here to come back to later 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: server 7 | annotations: 8 | app.openshift.io/connects-to: database 9 | app.openshift.io/vcs-ref: master 10 | app.openshift.io/vcs-uri: 'https://github.com/gnunn-gitops/product-catalog-server' 11 | labels: 12 | app: server 13 | app.kubernetes.io/name: server 14 | app.kubernetes.io/component: backend 15 | app.kubernetes.io/instance: server 16 | app.openshift.io/runtime: quarkus 17 | app.kubernetes.io/part-of: product-catalog 18 | spec: 19 | selector: 20 | matchLabels: 21 | name: server 22 | replicas: 1 23 | template: 24 | metadata: 25 | labels: 26 | name: server 27 | quarkus-prometheus: "true" 28 | spec: 29 | containers: 30 | - name: server 31 | env: 32 | - name: QUARKUS_KUBERNETES_CONFIG_ENABLED 33 | value: "true" 34 | - name: QUARKUS_KUBERNETES_CONFIG_CONFIG_MAPS 35 | value: server 36 | # - name: JAVA_OPTIONS 37 | # value: >- 38 | # -Dquarkus.kubernetes-config.enabled=true 39 | # -Dquarkus.kubernetes-config.config-maps=server 40 | image: quay.io/gnunn/server:latest 41 | imagePullPolicy: Always 42 | ports: 43 | - containerPort: 8080 44 | protocol: TCP 45 | name: http 46 | - containerPort: 8443 47 | protocol: TCP 48 | - containerPort: 8778 49 | protocol: TCP 50 | resources: 51 | requests: 52 | memory: "250Mi" 53 | cpu: "100m" 54 | limits: 55 | memory: "512Mi" 56 | livenessProbe: 57 | httpGet: 58 | path: /q/health/live 59 | port: 8080 60 | initialDelaySeconds: 3 61 | periodSeconds: 10 62 | readinessProbe: 63 | httpGet: 64 | path: /q/health/ready 65 | port: 8080 66 | scheme: HTTP 67 | timeoutSeconds: 1 68 | periodSeconds: 10 69 | successThreshold: 1 70 | failureThreshold: 3 71 | terminationMessagePath: /dev/termination-log 72 | terminationMessagePolicy: File -------------------------------------------------------------------------------- /components/apps/server/base/server-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | name: server 5 | labels: 6 | app: server 7 | app.kubernetes.io/name: server 8 | app.kubernetes.io/component: backend 9 | app.kubernetes.io/instance: server 10 | app.kubernetes.io/part-of: product-catalog 11 | endpoint: server 12 | spec: 13 | tls: 14 | termination: edge 15 | insecureEdgeTerminationPolicy: Redirect 16 | port: 17 | targetPort: http 18 | to: 19 | kind: Service 20 | name: server 21 | -------------------------------------------------------------------------------- /components/apps/server/base/server-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: server 5 | labels: 6 | app: server 7 | app.kubernetes.io/name: server 8 | app.kubernetes.io/component: backend 9 | app.kubernetes.io/instance: server 10 | app.kubernetes.io/part-of: product-catalog 11 | quarkus-prometheus: "true" 12 | spec: 13 | ports: 14 | - name: http 15 | port: 8080 16 | selector: 17 | name: server 18 | sessionAffinity: None 19 | type: ClusterIP -------------------------------------------------------------------------------- /components/apps/server/base/server-sm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: monitoring.coreos.com/v1 2 | kind: ServiceMonitor 3 | metadata: 4 | name: server 5 | spec: 6 | selector: 7 | matchLabels: 8 | quarkus-prometheus: "true" 9 | endpoints: 10 | - port: http 11 | path: /q/metrics 12 | scheme: http -------------------------------------------------------------------------------- /components/apps/slack-message-handler/base/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | annotations: 5 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"el-slack-message"}]' 6 | labels: 7 | app: slack-message-handler 8 | app.kubernetes.io/component: slack-message-handler 9 | app.kubernetes.io/instance: slack-message-handler 10 | app.kubernetes.io/name: slack-message-handler 11 | app.openshift.io/runtime: quarkus 12 | app.openshift.io/runtime-namespace: product-catalog-cicd 13 | name: slack-message-handler 14 | spec: 15 | progressDeadlineSeconds: 600 16 | replicas: 1 17 | revisionHistoryLimit: 10 18 | selector: 19 | matchLabels: 20 | app: slack-message-handler 21 | strategy: 22 | rollingUpdate: 23 | maxSurge: 25% 24 | maxUnavailable: 25% 25 | type: RollingUpdate 26 | template: 27 | metadata: 28 | annotations: 29 | openshift.io/generated-by: OpenShiftWebConsole 30 | labels: 31 | app: slack-message-handler 32 | deployment: slack-message-handler 33 | spec: 34 | containers: 35 | - image: quay.io/gnunn/slack-message-handler:latest 36 | imagePullPolicy: Always 37 | name: slack-message-handler 38 | resources: 39 | requests: 40 | memory: "200Mi" 41 | cpu: "50m" 42 | limits: 43 | memory: "512Mi" 44 | livenessProbe: 45 | httpGet: 46 | path: /q/health/live 47 | port: 8080 48 | initialDelaySeconds: 3 49 | periodSeconds: 10 50 | readinessProbe: 51 | httpGet: 52 | path: /q/health/ready 53 | port: 8080 54 | scheme: HTTP 55 | timeoutSeconds: 1 56 | periodSeconds: 10 57 | successThreshold: 1 58 | failureThreshold: 3 59 | ports: 60 | - containerPort: 8080 61 | protocol: TCP 62 | - containerPort: 8443 63 | protocol: TCP 64 | - containerPort: 8778 65 | protocol: TCP 66 | terminationMessagePath: /dev/termination-log 67 | terminationMessagePolicy: File 68 | dnsPolicy: ClusterFirst 69 | restartPolicy: Always 70 | schedulerName: default-scheduler 71 | terminationGracePeriodSeconds: 30 72 | -------------------------------------------------------------------------------- /components/apps/slack-message-handler/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - service.yaml 3 | - deployment.yaml 4 | - route.yaml -------------------------------------------------------------------------------- /components/apps/slack-message-handler/base/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: route.openshift.io/v1 2 | kind: Route 3 | metadata: 4 | labels: 5 | app: slack-message-handler 6 | app.kubernetes.io/component: slack-message-handler 7 | app.kubernetes.io/instance: slack-message-handler 8 | app.kubernetes.io/name: slack-message-handler 9 | app.openshift.io/runtime-version: latest 10 | name: slack-message-handler 11 | spec: 12 | port: 13 | targetPort: 8080-tcp 14 | tls: 15 | insecureEdgeTerminationPolicy: Redirect 16 | termination: edge 17 | to: 18 | kind: Service 19 | name: slack-message-handler 20 | weight: 100 21 | wildcardPolicy: None 22 | -------------------------------------------------------------------------------- /components/apps/slack-message-handler/base/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"el-slack-message"}]' 6 | labels: 7 | app: slack-message-handler 8 | app.kubernetes.io/component: slack-message-handler 9 | app.kubernetes.io/instance: slack-message-handler 10 | app.kubernetes.io/name: slack-message-handler 11 | app.openshift.io/runtime-version: latest 12 | name: slack-message-handler 13 | spec: 14 | ports: 15 | - name: 8080-tcp 16 | port: 8080 17 | - name: 8443-tcp 18 | port: 8443 19 | - name: 8778-tcp 20 | port: 8778 21 | selector: 22 | app: slack-message-handler 23 | deployment: slack-message-handler 24 | -------------------------------------------------------------------------------- /components/tekton/pipelines/client/base/client-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Pipeline 3 | metadata: 4 | name: client 5 | spec: 6 | workspaces: 7 | - name: git-source 8 | - name: gitops-manifests 9 | - name: slack-secret 10 | optional: true 11 | - name: acs-central 12 | optional: true 13 | params: 14 | - name: source_git_revision 15 | type: string 16 | default: master 17 | - name: source_git_url 18 | type: string 19 | default: https://github.com/gnunn-gitops/product-catalog-client 20 | - name: gitops_git_revision 21 | type: string 22 | default: main 23 | - name: gitops_git_url 24 | type: string 25 | default: https://github.com/gnunn-gitops/product-catalog 26 | - name: image_dest_url 27 | type: string 28 | description: External registry location to copy image to 29 | default: quay.io/gnunn/client 30 | - name: cluster 31 | type: string 32 | description: Cluster to push to 33 | default: "local.home" 34 | - name: sonarqube_host 35 | default: sonarqube-dev-tools.apps.hub.ocplab.com 36 | description: Where sonarqube is located to support project scanning 37 | tasks: 38 | - name: acquire-lease 39 | taskRef: 40 | kind: Task 41 | params: 42 | - name: pathInRepo 43 | value: task/acquire-lease/1.0/acquire-lease.yaml 44 | resolver: git 45 | params: 46 | - name: lease-name 47 | value: "$(context.pipeline.name)" 48 | - name: owner 49 | value: "$(context.pipelineRun.name)" 50 | - name: variables 51 | taskRef: 52 | name: variables-from-k8s 53 | runAfter: 54 | - acquire-lease 55 | workspaces: 56 | - name: acs-central 57 | workspace: acs-central 58 | - name: clone 59 | taskRef: 60 | resolver: cluster 61 | params: 62 | - name: kind 63 | value: task 64 | - name: name 65 | value: git-clone 66 | - name: namespace 67 | value: openshift-pipelines 68 | runAfter: 69 | - variables 70 | workspaces: 71 | - name: output 72 | workspace: git-source 73 | params: 74 | - name: URL 75 | value: "$(params.source_git_url)" 76 | - name: REVISION 77 | value: "$(params.source_git_revision)" 78 | - name: DELETE_EXISTING 79 | value: "true" 80 | - name: generate-id 81 | taskRef: 82 | kind: Task 83 | params: 84 | - name: pathInRepo 85 | value: task/generate-build-id/1.0/generate-build-id.yaml 86 | resolver: git 87 | runAfter: 88 | - clone 89 | workspaces: 90 | - name: source 91 | workspace: git-source 92 | - name: build 93 | taskRef: 94 | name: npm 95 | kind: Task 96 | runAfter: 97 | - generate-id 98 | workspaces: 99 | - name: source 100 | workspace: git-source 101 | - name: quality 102 | taskRef: 103 | name: npm-quality 104 | kind: Task 105 | runAfter: 106 | - build 107 | workspaces: 108 | - name: source 109 | workspace: git-source 110 | - name: build-image 111 | taskRef: 112 | name: buildah 113 | kind: Task 114 | workspaces: 115 | - name: source 116 | workspace: git-source 117 | runAfter: 118 | - quality 119 | params: 120 | - name: IMAGE 121 | value: $(params.image_dest_url):latest 122 | - name: DOCKERFILE 123 | value: ./Containerfile 124 | - name: acs-scan-image 125 | taskRef: 126 | kind: Task 127 | params: 128 | - name: pathInRepo 129 | value: task/acs-image-scan/1.0/acs-image-scan.yaml 130 | resolver: git 131 | runAfter: 132 | - build-image 133 | params: 134 | - name: output_format 135 | value: table 136 | - name: image 137 | value: $(params.image_dest_url)@$(tasks.build-image.results.IMAGE_DIGEST) 138 | when: 139 | - input: "$(workspaces.acs-central.bound)" 140 | operator: in 141 | values: ["true"] 142 | workspaces: 143 | - name: acs-central 144 | workspace: acs-central 145 | - name: acs-check-image 146 | taskRef: 147 | kind: Task 148 | params: 149 | - name: pathInRepo 150 | value: task/acs-image-check/1.0/acs-image-check.yaml 151 | resolver: git 152 | runAfter: 153 | - acs-scan-image 154 | params: 155 | - name: image 156 | value: quay.io/gnunn/client@$(tasks.build-image.results.IMAGE_DIGEST) 157 | when: 158 | - input: "$(workspaces.acs-central.bound)" 159 | operator: in 160 | values: ["true"] 161 | workspaces: 162 | - name: acs-central 163 | workspace: acs-central 164 | - name: notify-on-scan-fail 165 | taskRef: 166 | name: send-to-webhook-slack 167 | kind: Task 168 | runAfter: 169 | - acs-check-image 170 | when: 171 | - input: "$(tasks.acs-check-image.results.check_passed)" 172 | operator: in 173 | values: ["false"] 174 | params: 175 | - name: message 176 | value: |- 177 | *Client Image Scan Failed* 178 | The image scan for client:$(tasks.generate-id.results.tag_id) failed, please view test results here: https://acs_central_host/main/vulnerability-management/images/$(tasks.build-image.results.IMAGE_DIGEST) 179 | workspaces: 180 | - name: slack-secret 181 | workspace: slack-secret 182 | - name: tag-dev-image 183 | taskRef: 184 | name: push-image 185 | kind: Task 186 | runAfter: 187 | - build-image 188 | params: 189 | - name: src_image 190 | value: $(params.image_dest_url)@$(tasks.build-image.results.IMAGE_DIGEST) 191 | - name: dest_image 192 | value: $(params.image_dest_url) 193 | - name: dest_tags 194 | value: $(tasks.generate-id.results.tag_id),dev 195 | - name: clone-gitops-manifests 196 | taskRef: 197 | resolver: cluster 198 | params: 199 | - name: kind 200 | value: task 201 | - name: name 202 | value: git-clone 203 | - name: namespace 204 | value: openshift-pipelines 205 | runAfter: 206 | - tag-dev-image 207 | workspaces: 208 | - name: output 209 | workspace: gitops-manifests 210 | params: 211 | - name: URL 212 | value: "$(params.gitops_git_url)" 213 | - name: REVISION 214 | value: "$(params.gitops_git_revision)" 215 | - name: DELETE_EXISTING 216 | value: "true" 217 | - name: dev-update-image 218 | taskRef: 219 | name: update-image 220 | runAfter: 221 | - clone-gitops-manifests 222 | params: 223 | - name: git_revision 224 | value: "$(params.gitops_git_revision)" 225 | - name: path 226 | value: clusters/$(params.cluster)/overlays/dev 227 | - name: image 228 | value: "$(params.image_dest_url)" 229 | - name: image_tag 230 | value: $(tasks.generate-id.results.tag_id) 231 | workspaces: 232 | - name: gitops-manifests 233 | workspace: gitops-manifests 234 | - name: dev-gitops-deploy 235 | taskRef: 236 | kind: Task 237 | params: 238 | - name: pathInRepo 239 | value: task/argocd-sync-and-wait/1.0/argocd-sync-and-wait.yaml 240 | resolver: git 241 | runAfter: 242 | - dev-update-image 243 | params: 244 | - name: application_name 245 | value: product-catalog-gitops/product-catalog-dev 246 | - name: revision 247 | value: $(params.gitops_git_revision) 248 | - name: deployment 249 | value: client 250 | - name: namespace 251 | value: product-catalog-dev 252 | - name: image_tag 253 | value: $(tasks.generate-id.results.tag_id) 254 | - name: tag-test-image 255 | taskRef: 256 | name: push-image 257 | kind: Task 258 | runAfter: 259 | - dev-gitops-deploy 260 | params: 261 | - name: src_image 262 | value: $(params.image_dest_url)@$(tasks.build-image.results.IMAGE_DIGEST) 263 | - name: dest_image 264 | value: $(params.image_dest_url) 265 | - name: dest_tags 266 | value: $(tasks.generate-id.results.tag_id),test,latest 267 | - name: test-update-image 268 | taskRef: 269 | name: update-image 270 | runAfter: 271 | - tag-test-image 272 | params: 273 | - name: git_revision 274 | value: "$(params.gitops_git_revision)" 275 | - name: path 276 | value: clusters/$(params.cluster)/overlays/test 277 | - name: image 278 | value: "$(params.image_dest_url)" 279 | - name: image_tag 280 | value: $(tasks.generate-id.results.tag_id) 281 | workspaces: 282 | - name: gitops-manifests 283 | workspace: gitops-manifests 284 | - name: test-gitops-deploy 285 | taskRef: 286 | kind: Task 287 | params: 288 | - name: pathInRepo 289 | value: task/argocd-sync-and-wait/1.0/argocd-sync-and-wait.yaml 290 | resolver: git 291 | runAfter: 292 | - test-update-image 293 | params: 294 | - name: application_name 295 | value: product-catalog-gitops/product-catalog-test 296 | - name: revision 297 | value: $(params.gitops_git_revision) 298 | - name: deployment 299 | value: client 300 | - name: namespace 301 | value: product-catalog-test 302 | - name: image_tag 303 | value: $(tasks.generate-id.results.tag_id) 304 | - name: notify-build-complete 305 | taskRef: 306 | name: send-to-webhook-slack 307 | kind: Task 308 | runAfter: 309 | - test-gitops-deploy 310 | - notify-on-scan-fail 311 | params: 312 | - name: console_message 313 | value: |- 314 | Client Build $(tasks.generate-id.results.tag_id) Completed 315 | 316 | The build of image $(params.image_dest_url):$(tasks.generate-id.results.tag_id) has been completed 317 | 318 | * Quay Image: https://$(params.image_dest_url):$(tasks.generate-id.results.tag_id) 319 | * ACS Scan: https://$(tasks.variables.results.acs_central_endpoint)/main/vulnerability-management/images/$(tasks.build-image.results.IMAGE_DIGEST) 320 | * SonarQube Results: https://$(params.sonarqube_host)/dashboard?id=product-catalog-client 321 | - name: message_type 322 | value: raw 323 | - name: message 324 | value: |- 325 | { 326 | "blocks": [ 327 | { 328 | "type": "header", 329 | "text": { 330 | "type": "plain_text", 331 | "text": "Client Pipeline Complete", 332 | "emoji": true 333 | } 334 | }, 335 | { 336 | "type": "section", 337 | "text": { 338 | "type": "mrkdwn", 339 | "text": "Client pipeline for image $(tasks.generate-id.results.tag_id) has completed" 340 | } 341 | }, 342 | { 343 | "type": "divider" 344 | }, 345 | { 346 | "type": "section", 347 | "text": { 348 | "type": "mrkdwn", 349 | "text": "*Pipeline Results*" 350 | } 351 | }, 352 | { 353 | "type": "section", 354 | "text": { 355 | "type": "mrkdwn", 356 | "text": "* <$(params.image_dest_url):$(tasks.generate-id.results.tag_id)|Quay Image>\n* \n* " 357 | } 358 | }, 359 | { 360 | "type": "divider" 361 | }, 362 | { 363 | "type": "section", 364 | "text": { 365 | "type": "mrkdwn", 366 | "text": "To push this specific release to production, use the button below to create a new pull request to be approved." 367 | } 368 | }, 369 | { 370 | "type": "divider" 371 | }, 372 | { 373 | "type": "actions", 374 | "elements": [ 375 | { 376 | "type": "button", 377 | "text": { 378 | "type": "plain_text", 379 | "text": "Create Release Pull Request", 380 | "emoji": true 381 | }, 382 | "value": "client,$(params.image_dest_url),$(tasks.generate-id.results.tag_id),$(params.cluster)", 383 | } 384 | ] 385 | } 386 | ] 387 | } 388 | workspaces: 389 | - name: slack-secret 390 | workspace: slack-secret 391 | finally: 392 | - name: release-lease 393 | taskRef: 394 | kind: Task 395 | params: 396 | - name: pathInRepo 397 | value: task/release-lease/1.0/release-lease.yaml 398 | resolver: git 399 | params: 400 | - name: lease-name 401 | value: "$(context.pipeline.name)" 402 | - name: notify-on-fail 403 | taskRef: 404 | name: send-to-webhook-slack 405 | kind: Task 406 | when: 407 | - input: $(tasks.status) 408 | operator: in 409 | values: ["Failed"] 410 | - input: "$(workspaces.slack-secret.bound)" 411 | operator: in 412 | values: ["true"] 413 | params: 414 | - name: message 415 | value: "Some tasks in the $(context.pipeline.name) have failed in pipelinerun $(context.pipelinerun.name) failed, please investigate" 416 | workspaces: 417 | - name: slack-secret 418 | workspace: slack-secret 419 | -------------------------------------------------------------------------------- /components/tekton/pipelines/client/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | resources: 8 | # Client pipeline 9 | - client-pipeline.yaml -------------------------------------------------------------------------------- /components/tekton/pipelines/push-prod-pr/base/gitops-manifests-pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: gitops-manifests 5 | spec: 6 | resources: 7 | requests: 8 | storage: 1Gi 9 | volumeMode: Filesystem 10 | accessModes: 11 | - ReadWriteOnce -------------------------------------------------------------------------------- /components/tekton/pipelines/push-prod-pr/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | resources: 8 | - gitops-manifests-pvc.yaml 9 | - push-prod-pr-pipeline.yaml -------------------------------------------------------------------------------- /components/tekton/pipelines/push-prod-pr/base/push-prod-pr-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Pipeline 3 | metadata: 4 | name: push-prod-pr 5 | spec: 6 | workspaces: 7 | - name: gitops-manifests 8 | - name: acs-central 9 | optional: true 10 | params: 11 | - name: git_revision 12 | type: string 13 | default: main 14 | - name: git_url 15 | type: string 16 | default: https://github.com/gnunn-gitops/product-catalog 17 | - name: git_source_url 18 | description: The URL of the repo containing the source code for the application, used to construct a link for code changes 19 | - name: image_dest_url 20 | type: string 21 | description: External registry location of image to push without tag 22 | - name: image_dest_tag 23 | type: string 24 | description: Tag of image to push to production 25 | - name: cluster 26 | type: string 27 | description: Cluster to push to 28 | - name: app 29 | type: string 30 | description: Either 'server' or 'client' 31 | - name: sonarqube_host 32 | type: string 33 | description: Hostname for sonarqube 34 | default: sonarqube-dev-tools.apps.hub.ocplab.com 35 | tasks: 36 | - name: variables 37 | taskRef: 38 | name: variables-from-k8s 39 | workspaces: 40 | - name: acs-central 41 | workspace: acs-central 42 | - name: get-image-digest 43 | taskRef: 44 | kind: Task 45 | params: 46 | - name: pathInRepo 47 | value: task/image-tag-to-digest/1.0/image-tag-to-digest.yaml 48 | resolver: git 49 | runAfter: 50 | - variables 51 | params: 52 | - name: image_dest_url 53 | value: $(params.image_dest_url) 54 | - name: image_dest_tag 55 | value: $(params.image_dest_tag) 56 | - name: clone 57 | taskRef: 58 | resolver: cluster 59 | params: 60 | - name: kind 61 | value: task 62 | - name: name 63 | value: git-clone 64 | - name: namespace 65 | value: openshift-pipelines 66 | runAfter: 67 | - get-image-digest 68 | workspaces: 69 | - name: output 70 | workspace: gitops-manifests 71 | params: 72 | - name: URL 73 | value: "$(params.git_url)" 74 | - name: REVISION 75 | value: "$(params.git_revision)" 76 | - name: DELETE_EXISTING 77 | value: "true" 78 | - name: branch 79 | taskRef: 80 | name: git 81 | runAfter: 82 | - clone 83 | workspaces: 84 | - name: source 85 | workspace: gitops-manifests 86 | params: 87 | - name: commands 88 | value: | 89 | git checkout -b push-$(params.image_dest_tag) 90 | - name: update-image 91 | taskRef: 92 | name: update-image 93 | runAfter: 94 | - branch 95 | params: 96 | - name: git_revision 97 | value: "push-$(params.image_dest_tag)" 98 | - name: path 99 | value: clusters/$(params.cluster)/overlays/prod 100 | - name: image 101 | value: "$(params.image_dest_url)" 102 | - name: image_tag 103 | value: $(params.image_dest_tag) 104 | workspaces: 105 | - name: gitops-manifests 106 | workspace: gitops-manifests 107 | - name: create-commit-list 108 | taskRef: 109 | name: create-commit-list 110 | runAfter: 111 | - update-image 112 | params: 113 | - name: deployment 114 | value: $(params.app) 115 | - name: namespace 116 | value: product-catalog-prod 117 | - name: image_tag 118 | value: $(params.image_dest_tag) 119 | - name: git_source_url 120 | value: $(params.git_source_url) 121 | - name: prod-pr-deploy 122 | taskRef: 123 | name: task-create-pr 124 | kind: Task 125 | runAfter: 126 | - create-commit-list 127 | workspaces: 128 | - name: source 129 | workspace: gitops-manifests 130 | when: 131 | - input: "$(tasks.update-image.results.image_updated)" 132 | operator: in 133 | values: ["true"] 134 | params: 135 | - name: title 136 | value: Update $(params.app) image to $(params.image_dest_tag) 137 | - name: body 138 | value: |- 139 | ## Security Checklist 140 | 141 | - [ ] [Quay Image Vulnerabilities](https://$(params.image_dest_url):$(params.image_dest_tag)) 142 | - [ ] [Advanced Cluster Security Image Vulnerabilities and Policies](https://$(tasks.variables.results.acs_central_endpoint)/main/vulnerability-management/images/$(tasks.get-image-digest.results.image_digest)) 143 | - [ ] [Sonarqube Results](https://$(params.sonarqube_host)/dashboard?id=product-catalog-$(params.app)) 144 | 145 | ## Code Changes 146 | - [ ] $(tasks.create-commit-list.results.commit-list-message) 147 | -------------------------------------------------------------------------------- /components/tekton/pipelines/server/base/config/newman-dev-env.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "30c331d4-e961-4606-aecb-5a60e8e15213", 3 | "name": "product-catalog-dev-service", 4 | "values": [ 5 | { 6 | "key": "host", 7 | "value": "server.product-catalog-dev:8080", 8 | "enabled": true 9 | }, 10 | { 11 | "key": "scheme", 12 | "value": "http", 13 | "enabled": true 14 | } 15 | ], 16 | "_postman_variable_scope": "environment" 17 | } 18 | -------------------------------------------------------------------------------- /components/tekton/pipelines/server/base/config/newman-prod-env.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "30c331d4-e961-4606-aecb-5a60e8e15213", 3 | "name": "product-catalog-dev-service", 4 | "values": [ 5 | { 6 | "key": "host", 7 | "value": "server.product-catalog-prod:8080", 8 | "enabled": true 9 | }, 10 | { 11 | "key": "scheme", 12 | "value": "http", 13 | "enabled": true 14 | } 15 | ], 16 | "_postman_variable_scope": "environment" 17 | } 18 | -------------------------------------------------------------------------------- /components/tekton/pipelines/server/base/config/newman-test-env.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "30c331d4-e961-4606-aecb-5a60e8e15213", 3 | "name": "product-catalog-dev-service", 4 | "values": [ 5 | { 6 | "key": "host", 7 | "value": "server.product-catalog-test:8080", 8 | "enabled": true 9 | }, 10 | { 11 | "key": "scheme", 12 | "value": "http", 13 | "enabled": true 14 | } 15 | ], 16 | "_postman_variable_scope": "environment" 17 | } 18 | -------------------------------------------------------------------------------- /components/tekton/pipelines/server/base/config/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.sonarsource.scanner.maven 4 | 5 | 6 | 7 | 8 | nexus-releases 9 | ${env.MAVEN_SERVER_USERNAME} 10 | ${env.MAVEN_SERVER_PASSWORD} 11 | 12 | 13 | nexus-snapshots 14 | ${env.MAVEN_SERVER_USERNAME} 15 | ${env.MAVEN_SERVER_PASSWORD} 16 | 17 | 18 | 19 | 20 | local-repository 21 | Nexus Maven Mirror 22 | https://nexus-dev-tools.apps.hub.ocplab.com/content/groups/public 23 | * 24 | 25 | 26 | 27 | 28 | 29 | nexus-internal 30 | 31 | https://nexus-dev-tools.apps.hub.ocplab.com/content/repositories/snapshots 32 | https://nexus-dev-tools.apps.hub.ocplab.com//content/repositories/releases 33 | 37 | 38 | 39 | 40 | sonar 41 | 42 | 43 | https://sonarqube-dev-tools.apps.hub.ocplab.com 44 | 45 | admin 46 | R@dhat1234 47 | 52 | 53 | 54 | 55 | pipeline 56 | 57 | /workspace/app-binary 58 | 59 | 60 | 61 | 62 | 63 | nexus-internal 64 | sonar 65 | pipeline 66 | 67 | -------------------------------------------------------------------------------- /components/tekton/pipelines/server/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | configMapGenerator: 8 | - name: maven-settings 9 | files: 10 | - config/settings.xml 11 | - name: newman-env 12 | files: 13 | - config/newman-dev-env.json 14 | - config/newman-test-env.json 15 | - config/newman-prod-env.json 16 | 17 | resources: 18 | - secrets/maven-repo-creds.yaml 19 | # Server pipeline 20 | - server-pipeline.yaml 21 | - server-post-prod-pipeline.yaml -------------------------------------------------------------------------------- /components/tekton/pipelines/server/base/secrets/maven-repo-creds.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | annotations: 5 | sealedsecrets.bitnami.com/managed: "true" 6 | argocd.argoproj.io/compare-options: IgnoreExtraneous 7 | name: maven-repo-creds 8 | type: Opaque 9 | data: 10 | MAVEN_SERVER_USERNAME: YWRtaW4K 11 | MAVEN_SERVER_PASSWORD: YWRtaW4xMjMK -------------------------------------------------------------------------------- /components/tekton/pipelines/server/base/server-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Pipeline 3 | metadata: 4 | name: server 5 | spec: 6 | workspaces: 7 | - name: git-source 8 | - name: maven-settings 9 | - name: newman-env 10 | - name: gitops-manifests 11 | - name: slack-secret 12 | optional: true 13 | - name: acs-central 14 | optional: true 15 | params: 16 | - name: source_git_revision 17 | type: string 18 | default: master 19 | - name: source_git_url 20 | type: string 21 | default: https://github.com/gnunn-gitops/product-catalog-server 22 | - name: gitops_git_revision 23 | type: string 24 | default: main 25 | - name: gitops_git_url 26 | type: string 27 | default: https://github.com/gnunn-gitops/product-catalog 28 | - name: api_test_collection_url 29 | type: string 30 | default: https://raw.githubusercontent.com/gnunn-gitops/product-catalog-server/master/tests/product-catalog-server-tests.json 31 | - name: MAVEN_MIRROR_URL 32 | default: "" 33 | - name: image_dest_url 34 | type: string 35 | description: External registry location to copy image to 36 | default: quay.io/gnunn/server 37 | - name: cluster 38 | type: string 39 | description: Cluster to push to 40 | default: "local.home" 41 | - name: MAVEN_IMAGE 42 | default: image-registry.openshift-image-registry.svc:5000/openshift/java:openjdk-11-ubi8 43 | - name: sonarqube_host 44 | default: sonarqube-dev-tools.apps.hub.ocplab.com 45 | description: Where sonarqube is located to support project scanning 46 | tasks: 47 | - name: acquire-lease 48 | taskRef: 49 | kind: Task 50 | params: 51 | - name: pathInRepo 52 | value: task/acquire-lease/1.0/acquire-lease.yaml 53 | resolver: git 54 | params: 55 | - name: lease-name 56 | value: "$(context.pipeline.name)" 57 | - name: owner 58 | value: "$(context.pipelineRun.name)" 59 | - name: variables 60 | taskRef: 61 | name: variables-from-k8s 62 | runAfter: 63 | - acquire-lease 64 | workspaces: 65 | - name: acs-central 66 | workspace: acs-central 67 | - name: clone 68 | taskRef: 69 | resolver: cluster 70 | params: 71 | - name: kind 72 | value: task 73 | - name: name 74 | value: git-clone 75 | - name: namespace 76 | value: openshift-pipelines 77 | runAfter: 78 | - variables 79 | workspaces: 80 | - name: output 81 | workspace: git-source 82 | params: 83 | - name: URL 84 | value: "$(params.source_git_url)" 85 | - name: REVISION 86 | value: "$(params.source_git_revision)" 87 | - name: DELETE_EXISTING 88 | value: "true" 89 | - name: generate-id 90 | taskRef: 91 | kind: Task 92 | params: 93 | - name: pathInRepo 94 | value: task/generate-build-id/1.0/generate-build-id.yaml 95 | resolver: git 96 | runAfter: 97 | - clone 98 | workspaces: 99 | - name: source 100 | workspace: git-source 101 | - name: build 102 | taskRef: 103 | name: maven 104 | kind: Task 105 | workspaces: 106 | - name: source 107 | workspace: git-source 108 | - name: maven-settings 109 | workspace: maven-settings 110 | runAfter: 111 | - generate-id 112 | params: 113 | - name: MAVEN_IMAGE 114 | value: $(params.MAVEN_IMAGE) 115 | - name: MAVEN_MIRROR_URL 116 | value: "$(params.MAVEN_MIRROR_URL)" 117 | - name: MAVEN_REPO_CONFIG_SECRET 118 | value: "maven-repo-creds" 119 | - name: GOALS 120 | value: 121 | - "package" 122 | - "deploy" 123 | - name: quality 124 | taskRef: 125 | name: maven 126 | kind: Task 127 | workspaces: 128 | - name: source 129 | workspace: git-source 130 | - name: maven-settings 131 | workspace: maven-settings 132 | runAfter: 133 | - build 134 | params: 135 | - name: MAVEN_IMAGE 136 | value: $(params.MAVEN_IMAGE) 137 | - name: MAVEN_MIRROR_URL 138 | value: "$(params.MAVEN_MIRROR_URL)" 139 | - name: MAVEN_REPO_CONFIG_SECRET 140 | value: "maven-repo-creds" 141 | - name: GOALS 142 | value: 143 | - "sonar:sonar" 144 | - "-Pquality" 145 | - name: build-image 146 | taskRef: 147 | name: buildah 148 | kind: Task 149 | workspaces: 150 | - name: source 151 | workspace: git-source 152 | runAfter: 153 | - quality 154 | params: 155 | - name: IMAGE 156 | value: $(params.image_dest_url):latest 157 | - name: DOCKERFILE 158 | value: ./Containerfile 159 | - name: tag-dev-image 160 | taskRef: 161 | name: push-image 162 | kind: Task 163 | runAfter: 164 | - build-image 165 | # - deploy-nexus 166 | params: 167 | - name: src_image 168 | value: $(params.image_dest_url)@$(tasks.build-image.results.IMAGE_DIGEST) 169 | - name: dest_image 170 | value: $(params.image_dest_url) 171 | - name: dest_tags 172 | value: "$(tasks.generate-id.results.tag_id),dev" 173 | - name: acs-scan-image 174 | taskRef: 175 | kind: Task 176 | params: 177 | - name: pathInRepo 178 | value: task/acs-image-scan/1.0/acs-image-scan.yaml 179 | resolver: git 180 | runAfter: 181 | - build-image 182 | params: 183 | - name: output_format 184 | value: table 185 | - name: image 186 | value: $(params.image_dest_url)@$(tasks.build-image.results.IMAGE_DIGEST) 187 | when: 188 | - input: "$(workspaces.acs-central.bound)" 189 | operator: in 190 | values: ["true"] 191 | workspaces: 192 | - name: acs-central 193 | workspace: acs-central 194 | - name: acs-check-image 195 | taskRef: 196 | kind: Task 197 | params: 198 | - name: pathInRepo 199 | value: task/acs-image-check/1.0/acs-image-check.yaml 200 | resolver: git 201 | runAfter: 202 | - acs-scan-image 203 | params: 204 | - name: image 205 | value: $(params.image_dest_url)@$(tasks.build-image.results.IMAGE_DIGEST) 206 | when: 207 | - input: "$(workspaces.acs-central.bound)" 208 | operator: in 209 | values: ["true"] 210 | workspaces: 211 | - name: acs-central 212 | workspace: acs-central 213 | - name: generate-sbom 214 | taskRef: 215 | kind: Task 216 | params: 217 | - name: pathInRepo 218 | value: task/acs-image-sbom/1.0/acs-image-sbom.yaml 219 | resolver: git 220 | runAfter: 221 | - acs-check-image 222 | params: 223 | - name: image 224 | value: $(params.image_dest_url)@$(tasks.build-image.results.IMAGE_DIGEST) 225 | when: 226 | - input: "$(workspaces.acs-central.bound)" 227 | operator: in 228 | values: ["true"] 229 | workspaces: 230 | - name: acs-central 231 | workspace: acs-central 232 | - name: notify-on-scan-fail 233 | taskRef: 234 | name: send-to-webhook-slack 235 | kind: Task 236 | runAfter: 237 | - generate-sbom 238 | when: 239 | - input: "$(tasks.acs-check-image.results.check_passed)" 240 | operator: in 241 | values: ["false"] 242 | params: 243 | - name: message 244 | value: |- 245 | *Server Image Scan Failed* 246 | The image scan for server:$(tasks.generate-id.results.tag_id) failed, please view test results here: https://$(tasks.variables.results.acs_central_endpoint)/main/vulnerability-management/images/$(tasks.build-image.results.IMAGE_DIGEST) 247 | workspaces: 248 | - name: slack-secret 249 | workspace: slack-secret 250 | # - name: verify-signature 251 | # taskRef: 252 | # kind: Task 253 | # params: 254 | # - name: pathInRepo 255 | # value: task/verify-signature/1.0/verify-signature.yaml 256 | # resolver: git 257 | # runAfter: 258 | # - acs-check-image 259 | # params: 260 | # - name: tuf_url 261 | # value: https://tuf-trusted-artifact-signer.apps.home.ocplab.com 262 | # - name: oidc_issuer_url 263 | # value: https://sso.ocplab.com/realms/ocplab 264 | # - name: fulcio_url 265 | # value: https://fulcio-server-trusted-artifact-signer.apps.home.ocplab.com 266 | # - name: rekor_url 267 | # value: https://rekor-server-trusted-artifact-signer.apps.home.ocplab.com 268 | # - name: certificate_identity 269 | # value: tekton-builder@ocplab.com 270 | # - name: image_url 271 | # value: $(params.image_dest_url):$(tasks.generate-id.results.tag_id) 272 | - name: clone-gitops-manifests 273 | taskRef: 274 | resolver: cluster 275 | params: 276 | - name: kind 277 | value: task 278 | - name: name 279 | value: git-clone 280 | - name: namespace 281 | value: openshift-pipelines 282 | runAfter: 283 | - tag-dev-image 284 | workspaces: 285 | - name: output 286 | workspace: gitops-manifests 287 | params: 288 | - name: URL 289 | value: "$(params.gitops_git_url)" 290 | - name: REVISION 291 | value: "$(params.gitops_git_revision)" 292 | - name: DELETE_EXISTING 293 | value: "true" 294 | - name: dev-update-image 295 | taskRef: 296 | name: update-image 297 | runAfter: 298 | - clone-gitops-manifests 299 | params: 300 | - name: url 301 | value: "$(params.gitops_git_url)" 302 | - name: git_revision 303 | value: "$(params.gitops_git_revision)" 304 | - name: path 305 | value: clusters/$(params.cluster)/overlays/dev 306 | - name: image 307 | value: "$(params.image_dest_url)" 308 | - name: image_tag 309 | value: "$(tasks.generate-id.results.tag_id)" 310 | workspaces: 311 | - name: gitops-manifests 312 | workspace: gitops-manifests 313 | - name: dev-gitops-deploy 314 | taskRef: 315 | kind: Task 316 | params: 317 | - name: pathInRepo 318 | value: task/argocd-sync-and-wait/1.0/argocd-sync-and-wait.yaml 319 | resolver: git 320 | runAfter: 321 | - dev-update-image 322 | params: 323 | - name: application_name 324 | value: product-catalog-gitops/product-catalog-dev 325 | - name: revision 326 | value: $(params.gitops_git_revision) 327 | - name: deployment 328 | value: server 329 | - name: namespace 330 | value: product-catalog-dev 331 | - name: image_tag 332 | value: "$(tasks.generate-id.results.tag_id)" 333 | - name: dev-test 334 | taskRef: 335 | name: newman 336 | kind: Task 337 | runAfter: 338 | - dev-gitops-deploy 339 | params: 340 | - name: COLLECTION 341 | value: $(params.api_test_collection_url) 342 | - name: ENVIRONMENT 343 | value: newman-dev-env.json 344 | workspaces: 345 | - name: newman-env 346 | workspace: newman-env 347 | - name: tag-test-image 348 | taskRef: 349 | name: push-image 350 | kind: Task 351 | runAfter: 352 | - dev-test 353 | params: 354 | - name: src_image 355 | value: $(params.image_dest_url)@$(tasks.build-image.results.IMAGE_DIGEST) 356 | - name: dest_image 357 | value: $(params.image_dest_url) 358 | - name: dest_tags 359 | value: test 360 | - name: test-update-image 361 | taskRef: 362 | name: update-image 363 | runAfter: 364 | - tag-test-image 365 | params: 366 | - name: git_revision 367 | value: "$(params.gitops_git_revision)" 368 | - name: path 369 | value: clusters/$(params.cluster)/overlays/test 370 | - name: image 371 | value: "$(params.image_dest_url)" 372 | - name: image_tag 373 | value: "$(tasks.generate-id.results.tag_id)" 374 | workspaces: 375 | - name: gitops-manifests 376 | workspace: gitops-manifests 377 | - name: test-gitops-deploy 378 | taskRef: 379 | kind: Task 380 | params: 381 | - name: pathInRepo 382 | value: task/argocd-sync-and-wait/1.0/argocd-sync-and-wait.yaml 383 | resolver: git 384 | runAfter: 385 | - test-update-image 386 | params: 387 | - name: application_name 388 | value: product-catalog-gitops/product-catalog-test 389 | - name: revision 390 | value: $(params.gitops_git_revision) 391 | - name: deployment 392 | value: server 393 | - name: namespace 394 | value: product-catalog-test 395 | - name: image_tag 396 | value: "$(tasks.generate-id.results.tag_id)" 397 | - name: test-test 398 | taskRef: 399 | name: newman 400 | kind: Task 401 | runAfter: 402 | - test-gitops-deploy 403 | params: 404 | - name: COLLECTION 405 | value: $(params.api_test_collection_url) 406 | - name: ENVIRONMENT 407 | value: newman-test-env.json 408 | workspaces: 409 | - name: newman-env 410 | workspace: newman-env 411 | - name: notify-build-complete 412 | taskRef: 413 | name: send-to-webhook-slack 414 | kind: Task 415 | runAfter: 416 | - test-test 417 | - notify-on-scan-fail 418 | params: 419 | - name: console_message 420 | value: |- 421 | Server Build $(tasks.generate-id.results.tag_id) Completed 422 | 423 | The build of image $(params.image_dest_url):$(tasks.generate-id.results.tag_id) has been completed 424 | 425 | * Quay Image: https://$(params.image_dest_url):$(tasks.generate-id.results.tag_id) 426 | * ACS Scan: https://$(tasks.variables.results.acs_central_endpoint)/main/vulnerability-management/images/$(tasks.build-image.results.IMAGE_DIGEST) 427 | * SonarQube Results: https://$(params.sonarqube_host)/dashboard?id=product-catalog-server 428 | - name: message_type 429 | value: raw 430 | - name: message 431 | value: |- 432 | { 433 | "blocks": [ 434 | { 435 | "type": "header", 436 | "text": { 437 | "type": "plain_text", 438 | "text": "Server Pipeline Complete", 439 | "emoji": true 440 | } 441 | }, 442 | { 443 | "type": "section", 444 | "text": { 445 | "type": "mrkdwn", 446 | "text": "Server pipeline for image $(tasks.generate-id.results.tag_id) has completed" 447 | } 448 | }, 449 | { 450 | "type": "divider" 451 | }, 452 | { 453 | "type": "section", 454 | "text": { 455 | "type": "mrkdwn", 456 | "text": "*Pipeline Results*" 457 | } 458 | }, 459 | { 460 | "type": "section", 461 | "text": { 462 | "type": "mrkdwn", 463 | "text": "* \n* \n* " 464 | } 465 | }, 466 | { 467 | "type": "divider" 468 | }, 469 | { 470 | "type": "section", 471 | "text": { 472 | "type": "mrkdwn", 473 | "text": "To push this specific release to production, use the button below to create a new pull request to be approved." 474 | } 475 | }, 476 | { 477 | "type": "divider" 478 | }, 479 | { 480 | "type": "actions", 481 | "elements": [ 482 | { 483 | "type": "button", 484 | "text": { 485 | "type": "plain_text", 486 | "text": "Create Release Pull Request :repeat:", 487 | "emoji": true 488 | }, 489 | "url": "https://slack-message-handler-product-catalog-cicd.${SUB_DOMAIN}/releaseApp?application=server&cluster=$(params.cluster)&image=$(params.image_dest_url)&tag=$(tasks.generate-id.results.tag_id)" 490 | } 491 | ] 492 | } 493 | ] 494 | } 495 | workspaces: 496 | - name: slack-secret 497 | workspace: slack-secret 498 | finally: 499 | - name: release-lease 500 | taskRef: 501 | kind: Task 502 | params: 503 | - name: pathInRepo 504 | value: task/release-lease/1.0/release-lease.yaml 505 | resolver: git 506 | params: 507 | - name: lease-name 508 | value: "$(context.pipeline.name)" 509 | - name: notify-on-fail 510 | taskRef: 511 | name: send-to-webhook-slack 512 | kind: Task 513 | when: 514 | - input: $(tasks.status) 515 | operator: in 516 | values: ["Failed"] 517 | - input: "$(workspaces.slack-secret.bound)" 518 | operator: in 519 | values: ["true"] 520 | params: 521 | - name: message 522 | value: "Some tasks in the $(context.pipeline.name) have failed in pipelinerun $(context.pipelinerun.name) failed, please investigate" 523 | workspaces: 524 | - name: slack-secret 525 | workspace: slack-secret 526 | -------------------------------------------------------------------------------- /components/tekton/pipelines/server/base/server-post-prod-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Pipeline 3 | metadata: 4 | name: server-post-prod 5 | spec: 6 | workspaces: 7 | - name: newman-env 8 | - name: slack-secret 9 | optional: true 10 | tasks: 11 | - name: prod-test 12 | taskRef: 13 | name: newman 14 | params: 15 | - name: COLLECTION 16 | value: https://raw.githubusercontent.com/gnunn-gitops/product-catalog-server/master/tests/product-catalog-server-tests.json 17 | - name: ENVIRONMENT 18 | value: newman-prod-env.json 19 | workspaces: 20 | - name: newman-env 21 | workspace: newman-env 22 | finally: 23 | # - name: notify 24 | # taskRef: 25 | # name: send-to-webhook-slack 26 | # when: 27 | # - input: "$(workspaces.slack-secret.bound)" 28 | # operator: in 29 | # values: ["true"] 30 | # params: 31 | # - name: message 32 | # value: "Production has been synchronized by ArgoCD, test status: $(tasks.prod-test.status)" 33 | # workspaces: 34 | # - name: slack-secret 35 | # workspace: slack-secret 36 | # Add when pipelines updated to version of Tekton that supports using when expressions 37 | - name: notify-succeeded 38 | when: 39 | - input: $(tasks.prod-test.status) 40 | operator: in 41 | values: ["Succeeded"] 42 | taskRef: 43 | name: send-to-webhook-slack 44 | params: 45 | - name: message 46 | value: "SUCCEEDED: Production has been synchronized by ArgoCD" 47 | workspaces: 48 | - name: slack-secret 49 | workspace: slack-secret 50 | - name: notify-failed 51 | when: 52 | - input: $(tasks.prod-test.status) 53 | operator: in 54 | values: ["Failed"] 55 | taskRef: 56 | name: send-to-webhook-slack 57 | params: 58 | - name: message 59 | value: "FAILED: Production has been synchronized by ArgoCD" 60 | workspaces: 61 | - name: slack-secret 62 | workspace: slack-secret -------------------------------------------------------------------------------- /components/tekton/tasks/base/dependency-cache-pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: dependency-cache 5 | spec: 6 | resources: 7 | requests: 8 | storage: 10Gi 9 | volumeMode: Filesystem 10 | accessModes: 11 | - ReadWriteOnce -------------------------------------------------------------------------------- /components/tekton/tasks/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | resources: 8 | # Supporting infra 9 | - m2-cache-pvc.yaml 10 | # - npm-cache-pvc.yaml 11 | - dependency-cache-pvc.yaml 12 | # Tekton tasks 13 | - task-git.yaml 14 | - task-yq.yaml 15 | - https://github.com/redhat-cop/gitops-catalog/openshift-pipelines-tasks/maven/overlays/m2-cache 16 | - task-npm.yaml 17 | - task-npm-quality.yaml 18 | - task-binary-s2i.yaml 19 | - task-buildah.yaml 20 | - task-push-image.yaml 21 | - task-deploy.yaml 22 | - https://github.com/redhat-cop/gitops-catalog/openshift-pipelines-tasks/newman/base 23 | - task-create-pr.yaml 24 | - task-tekton.yaml 25 | - task-send-to-webhook-slack.yaml 26 | - task-kustomize.yaml 27 | # - argocd-sync-and-wait-task.yaml 28 | - task-run-pipeline.yaml 29 | - task-update-image.yaml 30 | - task-variables.yaml 31 | - task-create-commit-list.yaml -------------------------------------------------------------------------------- /components/tekton/tasks/base/m2-cache-pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: m2-cache 5 | spec: 6 | resources: 7 | requests: 8 | storage: 10Gi 9 | accessModes: 10 | - ReadWriteOnce -------------------------------------------------------------------------------- /components/tekton/tasks/base/npm-cache-pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: npm-cache 5 | spec: 6 | resources: 7 | requests: 8 | storage: 10Gi 9 | volumeMode: Filesystem 10 | accessModes: 11 | - ReadWriteOnce -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-binary-s2i.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: binary-s2i 5 | spec: 6 | workspaces: 7 | - name: source 8 | params: 9 | - name: directory 10 | description: The sub-directory to use for the wildcard build 11 | type: string 12 | default: "" 13 | - name: file 14 | description: The file to use for the s2i binary build, can be a wildcard. If it resolves to multiple files the first one is used 15 | type: string 16 | default: "" 17 | - name: buildconfig 18 | type: string 19 | - name: IMAGE 20 | type: string 21 | description: "Only needed if using chains to automatically specify sign the image" 22 | default: "" 23 | results: 24 | - description: The build generated by this task 25 | name: build 26 | - description: The digest for the image created 27 | name: IMAGE_DIGEST 28 | - description: The URL for the image 29 | name: IMAGE_URL 30 | steps: 31 | - name: s2i-build 32 | image: quay.io/openshift/origin-cli:latest 33 | script: | 34 | #!/usr/bin/env bash 35 | 36 | if [[ "$(params.directory)" == "" && "$(params.file)" == "" ]] 37 | then 38 | echo "The task must have either directory or file parameters set" 39 | exit 1 40 | fi 41 | 42 | if [ "$(params.directory)" != "" ]; 43 | then 44 | oc start-build $(params.buildconfig) --from-dir=$(params.directory) --wait=true > output.txt 45 | else 46 | FILE_PATH="$(ls $(params.file))" 47 | echo "Using file ${FILE_PATH} for binary build" 48 | oc start-build $(params.buildconfig) --from-file="${FILE_PATH}" --wait=true > output.txt 49 | fi 50 | 51 | BUILD=$(grep -oP '(?<=build\.build\.openshift\.io\/).\S*' output.txt) 52 | echo -n "$BUILD" > $(results.build.path) 53 | echo "Build created was $BUILD" 54 | 55 | STATUS=$(oc get build $BUILD -o=jsonpath='{.status.phase}') 56 | if [ "$STATUS" == "Failed" ]; 57 | then 58 | echo "Build $BUILD failed" 59 | exit 1 60 | fi 61 | 62 | DIGEST=$(oc get build $BUILD -o jsonpath="{.status.output.to.imageDigest}") 63 | echo -n "$DIGEST" > $(results.IMAGE_DIGEST.path) 64 | echo "Digest for image build was $DIGEST" 65 | 66 | IMAGE_URL=$(oc get build $BUILD -o jsonpath="{.status.outputDockerImageReference}") 67 | IMAGE_URL="${IMAGE_URL%:*}" 68 | echo -n "$IMAGE_URL" > $(results.IMAGE_URL.path) 69 | echo "Image was pushed to: $IMAGE_URL" -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-buildah.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | annotations: 5 | manifestival: new 6 | tekton.dev/pipelines.minVersion: 0.12.1 7 | tekton.dev/tags: image-build 8 | labels: 9 | app.kubernetes.io/version: "0.1" 10 | name: buildah 11 | spec: 12 | description: |- 13 | Buildah task builds source into a container image and then pushes it to a container registry. 14 | Buildah Task builds source into a container image using Project Atomic's Buildah build tool.It uses Buildah's support for building from Dockerfiles, using its buildah bud command.This command executes the directives in the Dockerfile to assemble a container image, then pushes that image to a container registry. 15 | params: 16 | - description: Reference of the image buildah will produce. 17 | name: IMAGE 18 | type: string 19 | - default: registry.redhat.io/rhel8/buildah@sha256:0a86ecbdfbe86e9d225b7fe4b090a0dd6d323f8afdfdf2bd933ff223ddb53320 20 | description: The location of the buildah builder image. 21 | name: BUILDER_IMAGE 22 | type: string 23 | - default: vfs 24 | description: Set buildah storage driver 25 | name: STORAGE_DRIVER 26 | type: string 27 | - default: ./Dockerfile 28 | description: Path to the Dockerfile to build. 29 | name: DOCKERFILE 30 | type: string 31 | - default: . 32 | description: Path to the directory to use as context. 33 | name: CONTEXT 34 | type: string 35 | - default: "true" 36 | description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS 37 | registry) 38 | name: TLSVERIFY 39 | type: string 40 | - default: oci 41 | description: The format of the built container, oci or docker 42 | name: FORMAT 43 | type: string 44 | - default: "" 45 | description: Extra parameters passed for the build command when building images. 46 | name: BUILD_EXTRA_ARGS 47 | type: string 48 | - default: "" 49 | description: Extra parameters passed for the push command when pushing images. 50 | name: PUSH_EXTRA_ARGS 51 | type: string 52 | - default: "false" 53 | description: Skip pushing the built image 54 | name: SKIP_PUSH 55 | type: string 56 | results: 57 | - description: Digest of the image just built. 58 | name: IMAGE_DIGEST 59 | type: string 60 | - name: IMAGE_URL 61 | description: Image repository where the built image would be pushed to 62 | steps: 63 | - image: $(params.BUILDER_IMAGE) 64 | name: build-and-push 65 | script: | 66 | pwd 67 | ls 68 | 69 | buildah --storage-driver=$(params.STORAGE_DRIVER) bud \ 70 | $(params.BUILD_EXTRA_ARGS) --format=$(params.FORMAT) \ 71 | --tls-verify=$(params.TLSVERIFY) --no-cache \ 72 | -f $(params.DOCKERFILE) -t $(params.IMAGE) $(params.CONTEXT) 73 | 74 | [[ "$(params.SKIP_PUSH)" == "true" ]] && echo "Push skipped" && exit 0 75 | buildah --storage-driver=$(params.STORAGE_DRIVER) push \ 76 | $(params.PUSH_EXTRA_ARGS) --tls-verify=$(params.TLSVERIFY) \ 77 | --digestfile $(workspaces.source.path)/image-digest $(params.IMAGE) \ 78 | docker://$(params.IMAGE) 79 | cat $(workspaces.source.path)/image-digest | tee /tekton/results/IMAGE_DIGEST 80 | echo "$(params.IMAGE)" | tee $(results.IMAGE_URL.path) 81 | securityContext: 82 | capabilities: 83 | add: 84 | - SETFCAP 85 | volumeMounts: 86 | - mountPath: /var/lib/containers 87 | name: varlibcontainers 88 | workingDir: $(workspaces.source.path) 89 | volumes: 90 | - name: varlibcontainers 91 | workspaces: 92 | - name: source 93 | -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-create-commit-list.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: create-commit-list 5 | spec: 6 | description: >- 7 | This task creates a message with a link to the commit list between current deployment and PR 8 | params: 9 | - name: deployment 10 | description: name of the deployment 11 | - name: container 12 | description: the ordinal position in the container (0,1,2) in .spec.template.spec.containers 13 | default: "0" 14 | - name: namespace 15 | description: namespace where the deployment is located 16 | - name: image_tag 17 | description: tag of the image that is expected to be deployed 18 | - name: git_source_url 19 | description: The URL of the repo containing the source code, used to construct a link for code changes 20 | results: 21 | - description: The message or link reflecting the list of commits between current image and proposed image 22 | name: commit-list-message 23 | steps: 24 | - name: create-commit-list 25 | image: quay.io/gnunn/tools:4.10-1 26 | script: | 27 | echo "#### Checking current tag in namespace $(params.namespace) ####" 28 | CURRENT_TAG=$(oc get deploy $(params.deployment) -n $(params.namespace) -o jsonpath="{.spec.template.spec.containers[$(params.container)].image}" | cut -d ":" -f2) 29 | CURRENT_COMMIT=${CURRENT_TAG%-*} 30 | echo "Currently deployed commit is $CURRENT_COMMIT" 31 | 32 | NEW_TAG=$(params.image_tag) 33 | NEW_COMMIT=${NEW_TAG%-*} 34 | echo "New commit in PR will be $NEW_COMMIT" 35 | 36 | if [ "$CURRENT_COMMIT" == "$NEW_COMMIT" ]; then 37 | echo -n "No code changes between images" > $(results.commit-list-message.path) 38 | else 39 | echo -n "[$CURRENT_COMMIT to $NEW_COMMIT]($(params.git_source_url)/compare/$CURRENT_COMMIT..$NEW_COMMIT)" > $(results.commit-list-message.path) 40 | fi -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-create-pr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: task-create-pr 5 | spec: 6 | workspaces: 7 | - name: source 8 | params: 9 | - name: title 10 | type: string 11 | description: PR Title 12 | - name: body 13 | type: string 14 | default: "" 15 | description: The body of the PR request 16 | - name: github_secret 17 | type: string 18 | default: github 19 | description: "The name of the secret that has your github username and token" 20 | steps: 21 | - name: run-commands 22 | image: docker.io/michaelin/github-cli:latest 23 | script: | 24 | #!/usr/bin/env bash 25 | 26 | # git create PR 27 | gh pr create -t "$(params.title)" -b "$(params.body)" 28 | workingDir: $(workspaces.source.path) 29 | env: 30 | - name: GITHUB_TOKEN 31 | valueFrom: 32 | secretKeyRef: 33 | name: $(params.github_secret) 34 | key: password 35 | - name: GITHUB_USER 36 | valueFrom: 37 | secretKeyRef: 38 | name: $(params.github_secret) 39 | key: username 40 | - name: GITHUB_EMAIL 41 | valueFrom: 42 | secretKeyRef: 43 | name: $(params.github_secret) 44 | key: email 45 | -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: deploy 5 | spec: 6 | params: 7 | - name: NAME 8 | type: string 9 | - name: NAMESPACE 10 | type: string 11 | steps: 12 | - name: run-commands 13 | image: quay.io/openshift/origin-cli:latest 14 | script: | 15 | #!/usr/bin/env bash 16 | 17 | oc rollout restart deployment/$(inputs.params.NAME) -n $(inputs.params.NAMESPACE) 18 | oc rollout status deploy/$(inputs.params.NAME) -n $(inputs.params.NAMESPACE) -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-git.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: git 5 | spec: 6 | workspaces: 7 | - name: source 8 | params: 9 | - name: commands 10 | type: string 11 | description: The set of commands to run 12 | stepTemplate: 13 | env: 14 | - name: "HOME" 15 | value: "/tekton/home" 16 | steps: 17 | - name: git 18 | workingDir: $(workspaces.source.path) 19 | image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.12.1 20 | script: | 21 | #!/usr/bin/env sh 22 | $(params.commands) 23 | -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-kustomize.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: kustomize 5 | spec: 6 | params: 7 | - name: image-name 8 | type: string 9 | description: "The placeholder image in the deployment to be replaced." 10 | - name: new-image 11 | type: string 12 | description: "The image to update the deployment with." 13 | - name: new-tag 14 | type: string 15 | description: "The image to tag update the deployment with." 16 | - name: overlaypath 17 | type: string 18 | description: "Path to the overlay directory." 19 | steps: 20 | - name: kustomize 21 | workingDir: $(workspaces.source.path) 22 | image: quay.io/redhatworkshops/kustomize-task:v4.0.5 23 | script: | 24 | #!/usr/bin/env /bin/sh 25 | echo "Kustomizing image" 26 | echo "Running: kustomize edit set image $(params.image-name)=$(params.new-image):$(params.new-tag)" 27 | cd $(params.overlaypath) 28 | kustomize edit set image $(params.image-name)=$(params.new-image):$(params.new-tag) 29 | 30 | echo "Updated file kustomization.yaml file" 31 | cat kustomization.yaml 32 | workspaces: 33 | - name: source -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-npm-quality.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: npm-quality 5 | spec: 6 | workspaces: 7 | - name: source 8 | params: 9 | - name: SONAR_SERVER_URL 10 | default: "http://sonarqube.dev-tools:9000" 11 | description: The location of sonarqube, passed as an environment variable SONAR_SERVER_URL 12 | steps: 13 | - name: quality 14 | image: registry.redhat.io/ubi8/nodejs-14:latest 15 | command: 16 | - /bin/sh 17 | - -c 18 | args: 19 | - |- 20 | echo "Performing quality check" 21 | mkdir -p coverage 22 | mkdir -p reports/dependency-check 23 | dependency-check.sh --format ALL -s . --out reports/dependency-check --project "product-catalog-client" 24 | npm run sonar 25 | echo "Quality check complete, see results in sonarqube" 26 | env: 27 | - name: SONAR_SERVER_URL 28 | value: $(params.SONAR_SERVER_URL) 29 | workingDir: $(workspaces.source.path) 30 | volumeMounts: 31 | - name: dependency-cache 32 | mountPath: /opt/dependency-check/data 33 | volumes: 34 | - name: dependency-cache 35 | persistentVolumeClaim: 36 | claimName: dependency-cache -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-npm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: npm 5 | spec: 6 | workspaces: 7 | - name: source 8 | steps: 9 | - name: npm-install 10 | image: registry.redhat.io/ubi8/nodejs-14:latest 11 | command: 12 | - /bin/sh 13 | - -c 14 | args: 15 | - npm install --package-lock 16 | workingDir: $(workspaces.source.path) 17 | - name: build 18 | image: registry.redhat.io/ubi8/nodejs-14:latest 19 | command: 20 | - /bin/sh 21 | - -c 22 | args: 23 | - npm run build 24 | workingDir: $(workspaces.source.path) 25 | - name: copy-dist 26 | image: registry.redhat.io/ubi8/nodejs-14:latest 27 | command: 28 | - /bin/sh 29 | - -c 30 | args: 31 | - cp -R ./dist/* $(workspaces.source.path) 32 | workingDir: $(workspaces.source.path) 33 | - name: save-commit-sha 34 | image: docker.io/maven:3.6.3-jdk-8 35 | command: 36 | - '/bin/bash' 37 | - '-c' 38 | args: 39 | - |- 40 | mkdir -p $(workspaces.source.path)/git-sha 41 | git rev-parse --short HEAD | tee $(workspaces.source.path)/git-sha/commit-id 42 | workingDir: $(workspaces.source.path) -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-push-image.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: push-image 5 | spec: 6 | params: 7 | - name: src_image 8 | type: string 9 | - name: dest_image 10 | type: string 11 | - name: dest_tags 12 | type: string 13 | default: "" 14 | steps: 15 | - name: run-commands 16 | image: quay.io/skopeo/stable:v1.13.2 17 | script: | 18 | #!/usr/bin/env bash 19 | 20 | if [ "$(params.dest_tags)" != "" ]; 21 | then 22 | 23 | tags=$(params.dest_tags) 24 | for i in ${tags//,/ } 25 | do 26 | echo "Copying image docker://$(params.src_image) to docker://$(params.dest_image):$i" 27 | skopeo copy --src-tls-verify=false --dest-tls-verify=false docker://$(params.src_image) --dest-authfile=/tekton/creds-secrets/dest-docker-config/.dockerconfigjson docker://$(params.dest_image):$i 28 | done 29 | fi -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-run-pipeline.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: run-tekton-pipeline 5 | spec: 6 | params: 7 | - name: pipeline 8 | type: string 9 | description: The pipeline to execute 10 | - name: args 11 | type: string 12 | description: A space separated list of pipeline arguments (i.e. --param param1=value --param param2=value --workspace workspace) 13 | steps: 14 | - name: tkn 15 | image: quay.io/gnunn/pipelines-cli-tkn-rhel8:v1.6.2-3 16 | script: | 17 | #!/usr/bin/env sh 18 | echo "Starting pipeline with command 'tkn pipeline start $(params.pipeline) $(params.args) --output=json'" 19 | export PIPELINERUN=$(tkn pipeline start $(params.pipeline) $(params.args) --output=json | jq -r .metadata.name) 20 | if [ -z "$PIPELINERUN" ] 21 | then 22 | echo "\$PIPELINERUN is empty, error occurred aborting" 23 | exit 1 24 | fi 25 | 26 | echo "Executing PipelineRun $PIPELINERUN" 27 | tkn pipelinerun logs $PIPELINERUN --follow 28 | 29 | export RESULT=$(oc get pipelinerun $PIPELINERUN -o=jsonpath='{.status.conditions[?(@.type=="Succeeded")].status}') 30 | 31 | if [ "$RESULT" = "True" ]; then 32 | echo "PipelineRun $PIPELINERUN completed successfully" 33 | exit 0 34 | else 35 | echo "PipelineRun $PIPELINERUN failed" 36 | exit 1 37 | fi 38 | volumeMounts: 39 | - name: templates 40 | mountPath: /templates 41 | volumes: 42 | - name: templates 43 | configMap: 44 | name: workspace-template 45 | optional: true -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-send-to-webhook-slack.yaml: -------------------------------------------------------------------------------- 1 | # Adapted from Tekton Catalog item 2 | # https://github.com/tektoncd/catalog/tree/main/task/send-to-webhook-slack/0.1 3 | apiVersion: tekton.dev/v1 4 | kind: Task 5 | metadata: 6 | name: send-to-webhook-slack 7 | labels: 8 | app.kubernetes.io/version: "0.1" 9 | annotations: 10 | tekton.dev/pipelines.minVersion: "0.12.1" 11 | tekton.dev/tags: messaging 12 | spec: 13 | description: >- 14 | These tasks post a simple message to a slack channel. 15 | This task uses Incoming Webhooks of slack to send the message. 16 | params: 17 | - name: message 18 | type: string 19 | description: Basic message, markdown can be used for formatting 20 | - name: console_message 21 | type: string 22 | description: Message to log to console 23 | default: "" 24 | - name: message_type 25 | type: string 26 | description: The type of message to send, can be set to "raw" or "markdown". If set to "raw" the user is expected to provide a full json payload conforming to slack's specifications. 27 | default: "markdown" 28 | workspaces: 29 | - name: slack-secret 30 | optional: true 31 | steps: 32 | - name: post 33 | image: registry.redhat.io/openshift4/ose-tools-rhel8:v4.10 34 | script: | 35 | #!/bin/sh 36 | 37 | echo "Preparing to send message..." 38 | 39 | MESSAGE=$(cat <<-END 40 | $(params.message) 41 | END 42 | ) 43 | 44 | CONSOLE_MESSAGE=$(cat <<-END 45 | $(params.console_message) 46 | END 47 | ) 48 | 49 | # Echo message locally for logging purposes 50 | if [ -n "$CONSOLE_MESSAGE" ]; then 51 | echo "$CONSOLE_MESSAGE" 52 | elif [ -n "$MESSAGE" ]; then 53 | echo "$MESSAGE" 54 | fi 55 | 56 | if [ $(workspaces.slack-secret.bound) == "true" ] ; then 57 | if [ -f "$(workspaces.slack-secret.path)/url" ]; then 58 | printf "\nSending to slack...\n\n" 59 | URL=$(cat $(workspaces.slack-secret.path)/url) 60 | 61 | if [ "$(params.message_type)" == "raw" ]; then 62 | echo "Sending raw message to slack" 63 | /usr/bin/curl -s -S -X POST -H 'Content-type: application/json' --data "${MESSAGE}" $URL 64 | else 65 | echo "Sending message to slack" 66 | /usr/bin/curl -s -S -X POST -H 'Content-type: application/json' --data '{"type": "mrkdwn", "text":"$(params.message)"}' $URL 67 | fi 68 | fi 69 | else 70 | echo "Note: Slack message was not sent as it was not configured" 71 | fi -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-tekton.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: tkn 5 | spec: 6 | params: 7 | - name: commands 8 | type: string 9 | description: The tkn commands to run 10 | steps: 11 | - name: tkn 12 | image: quay.io/rhcanada/tkn-cli:0.17.2 13 | script: | 14 | #!/usr/bin/env sh 15 | echo "Running command '$(params.commands)'" 16 | $(params.commands) 17 | volumeMounts: 18 | - name: templates 19 | mountPath: /templates 20 | volumes: 21 | - name: templates 22 | configMap: 23 | name: workspace-template 24 | optional: true -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-update-image.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: update-image 5 | spec: 6 | workspaces: 7 | - name: gitops-manifests 8 | params: 9 | - name: git_revision 10 | type: string 11 | default: main 12 | - name: path 13 | type: string 14 | description: The path of the kustomization file which we will use to update the image reference 15 | - name: image 16 | type: string 17 | description: The name of the image that is being updated 18 | - name: image_tag 19 | type: string 20 | description: The tag of the new image that will be deployed 21 | - name: push_retry_count 22 | type: string 23 | description: The number of times to re-attempt a push to git in case of failure (does a push-pull with retry) 24 | default: "3" 25 | results: 26 | - description: Whether the image was updated and committed 27 | name: image_updated 28 | - description: The short commit hash for the update 29 | name: image_updated_short_commit 30 | stepTemplate: 31 | env: 32 | - name: "HOME" 33 | value: "/tekton/home" 34 | steps: 35 | - name: update-image 36 | workingDir: $(workspaces.gitops-manifests.path) 37 | image: quay.io/redhatworkshops/kustomize-task:v4.0.5 38 | script: | 39 | echo "Kustomizing image" 40 | echo "Running: kustomize edit set image $(params.image)=$(params.image):$(params.image_tag)" 41 | 42 | cd $(params.path) 43 | kustomize edit set image $(params.image)=$(params.image):$(params.image_tag) 44 | 45 | echo "Updated file kustomization.yaml file" 46 | cat kustomization.yaml 47 | - name: commit-and-push-change 48 | workingDir: $(workspaces.gitops-manifests.path) 49 | image: docker.io/alpine/git:v2.26.2@sha256:23618034b0be9205d9cc0846eb711b12ba4c9b468efdd8a59aac1d7b1a23363f 50 | script: | 51 | if git diff --exit-code; 52 | then 53 | echo "No changes staged, skipping add/commit" 54 | echo -n "false" > $(results.image_updated.path) 55 | else 56 | echo "Changes made, committing" 57 | git config --global user.name "pipeline" 58 | git config --global user.email "pipelines@nomail.com" 59 | git add -u 60 | git commit -m 'Update image in git to $(params.image):$(params.image_tag)' 61 | echo "Running 'git push origin HEAD:$(params.git_revision)'" 62 | 63 | 64 | n=0 65 | until [ "$n" -ge $(params.push_retry_count) ] 66 | do 67 | git push origin HEAD:$(params.git_revision) && break 68 | git pull origin HEAD:$(params.git_revision) 69 | n=$((n+1)) 70 | done 71 | 72 | if [ "$n" -ge $(params.push_retry_count) ]; 73 | then 74 | echo "Failed to push change to git" 75 | exit 1 76 | fi 77 | 78 | echo -n "true" > $(results.image_updated.path) 79 | git rev-parse --short HEAD > $(results.image_updated_short_commit.path) 80 | fi -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-variables.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: variables-from-k8s 5 | spec: 6 | results: 7 | - name: acs_central_endpoint 8 | workspaces: 9 | - name: acs-central 10 | optional: true 11 | steps: 12 | - name: get-variables 13 | image: quay.io/openshift/origin-cli:latest 14 | script: | 15 | #!/usr/bin/env sh 16 | if [ $(workspaces.acs-central.bound) == "true" ] ; then 17 | cat $(workspaces.acs-central.path)/rox_central_endpoint > $(results.acs_central_endpoint.path) 18 | else 19 | echo "not-set" > $(results.acs_central_endpoint.path) 20 | fi -------------------------------------------------------------------------------- /components/tekton/tasks/base/task-yq.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: yq 5 | spec: 6 | workspaces: 7 | - name: source 8 | params: 9 | - name: commands 10 | type: string 11 | description: The set of commands to run 12 | steps: 13 | - name: yq 14 | workingDir: $(workspaces.source.path) 15 | image: mikefarah/yq:3.3.2 16 | script: | 17 | #!/usr/bin/env sh 18 | echo "Running: '$(params.commands)'" 19 | $(params.commands) -------------------------------------------------------------------------------- /components/tekton/triggers/base/argocd-notification-triggerbinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: triggers.tekton.dev/v1beta1 2 | kind: TriggerBinding 3 | metadata: 4 | name: argocd-notification 5 | spec: 6 | params: 7 | - name: state 8 | value: $(body.state) 9 | - name: application 10 | value: $(body.application) 11 | - name: description 12 | value: $(body.description) -------------------------------------------------------------------------------- /components/tekton/triggers/base/client-eventlistener.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: triggers.tekton.dev/v1beta1 3 | kind: EventListener 4 | metadata: 5 | name: client 6 | spec: 7 | serviceAccountName: pipeline 8 | triggers: 9 | - name: client-webhook 10 | bindings: 11 | - kind: ClusterTriggerBinding 12 | ref: github-push 13 | template: 14 | ref: client 15 | --- 16 | apiVersion: route.openshift.io/v1 17 | kind: Route 18 | metadata: 19 | name: client-webhook 20 | labels: 21 | app.kubernetes.io/managed-by: EventListener 22 | app.kubernetes.io/part-of: Triggers 23 | eventlistener: client 24 | spec: 25 | port: 26 | targetPort: 8080 27 | to: 28 | kind: "Service" 29 | name: el-client 30 | weight: 100 31 | tls: 32 | termination: edge -------------------------------------------------------------------------------- /components/tekton/triggers/base/client-triggertemplate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: triggers.tekton.dev/v1beta1 3 | kind: TriggerTemplate 4 | metadata: 5 | name: client 6 | spec: 7 | params: 8 | - name: git-revision 9 | description: The git revision 10 | default: master 11 | - name: git-repo-url 12 | description: The git repository url 13 | resourcetemplates: 14 | - apiVersion: tekton.dev/v1 15 | kind: PipelineRun 16 | metadata: 17 | annotations: 18 | argocd.argoproj.io/compare-options: IgnoreExtraneous 19 | argocd.argoproj.io/sync-options: Prune=false 20 | labels: 21 | tekton.dev/pipeline: client 22 | generateName: client-webhook- 23 | spec: 24 | pipelineRef: 25 | name: client 26 | params: 27 | - name: git-url 28 | value: $(tt.params.git-repo-url) 29 | - name: git-revision 30 | value: $(tt.params.git-revision) 31 | - name: push-to-prod 32 | value: "true" 33 | workspaces: 34 | - name: git-source 35 | volumeClaimTemplate: 36 | spec: 37 | accessModes: 38 | - ReadWriteOnce 39 | resources: 40 | requests: 41 | storage: 5Gi 42 | - name: gitops-manifests 43 | volumeClaimTemplate: 44 | spec: 45 | accessModes: 46 | - ReadWriteOnce 47 | resources: 48 | requests: 49 | storage: 5Gi -------------------------------------------------------------------------------- /components/tekton/triggers/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - server-eventlistener.yaml 6 | - server-triggertemplate.yaml 7 | - client-eventlistener.yaml 8 | - client-triggertemplate.yaml 9 | - argocd-notification-triggerbinding.yaml 10 | - server-post-prod-eventlistener.yaml 11 | - server-post-prod-triggertemplate.yaml 12 | - slack-message-eventlistener.yaml 13 | - slack-message-triggerbinding.yaml 14 | - slack-message-triggertemplate.yaml -------------------------------------------------------------------------------- /components/tekton/triggers/base/server-eventlistener.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: triggers.tekton.dev/v1beta1 3 | kind: EventListener 4 | metadata: 5 | name: server 6 | spec: 7 | serviceAccountName: pipeline 8 | triggers: 9 | - name: server-webhook 10 | bindings: 11 | - kind: ClusterTriggerBinding 12 | ref: github-push 13 | template: 14 | ref: server 15 | --- 16 | apiVersion: route.openshift.io/v1 17 | kind: Route 18 | metadata: 19 | name: server-webhook 20 | labels: 21 | app.kubernetes.io/managed-by: EventListener 22 | app.kubernetes.io/part-of: Triggers 23 | eventlistener: server 24 | spec: 25 | port: 26 | targetPort: 8080 27 | to: 28 | kind: "Service" 29 | name: el-server 30 | weight: 100 31 | tls: 32 | termination: edge -------------------------------------------------------------------------------- /components/tekton/triggers/base/server-post-prod-eventlistener.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Use with post-sync job 4 | 5 | apiVersion: triggers.tekton.dev/v1beta1 6 | kind: EventListener 7 | metadata: 8 | name: server-post-prod 9 | spec: 10 | serviceAccountName: pipeline 11 | triggers: 12 | - name: server-post-prod-webhook 13 | template: 14 | ref: server-post-prod 15 | 16 | # Use with Argo CD Notifications 17 | # 18 | # apiVersion: triggers.tekton.dev/v1beta1 19 | # kind: EventListener 20 | # metadata: 21 | # name: server-post-prod 22 | # spec: 23 | # serviceAccountName: pipeline 24 | # triggers: 25 | # - name: server-post-prod-webhook 26 | # interceptors: 27 | # - name: "Only accept sync succeeded" 28 | # ref: 29 | # name: "cel" 30 | # params: 31 | # - name: "filter" 32 | # value: "body.state in ['success']" 33 | # bindings: 34 | # - kind: TriggerBinding 35 | # ref: argocd-notification 36 | # template: 37 | # ref: server-post-prod 38 | --- 39 | apiVersion: route.openshift.io/v1 40 | kind: Route 41 | metadata: 42 | name: server-post-prod-webhook 43 | labels: 44 | app.kubernetes.io/managed-by: EventListener 45 | app.kubernetes.io/part-of: Triggers 46 | eventlistener: server-post-prod 47 | spec: 48 | port: 49 | targetPort: 8080 50 | to: 51 | kind: "Service" 52 | name: el-server-post-prod 53 | weight: 100 54 | tls: 55 | termination: edge -------------------------------------------------------------------------------- /components/tekton/triggers/base/server-post-prod-triggertemplate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: triggers.tekton.dev/v1beta1 3 | kind: TriggerTemplate 4 | metadata: 5 | name: server-post-prod 6 | spec: 7 | resourcetemplates: 8 | - apiVersion: tekton.dev/v1 9 | kind: PipelineRun 10 | metadata: 11 | annotations: 12 | argocd.argoproj.io/compare-options: IgnoreExtraneous 13 | argocd.argoproj.io/sync-options: Prune=false 14 | labels: 15 | tekton.dev/pipeline: server-post-prod 16 | generateName: server-post-prod-webhook- 17 | spec: 18 | pipelineRef: 19 | name: server-post-prod 20 | workspaces: 21 | - name: newman-env 22 | configMap: 23 | name: newman-env -------------------------------------------------------------------------------- /components/tekton/triggers/base/server-triggertemplate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: triggers.tekton.dev/v1beta1 3 | kind: TriggerTemplate 4 | metadata: 5 | name: server 6 | spec: 7 | params: 8 | - name: git-revision 9 | description: The git revision 10 | default: master 11 | - name: git-repo-url 12 | description: The git repository url 13 | resourcetemplates: 14 | - apiVersion: tekton.dev/v1 15 | kind: PipelineRun 16 | metadata: 17 | annotations: 18 | argocd.argoproj.io/compare-options: IgnoreExtraneous 19 | argocd.argoproj.io/sync-options: Prune=false 20 | labels: 21 | tekton.dev/pipeline: server 22 | generateName: server-webhook- 23 | spec: 24 | pipelineRef: 25 | name: server 26 | params: 27 | - name: source_git_url 28 | #value: $(tt.params.git-repo-url) 29 | value: "https://github.com/gnunn-gitops/product-catalog-server" 30 | - name: source_git_revision 31 | value: $(tt.params.git-revision) 32 | - name: MAVEN_MIRROR_URL 33 | value: http://nexus:8081/content/groups/public/ 34 | - name: push-to-prod 35 | value: "false" 36 | workspaces: 37 | - name: git-source 38 | volumeClaimTemplate: 39 | spec: 40 | accessModes: 41 | - ReadWriteOnce 42 | resources: 43 | requests: 44 | storage: 5Gi 45 | - name: gitops-manifests 46 | volumeClaimTemplate: 47 | spec: 48 | accessModes: 49 | - ReadWriteOnce 50 | resources: 51 | requests: 52 | storage: 5Gi 53 | - name: maven-settings 54 | configmap: 55 | name: maven-settings 56 | - name: newman-env 57 | configMap: 58 | name: newman-env 59 | -------------------------------------------------------------------------------- /components/tekton/triggers/base/slack-message-eventlistener.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: triggers.tekton.dev/v1beta1 3 | kind: EventListener 4 | metadata: 5 | name: slack-message 6 | spec: 7 | serviceAccountName: pipeline 8 | triggers: 9 | - name: slack-message-webhook 10 | bindings: 11 | - kind: TriggerBinding 12 | ref: slack-message 13 | template: 14 | ref: slack-message 15 | -------------------------------------------------------------------------------- /components/tekton/triggers/base/slack-message-triggerbinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: triggers.tekton.dev/v1beta1 2 | kind: TriggerBinding 3 | metadata: 4 | name: slack-message 5 | spec: 6 | params: 7 | - name: git_revision 8 | value: $(body.git_revision) 9 | - name: git_url 10 | value: $(body.git_url) 11 | - name: git_source_url 12 | value: $(body.git_source_url) 13 | - name: image_dest_url 14 | value: $(body.image_dest_url) 15 | - name: image_dest_tag 16 | value: $(body.image_dest_tag) 17 | - name: cluster 18 | value: $(body.cluster) 19 | - name: app 20 | value: $(body.app) 21 | -------------------------------------------------------------------------------- /components/tekton/triggers/base/slack-message-triggertemplate.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: triggers.tekton.dev/v1beta1 3 | kind: TriggerTemplate 4 | metadata: 5 | name: slack-message 6 | spec: 7 | params: 8 | - name: git_revision 9 | description: The git revision 10 | default: main 11 | - name: git_url 12 | description: The git repository url 13 | - name: git_source_url 14 | description: The git repository source code URL 15 | - name: image_dest_url 16 | description: External registry location of image to push without tag 17 | - name: image_dest_tag 18 | description: Tag of image to push to production 19 | - name: cluster 20 | description: Cluster to push to 21 | - name: app 22 | description: Either 'server' or 'client' 23 | resourcetemplates: 24 | - apiVersion: tekton.dev/v1 25 | kind: PipelineRun 26 | metadata: 27 | labels: 28 | tekton.dev/pipeline: push-prod-pr 29 | generateName: slack-message-webhook- 30 | spec: 31 | pipelineRef: 32 | name: push-prod-pr 33 | params: 34 | - name: git_url 35 | value: $(tt.params.git_url) 36 | - name: git_revision 37 | value: $(tt.params.git_revision) 38 | - name: git_source_url 39 | value: $(tt.params.git_source_url) 40 | - name: image_dest_tag 41 | value: $(tt.params.image_dest_tag) 42 | - name: image_dest_url 43 | value: $(tt.params.image_dest_url) 44 | - name: cluster 45 | value: $(tt.params.cluster) 46 | - name: app 47 | value: $(tt.params.app) 48 | workspaces: 49 | - name: gitops-manifests 50 | volumeClaimTemplate: 51 | spec: 52 | accessModes: 53 | - ReadWriteOnce 54 | resources: 55 | requests: 56 | storage: 5Gi 57 | - name: acs-central 58 | secret: 59 | secretName: roxsecrets -------------------------------------------------------------------------------- /docs/img/argocd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnunn-gitops/product-catalog/68a63f5aa63b27c944113abdef7ec09b12718658/docs/img/argocd.png -------------------------------------------------------------------------------- /docs/img/cicd-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnunn-gitops/product-catalog/68a63f5aa63b27c944113abdef7ec09b12718658/docs/img/cicd-flow.png -------------------------------------------------------------------------------- /docs/img/client-server-database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnunn-gitops/product-catalog/68a63f5aa63b27c944113abdef7ec09b12718658/docs/img/client-server-database.png -------------------------------------------------------------------------------- /docs/img/monitoring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnunn-gitops/product-catalog/68a63f5aa63b27c944113abdef7ec09b12718658/docs/img/monitoring.png -------------------------------------------------------------------------------- /docs/img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnunn-gitops/product-catalog/68a63f5aa63b27c944113abdef7ec09b12718658/docs/img/screenshot.png -------------------------------------------------------------------------------- /docs/img/tekton-rerun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnunn-gitops/product-catalog/68a63f5aa63b27c944113abdef7ec09b12718658/docs/img/tekton-rerun.png -------------------------------------------------------------------------------- /docs/img/topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnunn-gitops/product-catalog/68a63f5aa63b27c944113abdef7ec09b12718658/docs/img/topology.png -------------------------------------------------------------------------------- /docs/swagger/openapi-3.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "info": { 4 | "title": "Product Catalog API", 5 | "contact": { 6 | "name": "Product Catalog API Support", 7 | "url": "http://demo.com/contact", 8 | "email": "techsupport@demo.com" 9 | }, 10 | "license": { 11 | "name": "Apache 2.0", 12 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 13 | }, 14 | "version": "1.0.0" 15 | }, 16 | "tags": [ 17 | { 18 | "name": "Authentication", 19 | "description": "An API to manage user authentication and authorization" 20 | }, 21 | { 22 | "name": "Categories", 23 | "description": "An API to manipulate the categories in the catalog" 24 | }, 25 | { 26 | "name": "Products", 27 | "description": "An API to manipulate the products in the catalog" 28 | }, 29 | { 30 | "name": "Users", 31 | "description": "An API to manipulate users of the catalog" 32 | } 33 | ], 34 | "paths": { 35 | "/api/auth": { 36 | "post": { 37 | "tags": [ 38 | "Authentication" 39 | ], 40 | "summary": "Login", 41 | "description": "Authenticate a user", 42 | "responses": { 43 | "200": { 44 | "description": "OK", 45 | "content": { 46 | "application/json": { 47 | "schema": { 48 | "$ref": "#/components/schemas/LoginResult" 49 | } 50 | } 51 | } 52 | } 53 | } 54 | } 55 | }, 56 | "/api/auth/register": { 57 | "post": { 58 | "tags": [ 59 | "Authentication" 60 | ], 61 | "summary": "Register", 62 | "description": "Register a new user", 63 | "requestBody": { 64 | "content": { 65 | "application/x-www-form-urlencoded": { 66 | "schema": { 67 | "type": "object", 68 | "properties": { 69 | "email": { 70 | "type": "string" 71 | }, 72 | "password": { 73 | "type": "string" 74 | }, 75 | "password_confirmation": { 76 | "type": "string" 77 | } 78 | } 79 | } 80 | } 81 | } 82 | }, 83 | "responses": { 84 | "200": { 85 | "description": "OK" 86 | } 87 | } 88 | } 89 | }, 90 | "/api/auth/user": { 91 | "get": { 92 | "tags": [ 93 | "Authentication" 94 | ], 95 | "summary": "Get user", 96 | "description": "Get the current user", 97 | "responses": { 98 | "200": { 99 | "description": "OK", 100 | "content": { 101 | "application/json": { 102 | "schema": { 103 | "$ref": "#/components/schemas/User" 104 | } 105 | } 106 | } 107 | } 108 | } 109 | } 110 | }, 111 | "/api/category": { 112 | "get": { 113 | "tags": [ 114 | "Categories" 115 | ], 116 | "responses": { 117 | "200": { 118 | "description": "OK", 119 | "content": { 120 | "application/json": { 121 | "schema": { 122 | "type": "array", 123 | "items": { 124 | "$ref": "#/components/schemas/Category" 125 | } 126 | } 127 | } 128 | } 129 | } 130 | } 131 | }, 132 | "post": { 133 | "tags": [ 134 | "Categories" 135 | ], 136 | "summary": "Create category", 137 | "description": "Create a new category", 138 | "requestBody": { 139 | "content": { 140 | "application/json": { 141 | "schema": { 142 | "$ref": "#/components/schemas/Category" 143 | } 144 | } 145 | } 146 | }, 147 | "responses": { 148 | "200": { 149 | "description": "OK" 150 | } 151 | } 152 | } 153 | }, 154 | "/api/category/{id}": { 155 | "get": { 156 | "tags": [ 157 | "Categories" 158 | ], 159 | "summary": "Get category", 160 | "description": "Get specific category by ID", 161 | "parameters": [ 162 | { 163 | "name": "id", 164 | "in": "path", 165 | "required": true, 166 | "schema": { 167 | "format": "int32", 168 | "type": "integer" 169 | } 170 | } 171 | ], 172 | "responses": { 173 | "200": { 174 | "description": "OK", 175 | "content": { 176 | "application/json": { 177 | "schema": { 178 | "$ref": "#/components/schemas/Category" 179 | } 180 | } 181 | } 182 | } 183 | } 184 | }, 185 | "put": { 186 | "tags": [ 187 | "Categories" 188 | ], 189 | "summary": "Update category", 190 | "description": "Update an existing category", 191 | "parameters": [ 192 | { 193 | "name": "id", 194 | "in": "path", 195 | "required": true, 196 | "schema": { 197 | "format": "int32", 198 | "type": "integer" 199 | } 200 | } 201 | ], 202 | "requestBody": { 203 | "content": { 204 | "application/json": { 205 | "schema": { 206 | "$ref": "#/components/schemas/Category" 207 | } 208 | } 209 | } 210 | }, 211 | "responses": { 212 | "200": { 213 | "description": "OK", 214 | "content": { 215 | "application/json": { 216 | "schema": { 217 | "$ref": "#/components/schemas/Category" 218 | } 219 | } 220 | } 221 | } 222 | } 223 | }, 224 | "delete": { 225 | "tags": [ 226 | "Categories" 227 | ], 228 | "summary": "Delete category", 229 | "description": "Delete a category", 230 | "parameters": [ 231 | { 232 | "name": "id", 233 | "in": "path", 234 | "required": true, 235 | "schema": { 236 | "format": "int32", 237 | "type": "integer" 238 | } 239 | } 240 | ], 241 | "responses": { 242 | "200": { 243 | "description": "OK" 244 | } 245 | } 246 | } 247 | }, 248 | "/api/product": { 249 | "get": { 250 | "tags": [ 251 | "Products" 252 | ], 253 | "summary": "Get product list", 254 | "description": "Get product list with support for paging and ordering", 255 | "parameters": [ 256 | { 257 | "name": "item_per_page", 258 | "in": "query", 259 | "schema": { 260 | "format": "int32", 261 | "default": "0", 262 | "type": "integer" 263 | } 264 | }, 265 | { 266 | "name": "name", 267 | "in": "query", 268 | "schema": { 269 | "default": "", 270 | "type": "string" 271 | } 272 | }, 273 | { 274 | "name": "order_by", 275 | "in": "query", 276 | "schema": { 277 | "default": "name", 278 | "type": "string" 279 | } 280 | }, 281 | { 282 | "name": "order_type", 283 | "in": "query", 284 | "schema": { 285 | "default": "asc", 286 | "type": "string" 287 | } 288 | }, 289 | { 290 | "name": "page", 291 | "in": "query", 292 | "schema": { 293 | "format": "int32", 294 | "default": "0", 295 | "type": "integer" 296 | } 297 | } 298 | ], 299 | "responses": { 300 | "200": { 301 | "description": "OK", 302 | "content": { 303 | "application/json": { 304 | "schema": { 305 | "type": "array", 306 | "items": { 307 | "$ref": "#/components/schemas/Product" 308 | } 309 | } 310 | } 311 | } 312 | } 313 | } 314 | }, 315 | "post": { 316 | "tags": [ 317 | "Products" 318 | ], 319 | "summary": "Create product", 320 | "description": "Create a new product", 321 | "requestBody": { 322 | "content": { 323 | "application/x-www-form-urlencoded": { 324 | "schema": { 325 | "type": "object", 326 | "properties": { 327 | "name": { 328 | "type": "string" 329 | }, 330 | "description": { 331 | "type": "string" 332 | }, 333 | "category_id": { 334 | "format": "int32", 335 | "type": "integer" 336 | }, 337 | "price": { 338 | "format": "double", 339 | "type": "number" 340 | } 341 | } 342 | } 343 | } 344 | } 345 | }, 346 | "responses": { 347 | "200": { 348 | "description": "OK", 349 | "content": { 350 | "application/json": { 351 | "schema": { 352 | "$ref": "#/components/schemas/ProductResult" 353 | } 354 | } 355 | } 356 | } 357 | } 358 | } 359 | }, 360 | "/api/product/count": { 361 | "get": { 362 | "tags": [ 363 | "Products" 364 | ], 365 | "summary": "Get product count", 366 | "description": "Get the total count of products available", 367 | "responses": { 368 | "200": { 369 | "description": "OK" 370 | } 371 | } 372 | } 373 | }, 374 | "/api/product/delete": { 375 | "post": { 376 | "tags": [ 377 | "Products" 378 | ], 379 | "summary": "Delete a set of products", 380 | "description": "Delete a set of products as specified by their IDs", 381 | "requestBody": { 382 | "content": { 383 | "application/x-www-form-urlencoded": { 384 | "schema": { 385 | "type": "object", 386 | "properties": { 387 | "del_ids[]": { 388 | "$ref": "#/components/schemas/ListInteger" 389 | } 390 | } 391 | } 392 | } 393 | } 394 | }, 395 | "responses": { 396 | "200": { 397 | "description": "OK" 398 | } 399 | } 400 | } 401 | }, 402 | "/api/product/{id}": { 403 | "get": { 404 | "tags": [ 405 | "Products" 406 | ], 407 | "summary": "Get product by ID", 408 | "description": "Get specific product by it's ID", 409 | "parameters": [ 410 | { 411 | "name": "id", 412 | "in": "path", 413 | "required": true, 414 | "schema": { 415 | "format": "int32", 416 | "type": "integer" 417 | } 418 | } 419 | ], 420 | "responses": { 421 | "200": { 422 | "description": "OK", 423 | "content": { 424 | "application/json": { 425 | "schema": { 426 | "$ref": "#/components/schemas/Product" 427 | } 428 | } 429 | } 430 | } 431 | } 432 | }, 433 | "put": { 434 | "tags": [ 435 | "Products" 436 | ], 437 | "summary": "Update product", 438 | "description": "Update an existing product", 439 | "parameters": [ 440 | { 441 | "name": "id", 442 | "in": "path", 443 | "required": true, 444 | "schema": { 445 | "format": "int32", 446 | "type": "integer" 447 | } 448 | } 449 | ], 450 | "requestBody": { 451 | "content": { 452 | "application/x-www-form-urlencoded": { 453 | "schema": { 454 | "type": "object", 455 | "properties": { 456 | "id": { 457 | "format": "int32", 458 | "type": "integer" 459 | }, 460 | "name": { 461 | "type": "string" 462 | }, 463 | "description": { 464 | "type": "string" 465 | }, 466 | "category_id": { 467 | "format": "int32", 468 | "type": "integer" 469 | }, 470 | "price": { 471 | "format": "double", 472 | "type": "number" 473 | } 474 | } 475 | } 476 | } 477 | } 478 | }, 479 | "responses": { 480 | "200": { 481 | "description": "OK", 482 | "content": { 483 | "application/json": { 484 | "schema": { 485 | "$ref": "#/components/schemas/ProductResult" 486 | } 487 | } 488 | } 489 | } 490 | } 491 | }, 492 | "delete": { 493 | "tags": [ 494 | "Products" 495 | ], 496 | "summary": "Delete product", 497 | "description": "Delete a single product by ID", 498 | "parameters": [ 499 | { 500 | "name": "id", 501 | "in": "path", 502 | "required": true, 503 | "schema": { 504 | "format": "int32", 505 | "type": "integer" 506 | } 507 | } 508 | ], 509 | "responses": { 510 | "200": { 511 | "description": "OK" 512 | } 513 | } 514 | } 515 | }, 516 | "/api/user": { 517 | "post": { 518 | "tags": [ 519 | "Users" 520 | ], 521 | "summary": "Create user", 522 | "description": "Create a new user", 523 | "requestBody": { 524 | "content": { 525 | "application/json": { 526 | "schema": { 527 | "$ref": "#/components/schemas/User" 528 | } 529 | } 530 | } 531 | }, 532 | "responses": { 533 | "200": { 534 | "description": "OK" 535 | } 536 | } 537 | } 538 | }, 539 | "/api/user/{id}": { 540 | "get": { 541 | "tags": [ 542 | "Users" 543 | ], 544 | "summary": "Get user by ID", 545 | "description": "Get specific user by it's ID", 546 | "parameters": [ 547 | { 548 | "name": "id", 549 | "in": "path", 550 | "required": true, 551 | "schema": { 552 | "format": "int32", 553 | "type": "integer" 554 | } 555 | } 556 | ], 557 | "responses": { 558 | "200": { 559 | "description": "OK", 560 | "content": { 561 | "application/json": { 562 | "schema": { 563 | "$ref": "#/components/schemas/User" 564 | } 565 | } 566 | } 567 | } 568 | } 569 | }, 570 | "put": { 571 | "tags": [ 572 | "Users" 573 | ], 574 | "summary": "Update user", 575 | "description": "Update an existing user", 576 | "parameters": [ 577 | { 578 | "name": "id", 579 | "in": "path", 580 | "required": true, 581 | "schema": { 582 | "format": "int32", 583 | "type": "integer" 584 | } 585 | } 586 | ], 587 | "requestBody": { 588 | "content": { 589 | "application/json": { 590 | "schema": { 591 | "$ref": "#/components/schemas/User" 592 | } 593 | } 594 | } 595 | }, 596 | "responses": { 597 | "200": { 598 | "description": "OK", 599 | "content": { 600 | "application/json": { 601 | "schema": { 602 | "$ref": "#/components/schemas/User" 603 | } 604 | } 605 | } 606 | } 607 | } 608 | }, 609 | "delete": { 610 | "tags": [ 611 | "Users" 612 | ], 613 | "summary": "Delete user", 614 | "description": "Delete a user", 615 | "parameters": [ 616 | { 617 | "name": "id", 618 | "in": "path", 619 | "required": true, 620 | "schema": { 621 | "format": "int32", 622 | "type": "integer" 623 | } 624 | } 625 | ], 626 | "responses": { 627 | "200": { 628 | "description": "OK" 629 | } 630 | } 631 | } 632 | } 633 | }, 634 | "components": { 635 | "schemas": { 636 | "Product": { 637 | "required": [ 638 | "category", 639 | "name", 640 | "price" 641 | ], 642 | "type": "object", 643 | "properties": { 644 | "category": { 645 | "allOf": [ 646 | { 647 | "$ref": "#/components/schemas/Category" 648 | }, 649 | { 650 | "nullable": false 651 | } 652 | ] 653 | }, 654 | "created": { 655 | "$ref": "#/components/schemas/Date1" 656 | }, 657 | "description": { 658 | "type": "string" 659 | }, 660 | "id": { 661 | "format": "int32", 662 | "type": "integer" 663 | }, 664 | "modified": { 665 | "$ref": "#/components/schemas/LocalDateTime" 666 | }, 667 | "name": { 668 | "type": "string", 669 | "nullable": false 670 | }, 671 | "price": { 672 | "format": "double", 673 | "type": "number", 674 | "nullable": false 675 | } 676 | } 677 | }, 678 | "ProductResource": { 679 | "type": "object", 680 | "properties": { 681 | "count": { 682 | "$ref": "#/components/schemas/Response" 683 | } 684 | } 685 | }, 686 | "Response": { 687 | "type": "object", 688 | "properties": { 689 | "allowedMethods": { 690 | "$ref": "#/components/schemas/SetString" 691 | }, 692 | "cookies": { 693 | "$ref": "#/components/schemas/MapStringNewCookie" 694 | }, 695 | "date": { 696 | "$ref": "#/components/schemas/Date" 697 | }, 698 | "entity": { 699 | "type": "object" 700 | }, 701 | "entityTag": { 702 | "$ref": "#/components/schemas/EntityTag" 703 | }, 704 | "headers": { 705 | "$ref": "#/components/schemas/MultivaluedMapStringObject" 706 | }, 707 | "language": { 708 | "$ref": "#/components/schemas/Locale" 709 | }, 710 | "lastModified": { 711 | "$ref": "#/components/schemas/Date" 712 | }, 713 | "length": { 714 | "format": "int32", 715 | "type": "integer" 716 | }, 717 | "links": { 718 | "$ref": "#/components/schemas/SetLink" 719 | }, 720 | "location": { 721 | "$ref": "#/components/schemas/URI" 722 | }, 723 | "mediaType": { 724 | "$ref": "#/components/schemas/MediaType" 725 | }, 726 | "metadata": { 727 | "$ref": "#/components/schemas/MultivaluedMapStringObject" 728 | }, 729 | "status": { 730 | "format": "int32", 731 | "type": "integer" 732 | }, 733 | "statusInfo": { 734 | "$ref": "#/components/schemas/StatusType" 735 | }, 736 | "stringHeaders": { 737 | "$ref": "#/components/schemas/MultivaluedMapStringString" 738 | } 739 | } 740 | }, 741 | "SetString": { 742 | "type": "array", 743 | "items": { 744 | "type": "string" 745 | } 746 | }, 747 | "NewCookie": { 748 | "type": "object", 749 | "properties": { 750 | "domain": { 751 | "type": "string" 752 | }, 753 | "name": { 754 | "type": "string" 755 | }, 756 | "path": { 757 | "type": "string" 758 | }, 759 | "value": { 760 | "type": "string" 761 | }, 762 | "version": { 763 | "format": "int32", 764 | "type": "integer" 765 | }, 766 | "comment": { 767 | "type": "string" 768 | }, 769 | "expiry": { 770 | "$ref": "#/components/schemas/Date" 771 | }, 772 | "httpOnly": { 773 | "type": "boolean" 774 | }, 775 | "maxAge": { 776 | "format": "int32", 777 | "type": "integer" 778 | }, 779 | "secure": { 780 | "type": "boolean" 781 | } 782 | } 783 | }, 784 | "MapStringNewCookie": { 785 | "type": "object", 786 | "additionalProperties": { 787 | "$ref": "#/components/schemas/NewCookie" 788 | } 789 | }, 790 | "Date": { 791 | "format": "date", 792 | "type": "string" 793 | }, 794 | "EntityTag": { 795 | "type": "object", 796 | "properties": { 797 | "value": { 798 | "type": "string" 799 | }, 800 | "weak": { 801 | "type": "boolean" 802 | } 803 | } 804 | }, 805 | "MultivaluedMapStringObject": { 806 | "type": "object", 807 | "additionalProperties": {} 808 | }, 809 | "Locale": { 810 | "type": "object", 811 | "properties": { 812 | "country": { 813 | "type": "string" 814 | }, 815 | "displayCountry": { 816 | "type": "string" 817 | }, 818 | "displayLanguage": { 819 | "type": "string" 820 | }, 821 | "displayName": { 822 | "type": "string" 823 | }, 824 | "displayScript": { 825 | "type": "string" 826 | }, 827 | "displayVariant": { 828 | "type": "string" 829 | }, 830 | "extensionKeys": { 831 | "$ref": "#/components/schemas/SetCharacter" 832 | }, 833 | "iSO3Country": { 834 | "type": "string" 835 | }, 836 | "iSO3Language": { 837 | "type": "string" 838 | }, 839 | "language": { 840 | "type": "string" 841 | }, 842 | "script": { 843 | "type": "string" 844 | }, 845 | "unicodeLocaleAttributes": { 846 | "$ref": "#/components/schemas/SetString" 847 | }, 848 | "unicodeLocaleKeys": { 849 | "$ref": "#/components/schemas/SetString" 850 | }, 851 | "variant": { 852 | "type": "string" 853 | } 854 | } 855 | }, 856 | "Link": { 857 | "type": "object", 858 | "properties": { 859 | "params": { 860 | "$ref": "#/components/schemas/MapStringString" 861 | }, 862 | "rel": { 863 | "type": "string" 864 | }, 865 | "rels": { 866 | "$ref": "#/components/schemas/ListString" 867 | }, 868 | "title": { 869 | "type": "string" 870 | }, 871 | "type": { 872 | "type": "string" 873 | }, 874 | "uri": { 875 | "$ref": "#/components/schemas/URI" 876 | }, 877 | "uriBuilder": { 878 | "$ref": "#/components/schemas/UriBuilder" 879 | } 880 | } 881 | }, 882 | "SetLink": { 883 | "type": "array", 884 | "items": { 885 | "$ref": "#/components/schemas/Link" 886 | } 887 | }, 888 | "URI": { 889 | "format": "uri", 890 | "type": "string" 891 | }, 892 | "MediaType": { 893 | "type": "object", 894 | "properties": { 895 | "parameters": { 896 | "$ref": "#/components/schemas/MapStringString" 897 | }, 898 | "subtype": { 899 | "type": "string" 900 | }, 901 | "type": { 902 | "type": "string" 903 | }, 904 | "wildcardSubtype": { 905 | "type": "boolean" 906 | }, 907 | "wildcardType": { 908 | "type": "boolean" 909 | } 910 | } 911 | }, 912 | "StatusType": { 913 | "type": "object", 914 | "properties": { 915 | "family": { 916 | "$ref": "#/components/schemas/Family" 917 | }, 918 | "reasonPhrase": { 919 | "type": "string" 920 | }, 921 | "statusCode": { 922 | "format": "int32", 923 | "type": "integer" 924 | } 925 | } 926 | }, 927 | "MultivaluedMapStringString": { 928 | "type": "object", 929 | "additionalProperties": { 930 | "type": "string" 931 | } 932 | }, 933 | "Family": { 934 | "enum": [ 935 | "CLIENT_ERROR", 936 | "INFORMATIONAL", 937 | "OTHER", 938 | "REDIRECTION", 939 | "SERVER_ERROR", 940 | "SUCCESSFUL" 941 | ], 942 | "type": "string" 943 | }, 944 | "MapStringString": { 945 | "type": "object", 946 | "additionalProperties": { 947 | "type": "string" 948 | } 949 | }, 950 | "ListString": { 951 | "type": "array", 952 | "items": { 953 | "type": "string" 954 | } 955 | }, 956 | "UriBuilder": { 957 | "type": "object" 958 | }, 959 | "SetCharacter": { 960 | "type": "array", 961 | "items": { 962 | "format": "byte", 963 | "type": "string" 964 | } 965 | }, 966 | "Category": { 967 | "required": [ 968 | "id", 969 | "name" 970 | ], 971 | "type": "object", 972 | "properties": { 973 | "created": { 974 | "$ref": "#/components/schemas/Date1" 975 | }, 976 | "description": { 977 | "type": "string" 978 | }, 979 | "id": { 980 | "format": "int32", 981 | "type": "integer", 982 | "nullable": false 983 | }, 984 | "modified": { 985 | "$ref": "#/components/schemas/LocalDateTime" 986 | }, 987 | "name": { 988 | "type": "string", 989 | "nullable": false 990 | } 991 | } 992 | }, 993 | "Date1": { 994 | "format": "date", 995 | "type": "string" 996 | }, 997 | "LocalDateTime": { 998 | "format": "date-time", 999 | "type": "string" 1000 | }, 1001 | "ProductResult": { 1002 | "type": "object", 1003 | "properties": { 1004 | "message": { 1005 | "type": "string" 1006 | }, 1007 | "product": { 1008 | "$ref": "#/components/schemas/Product" 1009 | }, 1010 | "success": { 1011 | "type": "boolean" 1012 | }, 1013 | "this$0": { 1014 | "$ref": "#/components/schemas/ProductResource" 1015 | } 1016 | } 1017 | }, 1018 | "ListInteger": { 1019 | "$ref": "#/components/schemas/ListInteger" 1020 | }, 1021 | "User": { 1022 | "type": "object", 1023 | "properties": { 1024 | "createdAt": { 1025 | "$ref": "#/components/schemas/LocalDateTime" 1026 | }, 1027 | "email": { 1028 | "type": "string" 1029 | }, 1030 | "id": { 1031 | "format": "int32", 1032 | "type": "integer" 1033 | }, 1034 | "iterations": { 1035 | "format": "int32", 1036 | "type": "integer" 1037 | }, 1038 | "passwordHash": { 1039 | "type": "string" 1040 | }, 1041 | "salt": { 1042 | "type": "string" 1043 | } 1044 | } 1045 | }, 1046 | "AuthResource": { 1047 | "type": "object" 1048 | }, 1049 | "SecureUser": { 1050 | "type": "object", 1051 | "properties": { 1052 | "createdAt": { 1053 | "$ref": "#/components/schemas/LocalDateTime" 1054 | }, 1055 | "email": { 1056 | "type": "string" 1057 | }, 1058 | "this$0": { 1059 | "$ref": "#/components/schemas/AuthResource" 1060 | } 1061 | } 1062 | }, 1063 | "LoginResult": { 1064 | "type": "object", 1065 | "properties": { 1066 | "message": { 1067 | "type": "string" 1068 | }, 1069 | "this$0": { 1070 | "$ref": "#/components/schemas/AuthResource" 1071 | }, 1072 | "user": { 1073 | "$ref": "#/components/schemas/SecureUser" 1074 | } 1075 | } 1076 | } 1077 | } 1078 | } 1079 | } -------------------------------------------------------------------------------- /docs/swagger/swagger-2.0.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "contact": { 5 | "email": "techsupport@demo.com", 6 | "name": "Product Catalog API Support", 7 | "url": "http://demo.com/contact" 8 | }, 9 | "license": { 10 | "name": "Apache 2.0", 11 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 12 | }, 13 | "title": "Product Catalog API", 14 | "version": "1.0.0" 15 | }, 16 | "paths": { 17 | "/api/auth": { 18 | "post": { 19 | "produces": [ 20 | "application/json" 21 | ], 22 | "parameters": [], 23 | "responses": { 24 | "200": { 25 | "description": "OK", 26 | "schema": { 27 | "$ref": "#/definitions/LoginResult" 28 | } 29 | } 30 | }, 31 | "tags": [ 32 | "Authentication" 33 | ], 34 | "description": "Authenticate a user", 35 | "summary": "Login" 36 | } 37 | }, 38 | "/api/auth/register": { 39 | "post": { 40 | "consumes": [ 41 | "application/x-www-form-urlencoded" 42 | ], 43 | "parameters": [ 44 | { 45 | "in": "formData", 46 | "name": "email", 47 | "type": "string" 48 | }, 49 | { 50 | "in": "formData", 51 | "name": "password", 52 | "type": "string" 53 | }, 54 | { 55 | "in": "formData", 56 | "name": "password_confirmation", 57 | "type": "string" 58 | } 59 | ], 60 | "responses": { 61 | "200": { 62 | "description": "OK" 63 | } 64 | }, 65 | "tags": [ 66 | "Authentication" 67 | ], 68 | "description": "Register a new user", 69 | "summary": "Register" 70 | } 71 | }, 72 | "/api/auth/user": { 73 | "get": { 74 | "produces": [ 75 | "application/json" 76 | ], 77 | "parameters": [], 78 | "responses": { 79 | "200": { 80 | "description": "OK", 81 | "schema": { 82 | "$ref": "#/definitions/User" 83 | } 84 | } 85 | }, 86 | "tags": [ 87 | "Authentication" 88 | ], 89 | "description": "Get the current user", 90 | "summary": "Get user" 91 | } 92 | }, 93 | "/api/category": { 94 | "get": { 95 | "produces": [ 96 | "application/json" 97 | ], 98 | "parameters": [], 99 | "responses": { 100 | "200": { 101 | "description": "OK", 102 | "schema": { 103 | "items": { 104 | "$ref": "#/definitions/Category" 105 | }, 106 | "type": "array" 107 | } 108 | } 109 | }, 110 | "tags": [ 111 | "Categories" 112 | ] 113 | }, 114 | "post": { 115 | "consumes": [ 116 | "application/json" 117 | ], 118 | "parameters": [ 119 | { 120 | "in": "body", 121 | "name": "body", 122 | "schema": { 123 | "$ref": "#/definitions/Category" 124 | } 125 | } 126 | ], 127 | "responses": { 128 | "200": { 129 | "description": "OK" 130 | } 131 | }, 132 | "tags": [ 133 | "Categories" 134 | ], 135 | "description": "Create a new category", 136 | "summary": "Create category" 137 | } 138 | }, 139 | "/api/category/{id}": { 140 | "delete": { 141 | "parameters": [ 142 | { 143 | "format": "int32", 144 | "in": "path", 145 | "name": "id", 146 | "required": true, 147 | "type": "integer" 148 | } 149 | ], 150 | "responses": { 151 | "200": { 152 | "description": "OK" 153 | } 154 | }, 155 | "tags": [ 156 | "Categories" 157 | ], 158 | "description": "Delete a category", 159 | "summary": "Delete category" 160 | }, 161 | "get": { 162 | "produces": [ 163 | "application/json" 164 | ], 165 | "parameters": [ 166 | { 167 | "format": "int32", 168 | "in": "path", 169 | "name": "id", 170 | "required": true, 171 | "type": "integer" 172 | } 173 | ], 174 | "responses": { 175 | "200": { 176 | "description": "OK", 177 | "schema": { 178 | "$ref": "#/definitions/Category" 179 | } 180 | } 181 | }, 182 | "tags": [ 183 | "Categories" 184 | ], 185 | "description": "Get specific category by ID", 186 | "summary": "Get category" 187 | }, 188 | "put": { 189 | "consumes": [ 190 | "application/json" 191 | ], 192 | "produces": [ 193 | "application/json" 194 | ], 195 | "parameters": [ 196 | { 197 | "format": "int32", 198 | "in": "path", 199 | "name": "id", 200 | "required": true, 201 | "type": "integer" 202 | }, 203 | { 204 | "in": "body", 205 | "name": "body", 206 | "schema": { 207 | "$ref": "#/definitions/Category" 208 | } 209 | } 210 | ], 211 | "responses": { 212 | "200": { 213 | "description": "OK", 214 | "schema": { 215 | "$ref": "#/definitions/Category" 216 | } 217 | } 218 | }, 219 | "tags": [ 220 | "Categories" 221 | ], 222 | "description": "Update an existing category", 223 | "summary": "Update category" 224 | } 225 | }, 226 | "/api/product": { 227 | "get": { 228 | "produces": [ 229 | "application/json" 230 | ], 231 | "parameters": [ 232 | { 233 | "default": "0", 234 | "format": "int32", 235 | "in": "query", 236 | "name": "item_per_page", 237 | "type": "integer" 238 | }, 239 | { 240 | "default": "", 241 | "in": "query", 242 | "name": "name", 243 | "type": "string" 244 | }, 245 | { 246 | "default": "name", 247 | "in": "query", 248 | "name": "order_by", 249 | "type": "string" 250 | }, 251 | { 252 | "default": "asc", 253 | "in": "query", 254 | "name": "order_type", 255 | "type": "string" 256 | }, 257 | { 258 | "default": "0", 259 | "format": "int32", 260 | "in": "query", 261 | "name": "page", 262 | "type": "integer" 263 | } 264 | ], 265 | "responses": { 266 | "200": { 267 | "description": "OK", 268 | "schema": { 269 | "items": { 270 | "$ref": "#/definitions/Product" 271 | }, 272 | "type": "array" 273 | } 274 | } 275 | }, 276 | "tags": [ 277 | "Products" 278 | ], 279 | "description": "Get product list with support for paging and ordering", 280 | "summary": "Get product list" 281 | }, 282 | "post": { 283 | "consumes": [ 284 | "application/x-www-form-urlencoded" 285 | ], 286 | "produces": [ 287 | "application/json" 288 | ], 289 | "parameters": [ 290 | { 291 | "in": "formData", 292 | "name": "name", 293 | "type": "string" 294 | }, 295 | { 296 | "in": "formData", 297 | "name": "description", 298 | "type": "string" 299 | }, 300 | { 301 | "format": "int32", 302 | "in": "formData", 303 | "name": "category_id", 304 | "type": "integer" 305 | }, 306 | { 307 | "format": "double", 308 | "in": "formData", 309 | "name": "price", 310 | "type": "number" 311 | } 312 | ], 313 | "responses": { 314 | "200": { 315 | "description": "OK", 316 | "schema": { 317 | "$ref": "#/definitions/ProductResult" 318 | } 319 | } 320 | }, 321 | "tags": [ 322 | "Products" 323 | ], 324 | "description": "Create a new product", 325 | "summary": "Create product" 326 | } 327 | }, 328 | "/api/product/count": { 329 | "get": { 330 | "parameters": [], 331 | "responses": { 332 | "200": { 333 | "description": "OK" 334 | } 335 | }, 336 | "tags": [ 337 | "Products" 338 | ], 339 | "description": "Get the total count of products available", 340 | "summary": "Get product count" 341 | } 342 | }, 343 | "/api/product/delete": { 344 | "post": { 345 | "consumes": [ 346 | "application/x-www-form-urlencoded" 347 | ], 348 | "parameters": [ 349 | { 350 | "in": "formData", 351 | "name": "del_ids[]" 352 | } 353 | ], 354 | "responses": { 355 | "200": { 356 | "description": "OK" 357 | } 358 | }, 359 | "tags": [ 360 | "Products" 361 | ], 362 | "description": "Delete a set of products as specified by their IDs", 363 | "summary": "Delete a set of products" 364 | } 365 | }, 366 | "/api/product/{id}": { 367 | "delete": { 368 | "parameters": [ 369 | { 370 | "format": "int32", 371 | "in": "path", 372 | "name": "id", 373 | "required": true, 374 | "type": "integer" 375 | } 376 | ], 377 | "responses": { 378 | "200": { 379 | "description": "OK" 380 | } 381 | }, 382 | "tags": [ 383 | "Products" 384 | ], 385 | "description": "Delete a single product by ID", 386 | "summary": "Delete product" 387 | }, 388 | "get": { 389 | "produces": [ 390 | "application/json" 391 | ], 392 | "parameters": [ 393 | { 394 | "format": "int32", 395 | "in": "path", 396 | "name": "id", 397 | "required": true, 398 | "type": "integer" 399 | } 400 | ], 401 | "responses": { 402 | "200": { 403 | "description": "OK", 404 | "schema": { 405 | "$ref": "#/definitions/Product" 406 | } 407 | } 408 | }, 409 | "tags": [ 410 | "Products" 411 | ], 412 | "description": "Get specific product by it's ID", 413 | "summary": "Get product by ID" 414 | }, 415 | "put": { 416 | "consumes": [ 417 | "application/x-www-form-urlencoded" 418 | ], 419 | "produces": [ 420 | "application/json" 421 | ], 422 | "parameters": [ 423 | { 424 | "format": "int32", 425 | "in": "path", 426 | "name": "id", 427 | "required": true, 428 | "type": "integer" 429 | }, 430 | { 431 | "format": "int32", 432 | "in": "formData", 433 | "name": "id", 434 | "type": "integer" 435 | }, 436 | { 437 | "in": "formData", 438 | "name": "name", 439 | "type": "string" 440 | }, 441 | { 442 | "in": "formData", 443 | "name": "description", 444 | "type": "string" 445 | }, 446 | { 447 | "format": "int32", 448 | "in": "formData", 449 | "name": "category_id", 450 | "type": "integer" 451 | }, 452 | { 453 | "format": "double", 454 | "in": "formData", 455 | "name": "price", 456 | "type": "number" 457 | } 458 | ], 459 | "responses": { 460 | "200": { 461 | "description": "OK", 462 | "schema": { 463 | "$ref": "#/definitions/ProductResult" 464 | } 465 | } 466 | }, 467 | "tags": [ 468 | "Products" 469 | ], 470 | "description": "Update an existing product", 471 | "summary": "Update product" 472 | } 473 | }, 474 | "/api/user": { 475 | "post": { 476 | "consumes": [ 477 | "application/json" 478 | ], 479 | "parameters": [ 480 | { 481 | "in": "body", 482 | "name": "body", 483 | "schema": { 484 | "$ref": "#/definitions/User" 485 | } 486 | } 487 | ], 488 | "responses": { 489 | "200": { 490 | "description": "OK" 491 | } 492 | }, 493 | "tags": [ 494 | "Users" 495 | ], 496 | "description": "Create a new user", 497 | "summary": "Create user" 498 | } 499 | }, 500 | "/api/user/{id}": { 501 | "delete": { 502 | "parameters": [ 503 | { 504 | "format": "int32", 505 | "in": "path", 506 | "name": "id", 507 | "required": true, 508 | "type": "integer" 509 | } 510 | ], 511 | "responses": { 512 | "200": { 513 | "description": "OK" 514 | } 515 | }, 516 | "tags": [ 517 | "Users" 518 | ], 519 | "description": "Delete a user", 520 | "summary": "Delete user" 521 | }, 522 | "get": { 523 | "produces": [ 524 | "application/json" 525 | ], 526 | "parameters": [ 527 | { 528 | "format": "int32", 529 | "in": "path", 530 | "name": "id", 531 | "required": true, 532 | "type": "integer" 533 | } 534 | ], 535 | "responses": { 536 | "200": { 537 | "description": "OK", 538 | "schema": { 539 | "$ref": "#/definitions/User" 540 | } 541 | } 542 | }, 543 | "tags": [ 544 | "Users" 545 | ], 546 | "description": "Get specific user by it's ID", 547 | "summary": "Get user by ID" 548 | }, 549 | "put": { 550 | "consumes": [ 551 | "application/json" 552 | ], 553 | "produces": [ 554 | "application/json" 555 | ], 556 | "parameters": [ 557 | { 558 | "format": "int32", 559 | "in": "path", 560 | "name": "id", 561 | "required": true, 562 | "type": "integer" 563 | }, 564 | { 565 | "in": "body", 566 | "name": "body", 567 | "schema": { 568 | "$ref": "#/definitions/User" 569 | } 570 | } 571 | ], 572 | "responses": { 573 | "200": { 574 | "description": "OK", 575 | "schema": { 576 | "$ref": "#/definitions/User" 577 | } 578 | } 579 | }, 580 | "tags": [ 581 | "Users" 582 | ], 583 | "description": "Update an existing user", 584 | "summary": "Update user" 585 | } 586 | } 587 | }, 588 | "definitions": { 589 | "AuthResource": { 590 | "type": "object" 591 | }, 592 | "Category": { 593 | "properties": { 594 | "created": { 595 | "$ref": "#/definitions/Date1" 596 | }, 597 | "description": { 598 | "type": "string" 599 | }, 600 | "id": { 601 | "format": "int32", 602 | "nullable": false, 603 | "type": "integer" 604 | }, 605 | "modified": { 606 | "$ref": "#/definitions/LocalDateTime" 607 | }, 608 | "name": { 609 | "nullable": false, 610 | "type": "string" 611 | } 612 | }, 613 | "required": [ 614 | "id", 615 | "name" 616 | ], 617 | "type": "object" 618 | }, 619 | "Date": { 620 | "format": "date", 621 | "type": "string" 622 | }, 623 | "Date1": { 624 | "format": "date", 625 | "type": "string" 626 | }, 627 | "EntityTag": { 628 | "properties": { 629 | "value": { 630 | "type": "string" 631 | }, 632 | "weak": { 633 | "type": "boolean" 634 | } 635 | }, 636 | "type": "object" 637 | }, 638 | "Family": { 639 | "enum": [ 640 | "CLIENT_ERROR", 641 | "INFORMATIONAL", 642 | "OTHER", 643 | "REDIRECTION", 644 | "SERVER_ERROR", 645 | "SUCCESSFUL" 646 | ], 647 | "type": "string" 648 | }, 649 | "Link": { 650 | "properties": { 651 | "params": { 652 | "$ref": "#/definitions/MapStringString" 653 | }, 654 | "rel": { 655 | "type": "string" 656 | }, 657 | "rels": { 658 | "$ref": "#/definitions/ListString" 659 | }, 660 | "title": { 661 | "type": "string" 662 | }, 663 | "type": { 664 | "type": "string" 665 | }, 666 | "uri": { 667 | "$ref": "#/definitions/URI" 668 | }, 669 | "uriBuilder": { 670 | "$ref": "#/definitions/UriBuilder" 671 | } 672 | }, 673 | "type": "object" 674 | }, 675 | "ListInteger": { 676 | "$ref": "#/definitions/ListInteger" 677 | }, 678 | "ListString": { 679 | "items": { 680 | "type": "string" 681 | }, 682 | "type": "array" 683 | }, 684 | "LocalDateTime": { 685 | "format": "date-time", 686 | "type": "string" 687 | }, 688 | "Locale": { 689 | "properties": { 690 | "country": { 691 | "type": "string" 692 | }, 693 | "displayCountry": { 694 | "type": "string" 695 | }, 696 | "displayLanguage": { 697 | "type": "string" 698 | }, 699 | "displayName": { 700 | "type": "string" 701 | }, 702 | "displayScript": { 703 | "type": "string" 704 | }, 705 | "displayVariant": { 706 | "type": "string" 707 | }, 708 | "extensionKeys": { 709 | "$ref": "#/definitions/SetCharacter" 710 | }, 711 | "iSO3Country": { 712 | "type": "string" 713 | }, 714 | "iSO3Language": { 715 | "type": "string" 716 | }, 717 | "language": { 718 | "type": "string" 719 | }, 720 | "script": { 721 | "type": "string" 722 | }, 723 | "unicodeLocaleAttributes": { 724 | "$ref": "#/definitions/SetString" 725 | }, 726 | "unicodeLocaleKeys": { 727 | "$ref": "#/definitions/SetString" 728 | }, 729 | "variant": { 730 | "type": "string" 731 | } 732 | }, 733 | "type": "object" 734 | }, 735 | "LoginResult": { 736 | "properties": { 737 | "message": { 738 | "type": "string" 739 | }, 740 | "this$0": { 741 | "$ref": "#/definitions/AuthResource" 742 | }, 743 | "user": { 744 | "$ref": "#/definitions/SecureUser" 745 | } 746 | }, 747 | "type": "object" 748 | }, 749 | "MapStringNewCookie": { 750 | "additionalProperties": { 751 | "$ref": "#/definitions/NewCookie" 752 | }, 753 | "type": "object" 754 | }, 755 | "MapStringString": { 756 | "additionalProperties": { 757 | "type": "string" 758 | }, 759 | "type": "object" 760 | }, 761 | "MediaType": { 762 | "properties": { 763 | "parameters": { 764 | "$ref": "#/definitions/MapStringString" 765 | }, 766 | "subtype": { 767 | "type": "string" 768 | }, 769 | "type": { 770 | "type": "string" 771 | }, 772 | "wildcardSubtype": { 773 | "type": "boolean" 774 | }, 775 | "wildcardType": { 776 | "type": "boolean" 777 | } 778 | }, 779 | "type": "object" 780 | }, 781 | "MultivaluedMapStringObject": { 782 | "additionalProperties": {}, 783 | "type": "object" 784 | }, 785 | "MultivaluedMapStringString": { 786 | "additionalProperties": { 787 | "type": "string" 788 | }, 789 | "type": "object" 790 | }, 791 | "NewCookie": { 792 | "properties": { 793 | "comment": { 794 | "type": "string" 795 | }, 796 | "domain": { 797 | "type": "string" 798 | }, 799 | "expiry": { 800 | "$ref": "#/definitions/Date" 801 | }, 802 | "httpOnly": { 803 | "type": "boolean" 804 | }, 805 | "maxAge": { 806 | "format": "int32", 807 | "type": "integer" 808 | }, 809 | "name": { 810 | "type": "string" 811 | }, 812 | "path": { 813 | "type": "string" 814 | }, 815 | "secure": { 816 | "type": "boolean" 817 | }, 818 | "value": { 819 | "type": "string" 820 | }, 821 | "version": { 822 | "format": "int32", 823 | "type": "integer" 824 | } 825 | }, 826 | "type": "object" 827 | }, 828 | "Product": { 829 | "properties": { 830 | "category": { 831 | "allOf": [ 832 | { 833 | "$ref": "#/definitions/Category" 834 | }, 835 | { 836 | "nullable": false 837 | } 838 | ] 839 | }, 840 | "created": { 841 | "$ref": "#/definitions/Date1" 842 | }, 843 | "description": { 844 | "type": "string" 845 | }, 846 | "id": { 847 | "format": "int32", 848 | "type": "integer" 849 | }, 850 | "modified": { 851 | "$ref": "#/definitions/LocalDateTime" 852 | }, 853 | "name": { 854 | "nullable": false, 855 | "type": "string" 856 | }, 857 | "price": { 858 | "format": "double", 859 | "nullable": false, 860 | "type": "number" 861 | } 862 | }, 863 | "required": [ 864 | "category", 865 | "name", 866 | "price" 867 | ], 868 | "type": "object" 869 | }, 870 | "ProductResource": { 871 | "properties": { 872 | "count": { 873 | "$ref": "#/definitions/Response" 874 | } 875 | }, 876 | "type": "object" 877 | }, 878 | "ProductResult": { 879 | "properties": { 880 | "message": { 881 | "type": "string" 882 | }, 883 | "product": { 884 | "$ref": "#/definitions/Product" 885 | }, 886 | "success": { 887 | "type": "boolean" 888 | }, 889 | "this$0": { 890 | "$ref": "#/definitions/ProductResource" 891 | } 892 | }, 893 | "type": "object" 894 | }, 895 | "Response": { 896 | "properties": { 897 | "allowedMethods": { 898 | "$ref": "#/definitions/SetString" 899 | }, 900 | "cookies": { 901 | "$ref": "#/definitions/MapStringNewCookie" 902 | }, 903 | "date": { 904 | "$ref": "#/definitions/Date" 905 | }, 906 | "entity": { 907 | "type": "object" 908 | }, 909 | "entityTag": { 910 | "$ref": "#/definitions/EntityTag" 911 | }, 912 | "headers": { 913 | "$ref": "#/definitions/MultivaluedMapStringObject" 914 | }, 915 | "language": { 916 | "$ref": "#/definitions/Locale" 917 | }, 918 | "lastModified": { 919 | "$ref": "#/definitions/Date" 920 | }, 921 | "length": { 922 | "format": "int32", 923 | "type": "integer" 924 | }, 925 | "links": { 926 | "$ref": "#/definitions/SetLink" 927 | }, 928 | "location": { 929 | "$ref": "#/definitions/URI" 930 | }, 931 | "mediaType": { 932 | "$ref": "#/definitions/MediaType" 933 | }, 934 | "metadata": { 935 | "$ref": "#/definitions/MultivaluedMapStringObject" 936 | }, 937 | "status": { 938 | "format": "int32", 939 | "type": "integer" 940 | }, 941 | "statusInfo": { 942 | "$ref": "#/definitions/StatusType" 943 | }, 944 | "stringHeaders": { 945 | "$ref": "#/definitions/MultivaluedMapStringString" 946 | } 947 | }, 948 | "type": "object" 949 | }, 950 | "SecureUser": { 951 | "properties": { 952 | "createdAt": { 953 | "$ref": "#/definitions/LocalDateTime" 954 | }, 955 | "email": { 956 | "type": "string" 957 | }, 958 | "this$0": { 959 | "$ref": "#/definitions/AuthResource" 960 | } 961 | }, 962 | "type": "object" 963 | }, 964 | "SetCharacter": { 965 | "items": { 966 | "format": "byte", 967 | "type": "string" 968 | }, 969 | "type": "array" 970 | }, 971 | "SetLink": { 972 | "items": { 973 | "$ref": "#/definitions/Link" 974 | }, 975 | "type": "array" 976 | }, 977 | "SetString": { 978 | "items": { 979 | "type": "string" 980 | }, 981 | "type": "array" 982 | }, 983 | "StatusType": { 984 | "properties": { 985 | "family": { 986 | "$ref": "#/definitions/Family" 987 | }, 988 | "reasonPhrase": { 989 | "type": "string" 990 | }, 991 | "statusCode": { 992 | "format": "int32", 993 | "type": "integer" 994 | } 995 | }, 996 | "type": "object" 997 | }, 998 | "URI": { 999 | "format": "uri", 1000 | "type": "string" 1001 | }, 1002 | "UriBuilder": { 1003 | "type": "object" 1004 | }, 1005 | "User": { 1006 | "properties": { 1007 | "createdAt": { 1008 | "$ref": "#/definitions/LocalDateTime" 1009 | }, 1010 | "email": { 1011 | "type": "string" 1012 | }, 1013 | "id": { 1014 | "format": "int32", 1015 | "type": "integer" 1016 | }, 1017 | "iterations": { 1018 | "format": "int32", 1019 | "type": "integer" 1020 | }, 1021 | "passwordHash": { 1022 | "type": "string" 1023 | }, 1024 | "salt": { 1025 | "type": "string" 1026 | } 1027 | }, 1028 | "type": "object" 1029 | } 1030 | }, 1031 | "tags": [ 1032 | { 1033 | "description": "An API to manage user authentication and authorization", 1034 | "name": "Authentication" 1035 | }, 1036 | { 1037 | "description": "An API to manipulate the categories in the catalog", 1038 | "name": "Categories" 1039 | }, 1040 | { 1041 | "description": "An API to manipulate the products in the catalog", 1042 | "name": "Products" 1043 | }, 1044 | { 1045 | "description": "An API to manipulate users of the catalog", 1046 | "name": "Users" 1047 | } 1048 | ], 1049 | "x-components": {} 1050 | } 1051 | -------------------------------------------------------------------------------- /environments/overlays/cicd/acs-external-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: roxsecrets 5 | namespace: product-catalog-cicd 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | kind: SecretStore 10 | name: product-catalog 11 | target: 12 | name: roxsecrets 13 | creationPolicy: Owner 14 | dataFrom: 15 | - extract: 16 | key: ACS_SCAN_IMAGE 17 | -------------------------------------------------------------------------------- /environments/overlays/cicd/docker-external-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: dest-docker-config 5 | namespace: product-catalog-cicd 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | name: product-catalog 10 | kind: SecretStore 11 | target: 12 | template: 13 | type: kubernetes.io/dockerconfigjson 14 | data: 15 | .dockerconfigjson: "{{ .docker_secret | toString }}" 16 | name: dest-docker-config 17 | creationPolicy: Owner 18 | data: 19 | - secretKey: docker_secret 20 | remoteRef: 21 | key: DOCKER_CONFIG_JSON 22 | -------------------------------------------------------------------------------- /environments/overlays/cicd/github-external-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: github 5 | namespace: product-catalog-cicd 6 | spec: 7 | refreshInterval: 1h 8 | secretStoreRef: 9 | kind: SecretStore 10 | name: product-catalog 11 | target: 12 | name: github 13 | template: 14 | metadata: 15 | annotations: 16 | tekton.dev/git-0: https://github.com 17 | type: kubernetes.io/basic-auth 18 | creationPolicy: Owner 19 | dataFrom: 20 | - extract: 21 | key: GITHUB 22 | -------------------------------------------------------------------------------- /environments/overlays/cicd/gitops-network-policy.yaml: -------------------------------------------------------------------------------- 1 | kind: NetworkPolicy 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: allow-namespace-gitops 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | eventlistener: server-post-prod 9 | ingress: 10 | - from: 11 | - namespaceSelector: 12 | matchLabels: 13 | kubernetes.io/metadata.name: product-catalog-gitops 14 | ports: 15 | - protocol: TCP 16 | port: 8080 17 | policyTypes: 18 | - Ingress -------------------------------------------------------------------------------- /environments/overlays/cicd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-cicd 5 | 6 | resources: 7 | - workspace-template-cm.yaml 8 | - gitops-network-policy.yaml 9 | - docker-external-secret.yaml 10 | - github-external-secret.yaml 11 | - slack-deployments-webhook-external-secret.yaml 12 | - ../../../components/apps/slack-message-handler/base 13 | - ../../../components/tekton/tasks/base 14 | - ../../../components/tekton/pipelines/client/base 15 | - ../../../components/tekton/pipelines/server/base 16 | - ../../../components/tekton/pipelines/push-prod-pr/base 17 | - ../../../components/tekton/triggers/base 18 | - setup-local-credentials-job.yaml 19 | - acs-external-secret.yaml 20 | - prod-network-policy.yaml 21 | -------------------------------------------------------------------------------- /environments/overlays/cicd/prod-network-policy.yaml: -------------------------------------------------------------------------------- 1 | kind: NetworkPolicy 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: allow-namespace-prod 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | eventlistener: server-post-prod 9 | ingress: 10 | - from: 11 | - namespaceSelector: 12 | matchLabels: 13 | kubernetes.io/metadata.name: product-catalog-prod 14 | ports: 15 | - protocol: TCP 16 | port: 8080 17 | policyTypes: 18 | - Ingress -------------------------------------------------------------------------------- /environments/overlays/cicd/setup-local-credentials-job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: setup-local-credentials 5 | --- 6 | apiVersion: rbac.authorization.k8s.io/v1 7 | kind: RoleBinding 8 | metadata: 9 | name: setup-local-credentials 10 | roleRef: 11 | apiGroup: rbac.authorization.k8s.io 12 | kind: ClusterRole 13 | name: admin 14 | subjects: 15 | - kind: ServiceAccount 16 | name: setup-local-credentials 17 | namespace: product-catalog-cicd 18 | --- 19 | apiVersion: batch/v1 20 | kind: Job 21 | metadata: 22 | name: setup-local-credentials 23 | annotations: 24 | argocd.argoproj.io/hook: PostSync 25 | argocd.argoproj.io/hook-delete-policy: HookSucceeded 26 | spec: 27 | template: 28 | spec: 29 | containers: 30 | - image: registry.redhat.io/openshift4/ose-cli:v4.9 31 | command: 32 | - /bin/bash 33 | - -c 34 | - | 35 | echo "Linking github secret with pipeline service account" 36 | oc secrets link pipeline github 37 | 38 | echo "Linking dest-docker-config secret with builder service account" 39 | oc secrets link builder dest-docker-config 40 | 41 | echo "Linking dest-docker-config secret with pipeline service account" 42 | oc secrets link pipeline dest-docker-config 43 | # Chains requires imagePullSecrets set 44 | oc patch serviceaccount pipeline -p "{\"imagePullSecrets\": [{\"name\": \"dest-docker-config\"}]}" 45 | 46 | imagePullPolicy: Always 47 | name: setup-local-credentials 48 | serviceAccount: setup-local-credentials 49 | serviceAccountName: setup-local-credentials 50 | dnsPolicy: ClusterFirst 51 | restartPolicy: OnFailure 52 | terminationGracePeriodSeconds: 30 -------------------------------------------------------------------------------- /environments/overlays/cicd/slack-deployments-webhook-external-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: external-secrets.io/v1beta1 2 | kind: ExternalSecret 3 | metadata: 4 | name: slack-deployments-webhook 5 | namespace: product-catalog-cicd 6 | spec: 7 | secretStoreRef: 8 | kind: SecretStore 9 | name: product-catalog 10 | target: 11 | name: slack-deployments-webhook 12 | data: 13 | - secretKey: url 14 | remoteRef: 15 | key: SLACK_DEPLOYMENTS 16 | -------------------------------------------------------------------------------- /environments/overlays/cicd/workspace-template-cm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: workspace-template 5 | data: 6 | workspace-template.yaml: | 7 | spec: 8 | accessModes: 9 | - ReadWriteOnce 10 | resources: 11 | requests: 12 | storage: 5Gi -------------------------------------------------------------------------------- /environments/overlays/dev/cicd-networkpolicy.yaml: -------------------------------------------------------------------------------- 1 | kind: NetworkPolicy 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: allow-namespace-cicd 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | name: server 9 | ingress: 10 | - from: 11 | - namespaceSelector: 12 | matchLabels: 13 | kubernetes.io/metadata.name: product-catalog-cicd 14 | ports: 15 | - protocol: TCP 16 | port: 8080 17 | policyTypes: 18 | - Ingress -------------------------------------------------------------------------------- /environments/overlays/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-dev 5 | 6 | bases: 7 | - cicd-networkpolicy.yaml 8 | - ../../../components/apps/database/base 9 | - ../../../components/apps/client/base 10 | - ../../../components/apps/server/base 11 | 12 | images: 13 | - name: quay.io/gnunn/client 14 | newTag: dev 15 | - name: quay.io/gnunn/server 16 | newTag: dev -------------------------------------------------------------------------------- /environments/overlays/monitor/README.md: -------------------------------------------------------------------------------- 1 | #### Overview 2 | 3 | Deploys just grafana and relies on [app monitoring](https://docs.openshift.com/container-platform/4.6/monitoring/enabling-monitoring-for-user-defined-projects.html) being enabled in the OpenShift cluster. -------------------------------------------------------------------------------- /environments/overlays/monitor/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-monitor 5 | 6 | commonAnnotations: 7 | argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true 8 | 9 | bases: 10 | - ../../../components/apps/monitor/base -------------------------------------------------------------------------------- /environments/overlays/prod/cicd-networkpolicy.yaml: -------------------------------------------------------------------------------- 1 | kind: NetworkPolicy 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: allow-namespace-cicd 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | name: server 9 | ingress: 10 | - from: 11 | - namespaceSelector: 12 | matchLabels: 13 | kubernetes.io/metadata.name: product-catalog-cicd 14 | ports: 15 | - protocol: TCP 16 | port: 8080 17 | policyTypes: 18 | - Ingress -------------------------------------------------------------------------------- /environments/overlays/prod/cicd-pipeline-view.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: cicd-view-prod 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: view 9 | subjects: 10 | - kind: ServiceAccount 11 | name: pipeline 12 | namespace: product-catalog-cicd -------------------------------------------------------------------------------- /environments/overlays/prod/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-prod 5 | 6 | resources: 7 | - cicd-networkpolicy.yaml 8 | - cicd-pipeline-view.yaml 9 | - ../../../components/apps/database/base 10 | - ../../../components/apps/server/base 11 | - ../../../components/apps/client/base 12 | - post-sync-pipeline-job.yaml 13 | -------------------------------------------------------------------------------- /environments/overlays/prod/post-sync-pipeline-job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: post-sync-pipeline 5 | generateName: post-sync-pipeline- 6 | annotations: 7 | argocd.argoproj.io/hook: PostSync 8 | argocd.argoproj.io/hook-delete-policy: BeforeHookCreation 9 | spec: 10 | template: 11 | spec: 12 | containers: 13 | - image: registry.access.redhat.com/ubi8 14 | command: 15 | - "curl" 16 | - "-X" 17 | - "POST" 18 | - "-H" 19 | - "Content-Type: application/json" 20 | - "--data" 21 | - "{}" 22 | - "--connect-timeout" 23 | - "60" 24 | - "http://el-server-post-prod.product-catalog-cicd:8080" 25 | imagePullPolicy: Always 26 | name: post-sync-pipeline 27 | dnsPolicy: ClusterFirst 28 | restartPolicy: OnFailure 29 | terminationGracePeriodSeconds: 30 -------------------------------------------------------------------------------- /environments/overlays/test/cicd-networkpolicy.yaml: -------------------------------------------------------------------------------- 1 | kind: NetworkPolicy 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: allow-namespace-cicd 5 | spec: 6 | podSelector: 7 | matchLabels: 8 | name: server 9 | ingress: 10 | - from: 11 | - namespaceSelector: 12 | matchLabels: 13 | kubernetes.io/metadata.name: product-catalog-cicd 14 | ports: 15 | - protocol: TCP 16 | port: 8080 17 | policyTypes: 18 | - Ingress -------------------------------------------------------------------------------- /environments/overlays/test/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: product-catalog-test 5 | 6 | bases: 7 | - cicd-networkpolicy.yaml 8 | - ../../../components/apps/database/base 9 | - ../../../components/apps/client/base 10 | - ../../../components/apps/server/base 11 | 12 | images: 13 | - name: quay.io/gnunn/client 14 | newTag: test 15 | - name: quay.io/gnunn/server 16 | newTag: test -------------------------------------------------------------------------------- /pipelinerun.sh: -------------------------------------------------------------------------------- 1 | if [ $# -lt 1 ]; then 2 | echo "No cluster name specified, please specify a cluster " 3 | exit 1 4 | else 5 | CLUSTER=$1 6 | echo "Running pipelines for cluster: ${CLUSTER}" 7 | fi 8 | kustomize build clusters/${CLUSTER}/overlays/pipelinerun/apps | oc apply -f - -------------------------------------------------------------------------------- /scripts/acm/1-apply-channel.sh: -------------------------------------------------------------------------------- 1 | oc apply -k ../../manifests/tools/acm/channel/base/ -------------------------------------------------------------------------------- /scripts/acm/2-apply-dev.sh: -------------------------------------------------------------------------------- 1 | oc apply -k ../../manifests/tools/acm/app/overlays/dev/ -------------------------------------------------------------------------------- /scripts/acm/3-apply-test.sh: -------------------------------------------------------------------------------- 1 | oc apply -k ../../manifests/tools/acm/app/overlays/test -------------------------------------------------------------------------------- /scripts/acm/4-apply-prod.sh: -------------------------------------------------------------------------------- 1 | oc apply -k ../../manifests/tools/acm/app/overlays/prod -------------------------------------------------------------------------------- /scripts/acm/5-spply-cicd.sh: -------------------------------------------------------------------------------- 1 | oc apply -k ../../manifests/tools/acm/cicd/overlays/default -------------------------------------------------------------------------------- /scripts/apply-home.sh: -------------------------------------------------------------------------------- 1 | oc apply -k ../clusters/overlays/home/tools/argocd/manager -------------------------------------------------------------------------------- /scripts/apply-ocplab.sh: -------------------------------------------------------------------------------- 1 | oc apply -k ../clusters/overlays/ocplab/tools/argocd/manager -------------------------------------------------------------------------------- /scripts/apply-pipelinerun.sh: -------------------------------------------------------------------------------- 1 | oc apply -k ../manifests/tekton/pipelineruns/client/base -n product-catalog-cicd 2 | oc apply -k ../manifests/tekton/pipelineruns/server/base -n product-catalog-cicd -------------------------------------------------------------------------------- /scripts/apply.sh: -------------------------------------------------------------------------------- 1 | oc apply -k ../clusters/overlays/ocplab/tools/argocd -------------------------------------------------------------------------------- /scripts/siege.sh: -------------------------------------------------------------------------------- 1 | COUNT=50 2 | 3 | SERVER_HOST=server-product-catalog-prod.apps.home.ocplab.com 4 | 5 | siege -r $COUNT -c 50 -v http://${SERVER_HOST}/api/product & 6 | siege -r $COUNT -c 50 -v http://${SERVER_HOST}/api/product/count & 7 | siege -r $COUNT -c 50 -v http://${SERVER_HOST}/api/category & 8 | --------------------------------------------------------------------------------