diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d4b2001..cb4f3e154 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Update jdk and scala quickstarters and agents ([#1032](https://github.com/opendevstack/ods-quickstarters/issues/1032)) - Gitleaks docs fix and update ([#1028](https://github.com/opendevstack/ods-quickstarters/issues/1028)) - Enable OpenSSL vendored compilation for Rust Jenkins Agent ([#1026](https://github.com/opendevstack/ods-quickstarters/pull/1026)) +- Add Helm Chart to Docker Plain Quickstarter ([#1035](https://github.com/opendevstack/ods-quickstarters/pull/1035)) - Update Streamlit Quickstarter ([#1030](https://github.com/opendevstack/ods-quickstarters/issues/1030)) - Update Golang agent ([#1031](https://github.com/opendevstack/ods-quickstarters/issues/1031)) - Update gateway/nginx Quickstarter ([#1048](https://github.com/opendevstack/ods-quickstarters/pull/1048)) diff --git a/docker-plain/Chart.yaml.template b/docker-plain/Chart.yaml.template new file mode 100644 index 000000000..2c1f7a8dd --- /dev/null +++ b/docker-plain/Chart.yaml.template @@ -0,0 +1,24 @@ +apiVersion: v2 +name: @component_id@ +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 1.0.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.0.0" diff --git a/docker-plain/Jenkinsfile b/docker-plain/Jenkinsfile index 7c859cc0f..c1eee50ad 100644 --- a/docker-plain/Jenkinsfile +++ b/docker-plain/Jenkinsfile @@ -20,12 +20,17 @@ odsQuickstarterPipeline( odsQuickstarterStageCopyFiles(context) - odsQuickstarterStageCreateOpenShiftResources( - context, - [directory: 'common/ocp-config/component-environment'] - ) - odsQuickstarterStageRenderJenkinsfile(context) odsQuickstarterStageRenderSonarProperties(context) + + renderHelmChart(context) +} + +def renderHelmChart(def context) { + def relativeSourceFilePath = "Chart.yaml.template" + def relativeDestinationFilePath = "chart/Chart.yaml" + def absoluteSourceFilePath = "${context.sourceDir}/${relativeSourceFilePath}" + def absoluteDestinationFilePath = "${context.targetDir}/${relativeDestinationFilePath}" + sh(script: "sed 's|@component_id@|${context.componentId}|g' ${absoluteSourceFilePath} > ${absoluteDestinationFilePath}", label: "Render Helm Chart.yaml file") } diff --git a/docker-plain/Jenkinsfile.template b/docker-plain/Jenkinsfile.template index d885ca418..e537a211a 100644 --- a/docker-plain/Jenkinsfile.template +++ b/docker-plain/Jenkinsfile.template @@ -20,7 +20,10 @@ odsComponentPipeline( */ odsComponentStageBuildOpenShiftImage(context) } - odsComponentStageRolloutOpenShiftDeployment(context) + odsComponentStageRolloutOpenShiftDeployment(context, [ + 'selector': "app.kubernetes.io/name=${context.componentId}", + 'helmEnvBasedValuesFiles': ["values.env.yaml"], + ]) } def stageBuild(def context) { diff --git a/docker-plain/files/README.md b/docker-plain/files/README.md index feb272fe8..cd124f08c 100644 --- a/docker-plain/files/README.md +++ b/docker-plain/files/README.md @@ -1,8 +1,66 @@ # Plain Docker image (docker-plain) +## Purpose + +This Quickstarter serves as a minimal starting point for building your own components that don't fit any of the other Quickstarters. +For demonstration purposes, a nginx webserver provides a simple 'Hello World' message. + +## Folder structure and important files + +- docker: All files inside this folder are available for use in building the docker container + - [docker/Dockerfile](docker/Dockerfile): Defines the container to be built. +- chart: The Helm chart used for deploying the component. + - [chart/Chart.yaml](chart/Chart.yaml): Metadata for your Helm chart. + - [chart/values.yaml](chart/values.yaml): Default values used when templating the Helm chart. + - [chart/values.dev.yaml](chart/values.dev.yaml): Values used for deployment in the 'dev' environment. Values specified in this file are overriding default values from [chart/values.yaml](chart/values.yaml). + - chart/templates: + - [chart/templates/deployment.yaml](chart/templates/deployment.yaml): Template for the [deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) resource. This is where you add additional configuration like environment variables. + - [chart/templates/service.yaml](chart/templates/service.yaml): Template for the [service](https://kubernetes.io/docs/concepts/services-networking/service/) resource. + - [chart/templates/ingress.yaml](chart/templates/ingress.yaml): Template for the [ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) resource. + +## Testing locally + +If you want to run this component locally for testing, this might get you started. +It also mimicks what's happening in the [Jenkinsfile](Jenkinsfile). + +### Building the container + +Using a local container runtime you can build the container and tag it with the current git revision: + +```bash +docker build -t testing/my-component:$(git rev-parse --short=8 HEAD) docker/ +``` + +### Helm chart linting + +This quickstarter comes with a fine-tunned [values.schema.json](chart/values.schema.json) Helm chart linting file. +Validate your chart template by running, under the `chart` folder, the following command: + +```bash +helm lint +``` + +### Helm chart template processing test + +One can test the chart template processing; run, under the `chart` folder, the following command: + +```bash +helm --debug template . --set image.path=testing --set image.name=my-component --set image.tag=$(git rev-parse --short=8 HEAD) +``` + +### Deploying the helm chart using a local k8s + +Using a local kubernetes cluster (i.e.: [kind](https://kind.sigs.k8s.io/)) you can deploy the component: + +```bash +kubectl create ns docker-plain +kind load docker-image testing/my-component:$(git rev-parse --short=8 HEAD) +helm upgrade --install --wait --atomic --namespace docker-plain --set image.path=testing --set image.name=my-component --set image.tag=$(git rev-parse --short=8 HEAD) docker-plain chart +``` + ## How to create a custom jenkins-agent out of this docker-plain component - Remove `odsComponentStageRolloutOpenShiftDeployment(context)` from your `Jenkinsfile`. We only want to build a docker image, not run it outside the pipeline. - In your `Dockerfile`, replace `FROM alpine:latest` with the ods-jenkins-agent-base image that is available in the OpenDevStack namespace of your cluster, e.g. `FROM docker-registry.default.svc:5000/ods/jenkins-agent-base:latest`. - Add everything you need in the jenkins-agent to your `Dockerfile`, for examples see the existing agents at [github](https://github.com/opendevstack/ods-quickstarters/tree/master/common/jenkins-agents). - Commit and push your code to git, this will trigger the pipeline and result in a docker image of your custom jenkins-agent in your cd-namespace. -- Now you can use your custom jenkins-agent by changing the imageStreamTag to `imageStreamTag: '/:latest'` in the `Jenkinsfile` of the actual application you want to build with your custom new jenkins-agent. \ No newline at end of file +- Now you can use your custom jenkins-agent by changing the imageStreamTag to `imageStreamTag: '/:latest'` in the `Jenkinsfile` of the actual application you want to build with your custom new jenkins-agent. diff --git a/docker-plain/files/chart/.helmignore b/docker-plain/files/chart/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/docker-plain/files/chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/docker-plain/files/chart/Chart.yaml b/docker-plain/files/chart/Chart.yaml new file mode 100644 index 000000000..64267e8c4 --- /dev/null +++ b/docker-plain/files/chart/Chart.yaml @@ -0,0 +1,26 @@ +# IMPORTANT: Content will be recreated from the Chart.yaml.template file by the Jenkins shared library provision job +# NOTE: The content is provided for testing purposes +apiVersion: v2 +name: Your helm chart +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 1.0.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.0.0" diff --git a/docker-plain/files/chart/DEVNOTES.md b/docker-plain/files/chart/DEVNOTES.md new file mode 100644 index 000000000..50bf60bfb --- /dev/null +++ b/docker-plain/files/chart/DEVNOTES.md @@ -0,0 +1,41 @@ +# TODO +Can we adapt NOTES.txt to display some deployed resources and other important information for release documentation? Do we need it? + +- helm linting preps, hence to add values schema validation in the chart together with the values.yaml + - add a test for helm lint + - future add as a Jenkins shared lib stage + - now either we do it as an added manually in the jenkinsfiles or documented and test locally + - make use of it on the github actions when PRs on quickstarters (reuse max as possible) +- we will move to values.yaml + - [X] the probes + - [x] the affinity (missing labels now and some additions required) + - [x] the route (with timeout values and example for ACME usage) + - [x] rolling update strategy? +- [X] remove provisioning resources creation -> get rid odsQuickstarterStageCreateOpenShiftResources +- jenkinsfile with values.env.yaml ready, so we will provide all the env values too: Different replicaCount + ingress hosts +- [X] update test-conection.yaml with better image (to not suffer dockerhub rate limiting) +- [X] golden tests do not check anymore imagetags nor deploymentconfigs +- [X] Test ingress on Openshift without hostname -> is there a generated one? NO -> host value is required but tls can be left empty now with default OpenShift TLS (See ingress.yaml and values.yaml). host value is required as ingress approach is a rules based system. +- [X] Make ingress more standard helm (e.g. list of hosts) -> Sebastian +- start defining howtos/FAQS we detect on the way (goal to keep simple the chart but to show how to improve it and have good practises) bitnami examples (more elaborated affinity, ...) + + +- example of chart dependency +- example of configmap and secret +- example of secret resource management in code +- add support for extra secret operator + +Later +- with the common folder with tpl files we provide a more clean and fitting approach for us +- create a new pipeline step provisioning that copies over .tpl files required from common +- creation of template files folder in common, so we try to centralise the chart creation and maintenance from one place (as it is done already with openshift templates/tailor) + +Decisions: +- To stay close to default helm templates: Remove the Values.componentId and use chart.fullname instead -> Otherwise breaks DEV + PREVIEW. Chart.Name should be the source for componentId. If we want to automate -> template the Chart.yaml on provisioning <- DONE + +Shared library ToDo's: +- Match required labels (app) +- Review helm install command +- Release manager image checks +- Agree if helm install requires overwritting values on image (see helm notes and deployment, and values.yaml) +- provide docs on how to test chart updates, by running `helm --debug template . ` under the chart folder to be tested diff --git a/docker-plain/files/chart/templates/NOTES.txt b/docker-plain/files/chart/templates/NOTES.txt new file mode 100644 index 000000000..1708ecef9 --- /dev/null +++ b/docker-plain/files/chart/templates/NOTES.txt @@ -0,0 +1,10 @@ +Component '{{ include "chart.fullname" . }}' on version '{{ .Values.imageTag }}' released with Helm! +{{- if .Values.ingress.enabled }} +The component is exposed via the following routes: +{{- $appUrl := .Values.appUrl -}} +{{- range .Values.ingress.hosts }} +{{ printf "https://%s" .host }} +{{- end }} +{{- else }} +The component is not exposed. +{{- end }} diff --git a/docker-plain/files/chart/templates/_affinity.tpl b/docker-plain/files/chart/templates/_affinity.tpl new file mode 100644 index 000000000..cc9c3519c --- /dev/null +++ b/docker-plain/files/chart/templates/_affinity.tpl @@ -0,0 +1,51 @@ +{{/* +Part of the ODS helm tpl library + +Version: 1.0 +*/}} + + +{{/* +Pod affinity/anti-affinity (soft) + +Usage: Include where needed, e.g. +```` +apiVersion: apps/v1 +kind: Deployment +spec: + template: + spec: + affinity: + podAntiAffinity: {{- include "common.affinities.pods.soft" . | nindent 10}} +```` +*/}} +{{- define "common.affinities.pods.soft" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - weight: 1 + podAffinityTerm: + labelSelector: + matchLabels: {{- include "common.matchLabels" . | nindent 10 }} + topologyKey: "kubernetes.io/hostname" +{{- end -}} + +{{/* +Pod affinity/anti-affinity (hard) + +Usage: Include where needed, e.g. +```` +apiVersion: apps/v1 +kind: Deployment +spec: + template: + spec: + affinity: + podAntiAffinity: {{- include "common.affinities.pods.hard" . | nindent 10}} +```` +*/}} +{{- define "common.affinities.pods.hard" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- include "common.matchLabels" . | nindent 10 }} + topologyKey: "kubernetes.io/hostname" +{{- end -}} diff --git a/docker-plain/files/chart/templates/_helpers.tpl b/docker-plain/files/chart/templates/_helpers.tpl new file mode 100644 index 000000000..7ba5edc27 --- /dev/null +++ b/docker-plain/files/chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "chart.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "chart.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "chart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "chart.labels" -}} +helm.sh/chart: {{ include "chart.chart" . }} +{{ include "chart.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "chart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "chart.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/docker-plain/files/chart/templates/_image.tpl b/docker-plain/files/chart/templates/_image.tpl new file mode 100644 index 000000000..1a527617b --- /dev/null +++ b/docker-plain/files/chart/templates/_image.tpl @@ -0,0 +1,19 @@ + +{{/* +Part of the ODS helm tpl library + +Version: 1.0 +*/}} + +{{/* +Create an image name from the registry, image path, name and tag. +.Values.registry, .Values.imageNamespace, .Values.componentId and .Values.imageTag are injected by the ODS pipeline on deployment. +If not set, values from .Values.image.registry, .Values.image.path, .Values.image.name and .Values.image.tag are used. +*/}} +{{- define "image.fullname" -}} +{{- if (or .Values.registry .Values.image.registry) }} +{{- printf "%s/%s/%s:%s" (or .Values.registry .Values.image.registry) (or .Values.imageNamespace .Values.image.path) (or .Values.componentId .Values.image.name) (or .Values.imageTag .Values.image.tag ) -}} +{{- else }} +{{- printf "%s/%s:%s" (or .Values.imageNamespace .Values.image.path) (or .Values.componentId .Values.image.name) (or .Values.imageTag .Values.image.tag ) -}} +{{- end }} +{{- end }} diff --git a/docker-plain/files/chart/templates/_labels.tpl b/docker-plain/files/chart/templates/_labels.tpl new file mode 100644 index 000000000..6a222c379 --- /dev/null +++ b/docker-plain/files/chart/templates/_labels.tpl @@ -0,0 +1,11 @@ +{{/* +Part of the ODS helm tpl library + +Version: 1.0 +*/}} + + +{{- define "common.matchLabels" -}} +app.kubernetes.io/name: {{ include "chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/docker-plain/files/chart/templates/deployment.yaml b/docker-plain/files/chart/templates/deployment.yaml new file mode 100644 index 000000000..03ddf70e8 --- /dev/null +++ b/docker-plain/files/chart/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "chart.fullname" . }} + labels: + {{- include "chart.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + strategy: + {{- toYaml .Values.deploymentStrategy | nindent 4 }} + selector: + matchLabels: + {{- include "chart.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "chart.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "chart.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + # Priority is on Values from CICD jenkins injected Helm values, if not then use values from values.yaml + image: {{ include "image.fullname" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.probes.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.probes.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + + affinity: + {{- with .Values.affinity }} + {{- toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if eq .Values.podAntiAffinity "soft" }} + podAntiAffinity: {{- include "common.affinities.pods.soft" . | nindent 10}} + {{- end }} + {{- if eq .Values.podAntiAffinity "hard" }} + podAntiAffinity: {{- include "common.affinities.pods.hard" . | nindent 10}} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/docker-plain/files/chart/templates/hpa.yaml b/docker-plain/files/chart/templates/hpa.yaml new file mode 100644 index 000000000..58170e998 --- /dev/null +++ b/docker-plain/files/chart/templates/hpa.yaml @@ -0,0 +1,23 @@ +{{- if .Values.autoscaling.enabled -}} +{{- $fullName := include "chart.fullname" . -}} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $fullName }} + labels: + {{- include "chart.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $fullName }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + type: Utilization +{{- end }} diff --git a/docker-plain/files/chart/templates/ingress.yaml b/docker-plain/files/chart/templates/ingress.yaml new file mode 100644 index 000000000..a2c5cb5c4 --- /dev/null +++ b/docker-plain/files/chart/templates/ingress.yaml @@ -0,0 +1,51 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "chart.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- if .Values.ingress.router }} + router: {{ .Values.ingress.router }} + {{- end }} + {{- include "chart.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- else }} + tls: + - {} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if .pathType }} + pathType: {{ .pathType }} + {{- end }} + backend: + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/docker-plain/files/chart/templates/service.yaml b/docker-plain/files/chart/templates/service.yaml new file mode 100644 index 000000000..4583f232e --- /dev/null +++ b/docker-plain/files/chart/templates/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.service.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "chart.fullname" . }} + labels: + {{- include "chart.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "chart.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/docker-plain/files/chart/templates/serviceaccount.yaml b/docker-plain/files/chart/templates/serviceaccount.yaml new file mode 100644 index 000000000..1df935010 --- /dev/null +++ b/docker-plain/files/chart/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "chart.serviceAccountName" . }} + labels: + {{- include "chart.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/docker-plain/files/chart/templates/tests/test-connection.yaml b/docker-plain/files/chart/templates/tests/test-connection.yaml new file mode 100644 index 000000000..2ad42849f --- /dev/null +++ b/docker-plain/files/chart/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "chart.fullname" . }}-test-connection" + labels: + {{- include "chart.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: public.ecr.aws/docker/library/busybox + command: ['wget'] + args: ['{{ include "chart.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/docker-plain/files/chart/values.dev.yaml b/docker-plain/files/chart/values.dev.yaml new file mode 100644 index 000000000..99520c495 --- /dev/null +++ b/docker-plain/files/chart/values.dev.yaml @@ -0,0 +1 @@ +# This file is used to override the default values in the chart/values.yaml file for deployment in 'dev' environment \ No newline at end of file diff --git a/docker-plain/files/chart/values.prod.yaml b/docker-plain/files/chart/values.prod.yaml new file mode 100644 index 000000000..3fd11896a --- /dev/null +++ b/docker-plain/files/chart/values.prod.yaml @@ -0,0 +1,3 @@ +# This file is used to override the default values in the chart/values.yaml file for deployment in 'prod' environment + +replicaCount: 2 \ No newline at end of file diff --git a/docker-plain/files/chart/values.schema.json b/docker-plain/files/chart/values.schema.json new file mode 100644 index 000000000..051b56aa1 --- /dev/null +++ b/docker-plain/files/chart/values.schema.json @@ -0,0 +1,552 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "additionalProperties": true, + "properties": { + "replicaCount": { + "description": "Number of replicas to deploy", + "type": "integer", + "minimum": 1, + "default": 1 + }, + "imagePullSecrets": { + "description": "List of image pull secrets", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "nameOverride": { + "description": "Override the name of the chart", + "type": "string", + "default": "" + }, + "fullnameOverride": { + "description": "Override the full name of the chart", + "type": "string", + "default": "" + }, + "image": { + "description": "Container image to deploy", + "type": "object", + "additionalProperties": false, + "properties": { + "registry": { + "description": "Image registry", + "type": "string", + "default": "public.ecr.aws" + }, + "path": { + "description": "Image path", + "type": "string", + "default": "nginx" + }, + "name": { + "description": "Image name", + "type": "string", + "default": "nginx-unprivileged" + }, + "tag": { + "description": "Image tag", + "type": "string", + "default": "alpine-slim" + }, + "pullPolicy": { + "description": "Image pull policy", + "type": "string", + "default": "IfNotPresent" + } + } + }, + "ingress": { + "description": "Ingress configuration for the Helm chart", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "description": "Enable ingress", + "type": "boolean", + "default": false + }, + "className": { + "description": "Ingress class name", + "type": "string", + "default": "openshift-default" + }, + "annotations": { + "description": "Annotations for the ingress", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "hosts": { + "description": "List of ingress hosts", + "type": "array", + "items": { + "type": "object", + "properties": { + "host": { + "description": "Hostname", + "type": "string" + }, + "paths": { + "description": "Paths for the host", + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "description": "Path", + "type": "string" + }, + "pathType": { + "description": "Path type", + "type": "string", + "enum": ["ImplementationSpecific", "Exact", "Prefix"] + } + } + } + } + } + } + }, + "tls": { + "description": "TLS configuration", + "type": "array", + "items": { + "type": "object", + "properties": { + "hosts": { + "description": "List of TLS hosts", + "type": "array", + "items": { + "type": "string" + } + }, + "secretName": { + "description": "Secret name for TLS", + "type": "string" + } + } + } + } + } + }, + "service": { + "description": "Service configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "description": "Enable service", + "type": "boolean", + "default": true + }, + "port": { + "description": "Service port", + "type": "integer", + "default": 8080 + }, + "type": { + "description": "Service type", + "type": "string", + "enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"], + "default": "ClusterIP" + } + } + }, + "deploymentStrategy": { + "description": "Deployment strategy configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "Deployment strategy type", + "type": "string", + "default": "RollingUpdate" + }, + "rollingUpdate": { + "description": "Rolling update configuration", + "type": "object", + "properties": { + "maxUnavailable": { + "description": "Maximum unavailable pods during update", + "type": "string", + "default": "0%" + }, + "maxSurge": { + "description": "Maximum surge pods during update", + "type": "string", + "default": "50%" + } + } + } + } + }, + "probes": { + "description": "Probes configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "livenessProbe": { + "description": "Liveness probe configuration", + "type": "object", + "properties": { + "exec": { + "description": "Exec probe configuration", + "type": "object", + "properties": { + "command": { + "description": "Command to execute", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "tcpSocket": { + "description": "TCP socket probe configuration", + "type": "object", + "properties": { + "port": { + "description": "Port to probe", + "type": "integer" + } + } + }, + "failureThreshold": { + "description": "Failure threshold", + "type": "integer", + "default": 3 + }, + "httpGet": { + "description": "HTTP GET configuration for liveness probe", + "type": "object", + "properties": { + "path": { + "description": "Path to probe", + "type": "string", + "default": "/" + }, + "port": { + "description": "Port to probe", + "type": "integer", + "default": 8080 + }, + "scheme": { + "description": "Scheme to use", + "type": "string", + "default": "HTTP" + } + } + }, + "initialDelaySeconds": { + "description": "Initial delay in seconds", + "type": "integer", + "default": 5 + }, + "periodSeconds": { + "description": "Period in seconds", + "type": "integer", + "default": 10 + }, + "successThreshold": { + "description": "Success threshold", + "type": "integer", + "default": 1 + }, + "timeoutSeconds": { + "description": "Timeout in seconds", + "type": "integer", + "default": 3 + } + } + }, + "readinessProbe": { + "description": "Readiness probe configuration", + "type": "object", + "properties": { + "exec": { + "description": "Exec probe configuration", + "type": "object", + "properties": { + "command": { + "description": "Command to execute", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "tcpSocket": { + "description": "TCP socket probe configuration", + "type": "object", + "properties": { + "port": { + "description": "Port to probe", + "type": "integer" + } + } + }, + "failureThreshold": { + "description": "Failure threshold", + "type": "integer", + "default": 3 + }, + "httpGet": { + "description": "HTTP GET configuration for liveness probe", + "type": "object", + "properties": { + "path": { + "description": "Path to probe", + "type": "string", + "default": "/" + }, + "port": { + "description": "Port to probe", + "type": "integer", + "default": 8080 + }, + "scheme": { + "description": "Scheme to use", + "type": "string", + "default": "HTTP" + } + } + }, + "initialDelaySeconds": { + "description": "Initial delay in seconds", + "type": "integer", + "default": 5 + }, + "periodSeconds": { + "description": "Period in seconds", + "type": "integer", + "default": 10 + }, + "successThreshold": { + "description": "Success threshold", + "type": "integer", + "default": 1 + }, + "timeoutSeconds": { + "description": "Timeout in seconds", + "type": "integer", + "default": 3 + } + } + } + } + }, + "serviceAccount": { + "description": "Service account configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "create": { + "description": "Create service account", + "type": "boolean", + "default": false + }, + "annotations": { + "description": "Annotations for the service account", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "name": { + "description": "Name of the service account", + "type": "string" + } + } + }, + "podAnnotations": { + "description": "Annotations for the pod", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "podSecurityContext": { + "description": "Pod security context", + "type": "object", + "additionalProperties": false, + "properties": { + "fsGroup": { + "description": "Filesystem group", + "type": "integer" + } + } + }, + "securityContext": { + "description": "Container security context", + "type": "object", + "additionalProperties": false, + "properties": { + "capabilities": { + "description": "Security capabilities", + "type": "object", + "properties": { + "drop": { + "description": "Capabilities to drop", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "readOnlyRootFilesystem": { + "description": "Read-only root filesystem", + "type": "boolean" + }, + "runAsNonRoot": { + "description": "Run as non-root user", + "type": "boolean" + }, + "runAsUser": { + "description": "User ID to run as", + "type": "integer" + } + } + }, + "resources": { + "description": "Resource requests and limits", + "type": "object", + "additionalProperties": false, + "properties": { + "limits": { + "description": "Resource limits", + "type": "object", + "properties": { + "cpu": { + "description": "CPU limit", + "type": "string", + "default": "100m" + }, + "memory": { + "description": "Memory limit", + "type": "string", + "default": "128Mi" + } + } + }, + "requests": { + "description": "Resource requests", + "type": "object", + "properties": { + "cpu": { + "description": "CPU request", + "type": "string", + "default": "50m" + }, + "memory": { + "description": "Memory request", + "type": "string", + "default": "64Mi" + } + } + } + } + }, + "autoscaling": { + "description": "Autoscaling configuration", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "description": "Enable autoscaling", + "type": "boolean", + "default": false + }, + "minReplicas": { + "description": "Minimum number of replicas", + "type": "integer", + "default": 1 + }, + "maxReplicas": { + "description": "Maximum number of replicas", + "type": "integer", + "default": 100 + }, + "targetCPUUtilizationPercentage": { + "description": "Target CPU utilization percentage", + "type": "integer", + "default": 80 + }, + "targetMemoryUtilizationPercentage": { + "description": "Target memory utilization percentage", + "type": "integer" + } + } + }, + "nodeSelector": { + "description": "Node selector for pod assignment", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "tolerations": { + "description": "Tolerations for pod assignment", + "type": "array", + "items": { + "type": "object" + } + }, + "affinity": { + "description": "Affinity rules for pod assignment", + "type": "object" + }, + "podAntiAffinity": { + "description": "Pod anti-affinity rules", + "type": ["string", "null"], + "default": "soft", + "enum": ["soft", "hard", null] + }, + "registry": { + "description": "Registry configuration - Injected by ODS pipeline", + "type": "string" + }, + "componentId": { + "description": "Component ID - Injected by ODS pipeline", + "type": "string" + }, + "imageTag": { + "description": "Image tag - Injected by ODS pipeline", + "type": "string" + }, + "imageNamespace": { + "description": "Image namespace", + "type": "string" + }, + "global": { + "description": "Global configuration - Injected by ODS pipeline", + "type": "object", + "additionalProperties": true, + "properties": { + "imageNamespace": { + "description": "Image namespace - Injected by ODS pipeline", + "type": "string" + }, + "registry": { + "description": "Registry configuration - Injected by ODS pipeline", + "type": "string" + }, + "componentId": { + "description": "Component ID - Injected by ODS pipeline", + "type": "string" + }, + "imageTag": { + "description": "Image tag - Injected by ODS pipeline", + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/docker-plain/files/chart/values.test.yaml b/docker-plain/files/chart/values.test.yaml new file mode 100644 index 000000000..8c6f855da --- /dev/null +++ b/docker-plain/files/chart/values.test.yaml @@ -0,0 +1,3 @@ +# This file is used to override the default values in the chart/values.yaml file for deployment in 'test' environment + +replicaCount: 2 \ No newline at end of file diff --git a/docker-plain/files/chart/values.yaml b/docker-plain/files/chart/values.yaml new file mode 100644 index 000000000..01f88a17e --- /dev/null +++ b/docker-plain/files/chart/values.yaml @@ -0,0 +1,127 @@ +## Default values for chart. +## This is a YAML-formatted file. Intendation matters! +## Comments are prefixed with two hashes (##), examples are commented with one hash (#) +## Declare variables to be passed into your templates. + +## The number of replicas to deploy. +## For high availability use more than 1 replica +replicaCount: 1 + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +# NOTE: By default image Values are injected from CICD Jenkins pipeline, values defined here are used when not being on CICD Jenkins pipeline context. +# Default values here are provided in case one needs to use the chart without CICD (i.e.: testing the chart). +image: + # registry: "public.ecr.aws" + # path: "nginx" + # name: "nginx-unprivileged" + # tag: "alpine-slim" + # see https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy + pullPolicy: IfNotPresent + +## Prefer using ingress over openshift routes +ingress: + enabled: false + className: 'openshift-default' + # router: external + annotations: + ## adjust openshift timeouts (default is 60s) + haproxy.router.openshift.io/timeout: 300s + haproxy.router.openshift.io/timeout-tunnel: 300s + ## e.g. add cert-manager support by annotating the ingress https://cert-manager.io/docs/usage/ingress/ + ## ask in your company for good defaults + + hosts: [] # When defining a host you must define also a path + # - host: yourapp.yourdomain.com + # paths: + # - path: / + # pathType: Prefix + tls: [] # If `tls` is left empty then the default OpenShift TLS config will be loaded (i.e.: TLS edge termination with HTTP redirect to HTTPS) + # - secretName: chart-example-tls + # hosts: + # - yourapp.yourdomain.com + +service: + enabled: true + port: 8080 + type: ClusterIP + +# There are two types of strategy: `Recreate` and `RollingUpdate` +# Please refer to https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy +deploymentStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0% + maxSurge: 50% + +probes: + livenessProbe: + failureThreshold: 3 + httpGet: + path: "/health" + port: 8080 + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 3 + + readinessProbe: + failureThreshold: 1 + httpGet: + path: "/health" + port: 8080 + scheme: HTTP + initialDelaySeconds: 3 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + +serviceAccount: + # Specifies whether a service account should be created + create: false + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + # name: "default" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +resources: + limits: + cpu: 100m + memory: 32Mi + requests: + cpu: 10m + memory: 16Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +## PodAntiAffinity is a way to control that not all of your pods are scheduled +## onto the same node. +## possible values: soft, hard, null +podAntiAffinity: soft diff --git a/docker-plain/files/docker/Dockerfile b/docker-plain/files/docker/Dockerfile index 540cb64bd..51afac909 100644 --- a/docker-plain/files/docker/Dockerfile +++ b/docker-plain/files/docker/Dockerfile @@ -1,12 +1,5 @@ -# add /overwrite FROM with your base image, and do whatever you like here :) -FROM alpine:latest +FROM public.ecr.aws/nginx/nginx-unprivileged:alpine-slim -RUN echo "building simple container" +ENV NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE=set -RUN apk update && \ - apk -i upgrade && \ - apk cache clean - -EXPOSE 8080 - -CMD ["/bin/sh", "-c", "/usr/bin/nc -lk -p 8080 -e echo -e \"HTTP/1.1 200 OK\n\nHello World!\n$(date)\""] +COPY default.conf /etc/nginx/conf.d/ diff --git a/docker-plain/files/docker/default.conf b/docker-plain/files/docker/default.conf new file mode 100644 index 000000000..efdede32c --- /dev/null +++ b/docker-plain/files/docker/default.conf @@ -0,0 +1,22 @@ +server { + listen 8080; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + location = /health { + access_log off; + add_header 'Content-Type' 'application/json'; + return 200 '{"status":"UP"}'; + } +} diff --git a/docker-plain/testdata/steps.yml b/docker-plain/testdata/steps.yml index d9a0b7610..b89e746f0 100644 --- a/docker-plain/testdata/steps.yml +++ b/docker-plain/testdata/steps.yml @@ -9,12 +9,7 @@ steps: verify: jenkinsStages: golden/jenkins-build-stages.json openShiftResources: - imageTags: - - name: "{{.ComponentID}}" - tag: latest imageStreams: - "{{.ComponentID}}" - deploymentConfigs: - - "{{.ComponentID}}" services: - "{{.ComponentID}}"