From e4dc1f6cb69399487cdefed71f1ad6b4d89d0d06 Mon Sep 17 00:00:00 2001 From: matthiasbremser Date: Fri, 24 Oct 2025 13:41:40 +0200 Subject: [PATCH 1/9] feat: INFIAAS-9233 Upgrade with dynamic backend configuration of Azure quickstarter (#1131) --- CHANGELOG.md | 10 ++ inf-terraform-azure/Jenkinsfile.template | 129 ++++++++++-------- inf-terraform-azure/files/Makefile | 22 ++- inf-terraform-azure/files/README.md | 5 +- inf-terraform-azure/files/backend.tf | 1 - .../files/environments/dev.json | 5 +- .../files/environments/dev.yml | 6 + .../files/environments/prod.json | 5 +- .../files/environments/prod.yml | 6 + .../files/environments/productive.json | 6 + .../files/environments/test.json | 5 +- .../files/environments/test.yml | 6 + .../files/environments/testing.yml | 7 + inf-terraform-azure/files/inputs2outputs.tf | 1 + inf-terraform-azure/files/main.tf | 3 +- .../files/test/fixtures/default/main.tf | 4 + .../test/fixtures/default/moduleoutputs.tf | 2 +- inf-terraform-azure/files/variables.tf | 6 +- inf-terraform-azure/files/versions.tf | 2 +- 19 files changed, 158 insertions(+), 73 deletions(-) create mode 100644 inf-terraform-azure/files/environments/dev.yml create mode 100644 inf-terraform-azure/files/environments/prod.yml create mode 100644 inf-terraform-azure/files/environments/productive.json create mode 100644 inf-terraform-azure/files/environments/test.yml create mode 100644 inf-terraform-azure/files/environments/testing.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc0aebe0..8eaad0c80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ ### Changed +<<<<<<< HEAD +======= +- Upgrade with dynamic backend configuration of Azure quickstarter ([#1131](https://github.com/opendevstack/ods-quickstarters/pull/1131)) + +### Fixed + + +## [4.10.0] - 2025-10-13 +### Changed +>>>>>>> c8c6fd9 (feat: INFIAAS-9233 Upgrade with dynamic backend configuration of Azure quickstarter (#1131)) - Use npm ci in e2e-cypress quickstarter & Better TypeScript support in e2e-cypress quickstarter ([#1114](https://github.com/opendevstack/ods-quickstarters/pull/1114)) - e2e-cypress get rid of additional chrome installation and switch to edge for pdf generation ([#1112](https://github.com/opendevstack/ods-quickstarters/pull/1112)) diff --git a/inf-terraform-azure/Jenkinsfile.template b/inf-terraform-azure/Jenkinsfile.template index 712a0a3eb..c42f90899 100644 --- a/inf-terraform-azure/Jenkinsfile.template +++ b/inf-terraform-azure/Jenkinsfile.template @@ -25,6 +25,17 @@ import org.ods.services.ServiceRegistry import org.ods.services.BitbucketService import org.ods.util.GitCredentialStore +def convertToName(String value) { + return value + // Remove all quotes + .replaceAll(/['"]+/, '') + // Replace all non-alphanums with a '-' + .replaceAll(/[^a-zA-Z0-9\-_]/, '-') + // Replace sequences of '-' with a single '-' + .replaceAll(/\-+/, '-') + // Remove leading and traiiling '-'s + .replaceAll(/^\-?(.*?)\-?$/, '\$1') +} odsComponentPipeline( imageStreamTag: '@ods_namespace@/jenkins-agent-terraform-2408:@agent_image_tag@', @@ -41,13 +52,16 @@ odsComponentPipeline( stageReport(context) } -def getEnvSettings(def tfBackend, def workspace ) { +def getEnvSettings(def tfBackend, def workspace, def inputs, def context) { def config = [ "TF_BACKEND=${tfBackend}" ] - if (workspace) { - config << "TF_WORKSPACE=${workspace}" + if (workspace && inputs) { + config << "TF_WORKSPACE=${context.environment}" + config << "TFSTATE_KEY=${convertToName(inputs['name']).toLowerCase()}:" + config << "TFENVIRONMENT=${convertToName(inputs['environment']).toLowerCase()}" + config << "TFLOCATION=${convertToName(inputs['location']).toLowerCase()}" } return config } @@ -81,51 +95,50 @@ def stageGetConfig(def context) { // handle environment specific variables // copy json from /environments/${context.environment}.auto.tfvars.json to / if (fileExists("./environments/${context.environment}.json")) { - withEnv(["VARIABLESFILE=./environments/${context.environment}.json"]) - { + withEnv(["VARIABLESFILE=./environments/${context.environment}.json"]) { def statusVarEnv = sh(script: ''' cp $VARIABLESFILE env.auto.tfvars.json && echo "Variables file $VARIABLESFILE" ''', - returnStatus: true) + returnStatus: true) if (statusVarEnv != 0) { error "Can not copy json file!" } } } - } else { error "No environment specified!"} + } else { + error "No environment specified!" + } } } -//initialize pre-requisites def stageInit(def context) { - if (stackdeploy) - { - stage('Init') - { + if (stackdeploy) { + stage('Init') { def bitbucketService = ServiceRegistry.instance.get(BitbucketService) bitbucketService.withTokenCredentials { String username, String pw -> GitCredentialStore.configureAndStore(this, context.bitbucketUrl, username, pw) } - def workspace = env.WORKSPACE - def statusTF = sh(script: ''' - make create-tfvars''', - returnStatus: true) + def statusTF = sh(script: """ + TF_WORKSPACE=${context.environment} make create-tfvars""", + returnStatus: true) if (statusTF != 0) { error "Creation of tfvars failed!" } + tfvarsjson = readJSON file: './terraform.tfvars.json' } - } else { print "Stage Init has been skipped."} + } else { + print "Stage Init has been skipped." + } } def stageReport(def context) { - if (stackdeploy == true) { - stage('Report') - { + if (stackdeploy) { + stage('Report') { def status = sh(script: ''' - make install-report ''', - returnStatus: true) + make install-report ''', + returnStatus: true) if (status != 0) { error "Report failed!" } @@ -135,25 +148,24 @@ def stageReport(def context) { stash(name: "target-${context.componentId}-${context.buildNumber}", includes: 'reports/install/azure_deploy_account.log' , allowEmpty: true) stash(name: "state-${context.componentId}-${context.buildNumber}", includes: 'reports/install/tf_show.log' , allowEmpty: true) } - } else { print "Stage Report has been skipped."} + } else { + print "Stage Report has been skipped." + } } def stageSmoke(def context) { - if (stackdeploy == true) { - stage('Smoke') - { - withCredentials([ + if (stackdeploy) { + stage('Smoke') { + withCredentials([ string(credentialsId: azureSubscriptionId, variable: 'AZURE_SUBSCRIPTION_ID'), string(credentialsId: azureTenantId, variable: 'AZURE_TENANT_ID'), string(credentialsId: azureClientId, variable: 'AZURE_CLIENT_ID'), string(credentialsId: azureClientSecret, variable: 'AZURE_CLIENT_SECRET') - ]) - { - withEnv(getEnvSettings(context.environment, tfvarsjson['meta_environment'])) - { - def status = sh(script: ''' + ]) { + withEnv(getEnvSettings(context.environment, tfvarsjson['meta_environment'], tfvarsjson, context)) { + def status = sh(script: """ eval \"\$(rbenv init -)\" && \ - make smoke-test ''', + TF_WORKSPACE=${context.environment} make smoke-test """, returnStatus: true) if (status != 0) { error "Smoke-Test failed!" @@ -161,22 +173,22 @@ def stageSmoke(def context) { } } } - } else { print "Stage Smoke Test port has been skipped."} + } else { + print "Stage Smoke Test port has been skipped." + } } //execute make test of the project def stageTest(def context) { - if (stackdeploy == true) { + if (stackdeploy) { stage('Test') { withCredentials([ string(credentialsId: azureSubscriptionIdTesting, variable: 'AZURE_SUBSCRIPTION_ID'), string(credentialsId: azureTenantIdTesting, variable: 'AZURE_TENANT_ID'), string(credentialsId: azureClientIdTesting, variable: 'AZURE_CLIENT_ID'), string(credentialsId: azureClientSecretTesting, variable: 'AZURE_CLIENT_SECRET') - ]) - { - withEnv(getEnvSettings(context.environment, null)) - { + ]) { + withEnv(getEnvSettings(context.environment, null, tfvarsjson, context)) { def status = sh(script: ''' make test''', returnStatus: true) @@ -186,26 +198,25 @@ def stageTest(def context) { } } } - } else { print "Stage Test has been skipped." } + } else { + print "Stage Test has been skipped." + } } //execute make plan of the project def stagePlan(def context) { - if (stackdeploy == true) { - stage('Plan') - { + if (stackdeploy) { + stage('Plan') { withCredentials([ string(credentialsId: azureSubscriptionId, variable: 'AZURE_SUBSCRIPTION_ID'), string(credentialsId: azureTenantId, variable: 'AZURE_TENANT_ID'), string(credentialsId: azureClientId, variable: 'AZURE_CLIENT_ID'), string(credentialsId: azureClientSecret, variable: 'AZURE_CLIENT_SECRET') - ]) - { - withEnv(getEnvSettings(context.environment, tfvarsjson['meta_environment'])) - { - def status = sh(script: ''' + ]) { + withEnv(getEnvSettings(context.environment, tfvarsjson['meta_environment'], tfvarsjson, context)) { + def status = sh(script: """ eval \"\$(rbenv init -)\" && \ - make plan''', + make plan TF_WORKSPACE=${context.environment}""", returnStatus: true) if (status != 0) { error "Plan failed!" @@ -213,25 +224,25 @@ def stagePlan(def context) { } } } - } else { print "Stage Plan has been skipped." } + } else { + print "Stage Plan has been skipped." + } } //execute make deploy of the stack def stageDeploy(def context) { - if (stackdeploy == true) { + if (stackdeploy) { stage('Deploy') { withCredentials([ string(credentialsId: azureSubscriptionId, variable: 'AZURE_SUBSCRIPTION_ID'), string(credentialsId: azureTenantId, variable: 'AZURE_TENANT_ID'), string(credentialsId: azureClientId, variable: 'AZURE_CLIENT_ID'), string(credentialsId: azureClientSecret, variable: 'AZURE_CLIENT_SECRET') - ]) - { - withEnv(getEnvSettings(context.environment, tfvarsjson['meta_environment'])) - { - def status = sh(script: ''' + ]) { + withEnv(getEnvSettings(context.environment, tfvarsjson['meta_environment'], tfvarsjson, context)) { + def status = sh(script: """ eval \"\$(rbenv init -)\" && \ - make deploy''', + TF_WORKSPACE=${context.environment} make deploy """, returnStatus: true) if (status != 0) { error "Deploy failed!" @@ -239,5 +250,7 @@ def stageDeploy(def context) { } } } - } else { print "Stage Deploy has been skipped." } + } else { + print "Stage Deploy has been skipped." + } } diff --git a/inf-terraform-azure/files/Makefile b/inf-terraform-azure/files/Makefile index c15abb5a0..5ed96ee88 100644 --- a/inf-terraform-azure/files/Makefile +++ b/inf-terraform-azure/files/Makefile @@ -10,7 +10,10 @@ SHELL := /usr/bin/env bash MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --no-builtin-rules -TF_WORKSPACE = default +TF_WORKSPACE ?= default +RESOURCE_GROUP_NAME := zzzINF-tfstate-01-rg +SUBSCRIPTION_MD5 := $(shell echo $$AZURE_SUBSCRIPTION_ID | md5sum | cut -c1-16) +STORAGE_ACCOUNT_NAME := tfstate$(SUBSCRIPTION_MD5) # tfenv hack DEBUG := 0 @@ -29,7 +32,7 @@ init: install-dev-deps install-test-deps .PHONY: create-tfvars # create terraform.tfvars.json create-tfvars: - terraform-docs json . | jq '.inputs | map({ (.name): .default }) | add' > terraform.tfvars.json + cp ./environments/$(TF_WORKSPACE).json terraform.tfvars.json .PHONY: prep-test prep-test: @@ -82,6 +85,9 @@ plan: install-test-deps $(call check_defined,AZURE_TENANT_ID) $(call check_defined,AZURE_CLIENT_ID) $(call check_defined,AZURE_CLIENT_SECRET) + $(call check_defined,TFENVIRONMENT) + $(call check_defined,TFLOCATION) + $(call check_defined,TFSTATE_KEY) TF_IN_AUTOMATION=1 TF_WORKSPACE="$(TF_WORKSPACE)" terraform plan -input=false -out=tfplan .PHONY: deploy @@ -170,7 +176,17 @@ install-python-env: .PHONY: init-terraform # Install Terraform workspace. init-terraform: - echo 1 | terraform init -backend=true -force-copy -input=false -backend-config="environments/$(TF_BACKEND).tfbackend.config" + $(call check_defined,AZURE_SUBSCRIPTION_ID) + $(call check_defined,RESOURCE_GROUP_NAME) + $(call check_defined,TFENVIRONMENT) + $(call check_defined,TFLOCATION) + $(call check_defined,STORAGE_ACCOUNT_NAME) + $(call check_defined,TFSTATE_KEY) + printenv AZURE_SUBSCRIPTION_ID + printenv TFENVIRONMENT + printenv TFLOCATION + printenv TFSTATE_KEY + echo 1 | terraform init -backend=true -backend-config="resource_group_name=$(RESOURCE_GROUP_NAME)" -backend-config="storage_account_name=$(STORAGE_ACCOUNT_NAME)" -backend-config="key=$(TFSTATE_KEY)" -force-copy -input=false .PHONY: install-test-deps # Install testing dependencies. diff --git a/inf-terraform-azure/files/README.md b/inf-terraform-azure/files/README.md index 075b56e02..b727b65e2 100644 --- a/inf-terraform-azure/files/README.md +++ b/inf-terraform-azure/files/README.md @@ -49,8 +49,9 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [is\_test](#input\_is\_test) | Whether whether it is part of a test execution or not. Defaults to false. | `bool` | `false` | no | -| [meta\_environment](#input\_meta\_environment) | The type of the environment. Can be any of DEVELOPMENT, EVALUATION, PRODUCTIVE, QUALITYASSURANCE, TRAINING, VALIDATION. | `string` | `"DEVELOPMENT"` | no | -| [name](#input\_name) | The name of the stack. | `string` | `"stack-azure-quickstarter"` | no | +| [location](#input\_location) | The location of the stack. | `string` | n/a | yes | +| [meta\_environment](#input\_meta\_environment) | The type of the environment. Can be any of DEVELOPMENT, EVALUATION, PRODUCTIVE, QUALITYASSURANCE, TRAINING, VALIDATION. | `string` | n/a | yes | +| [name](#input\_name) | The name of the stack. | `string` | n/a | yes | ## Outputs diff --git a/inf-terraform-azure/files/backend.tf b/inf-terraform-azure/files/backend.tf index 7037fb000..6cf132323 100644 --- a/inf-terraform-azure/files/backend.tf +++ b/inf-terraform-azure/files/backend.tf @@ -1,7 +1,6 @@ terraform { backend "azurerm" { container_name = "tfstate" - key = "terraform.tfstate" use_azuread_auth = true } } diff --git a/inf-terraform-azure/files/environments/dev.json b/inf-terraform-azure/files/environments/dev.json index e9b927010..ab913fd8f 100644 --- a/inf-terraform-azure/files/environments/dev.json +++ b/inf-terraform-azure/files/environments/dev.json @@ -1,3 +1,6 @@ { - "meta_environment": "DEVELOPMENT" + "environment": "dev", + "location": "westeurope", + "meta_environment": "DEVELOPMENT", + "name": "stack-azure-quickstarter" } diff --git a/inf-terraform-azure/files/environments/dev.yml b/inf-terraform-azure/files/environments/dev.yml new file mode 100644 index 000000000..0d4a2cee0 --- /dev/null +++ b/inf-terraform-azure/files/environments/dev.yml @@ -0,0 +1,6 @@ +credentials: + azureSubscriptionId: age-cd-subscription-id-dev + azureTenantId: age-cd-tenant-id-dev + azureClientId: age-cd-client-id-dev + azureClientSecret: age-cd-client-secret-dev + diff --git a/inf-terraform-azure/files/environments/prod.json b/inf-terraform-azure/files/environments/prod.json index b582cfacc..f7c4097ed 100644 --- a/inf-terraform-azure/files/environments/prod.json +++ b/inf-terraform-azure/files/environments/prod.json @@ -1,3 +1,6 @@ { - "meta_environment": "PRODUCTIVE" + "environment": "prod", + "location": "westeurope", + "meta_environment": "PRODUCTIVE", + "name": "stack-azure-quickstarter" } diff --git a/inf-terraform-azure/files/environments/prod.yml b/inf-terraform-azure/files/environments/prod.yml new file mode 100644 index 000000000..c330dd77b --- /dev/null +++ b/inf-terraform-azure/files/environments/prod.yml @@ -0,0 +1,6 @@ +credentials: + azureSubscriptionId: age-cd-subscription-id-prod + azureTenantId: age-cd-tenant-id-prod + azureClientId: age-cd-client-id-prod + azureClientSecret: age-cd-client-secret-prod + diff --git a/inf-terraform-azure/files/environments/productive.json b/inf-terraform-azure/files/environments/productive.json new file mode 100644 index 000000000..03671d053 --- /dev/null +++ b/inf-terraform-azure/files/environments/productive.json @@ -0,0 +1,6 @@ +{ + "environment": "prod", + "location": "westeurope", + "meta_environment": "PRODUCTIVE", + "name": "stack-azure-skeleton-westeurope" +} diff --git a/inf-terraform-azure/files/environments/test.json b/inf-terraform-azure/files/environments/test.json index 198dc11dd..720ce9f51 100644 --- a/inf-terraform-azure/files/environments/test.json +++ b/inf-terraform-azure/files/environments/test.json @@ -1,3 +1,6 @@ { - "meta_environment": "QUALITYASSURANCE" + "environment": "test", + "location": "westeurope", + "meta_environment": "QUALITYASSURANCE", + "name": "stack-azure-quickstarter" } diff --git a/inf-terraform-azure/files/environments/test.yml b/inf-terraform-azure/files/environments/test.yml new file mode 100644 index 000000000..fe426bb73 --- /dev/null +++ b/inf-terraform-azure/files/environments/test.yml @@ -0,0 +1,6 @@ +credentials: + azureSubscriptionId: age-cd-subscription-id-qa + azureTenantId: age-cd-tenant-id-qa + azureClientId: age-cd-client-id-qa + azureClientSecret: age-cd-client-secret-qa + diff --git a/inf-terraform-azure/files/environments/testing.yml b/inf-terraform-azure/files/environments/testing.yml new file mode 100644 index 000000000..a7ec4faeb --- /dev/null +++ b/inf-terraform-azure/files/environments/testing.yml @@ -0,0 +1,7 @@ +# TODO: map credentials here as they are stored prefixed with project_id? + +credentials: + azureSubscriptionId: age-cd-subscription-id-testing + azureTenantId: age-cd-tenant-id-testing + azureClientId: age-cd-client-id-testing + azureClientSecret: age-cd-client-secret-testing diff --git a/inf-terraform-azure/files/inputs2outputs.tf b/inf-terraform-azure/files/inputs2outputs.tf index 985d2307b..390717084 100644 --- a/inf-terraform-azure/files/inputs2outputs.tf +++ b/inf-terraform-azure/files/inputs2outputs.tf @@ -6,6 +6,7 @@ output "inputs2outputs" { description = "all inputs passed to outputs" value = [{ is_test = var.is_test + location = var.location meta_environment = var.meta_environment name = var.name }] diff --git a/inf-terraform-azure/files/main.tf b/inf-terraform-azure/files/main.tf index 6246abe3b..952760e89 100644 --- a/inf-terraform-azure/files/main.tf +++ b/inf-terraform-azure/files/main.tf @@ -1,6 +1,5 @@ locals { unique_name = var.is_test ? "${var.name}-${local.id}" : var.name - location = "westeurope" tags = merge(local.common_tags, { DeploymentDate = formatdate("YYYYMMDD", timestamp()) @@ -12,7 +11,7 @@ resource "time_static" "deployment" {} resource "azurerm_resource_group" "this" { name = local.unique_name - location = local.location + location = var.location tags = local.tags } diff --git a/inf-terraform-azure/files/test/fixtures/default/main.tf b/inf-terraform-azure/files/test/fixtures/default/main.tf index 910a7e7b2..8089cd2ce 100644 --- a/inf-terraform-azure/files/test/fixtures/default/main.tf +++ b/inf-terraform-azure/files/test/fixtures/default/main.tf @@ -1,5 +1,8 @@ locals { name = "stack-azure-quickstarter-test" + tags = { + Name = local.name + } } module "stack-azure-quickstarter-test" { @@ -9,4 +12,5 @@ module "stack-azure-quickstarter-test" { is_test = true name = local.name meta_environment = "DEVELOPMENT" + location = "westeurope" } diff --git a/inf-terraform-azure/files/test/fixtures/default/moduleoutputs.tf b/inf-terraform-azure/files/test/fixtures/default/moduleoutputs.tf index d85fa71da..f3fc10ade 100644 --- a/inf-terraform-azure/files/test/fixtures/default/moduleoutputs.tf +++ b/inf-terraform-azure/files/test/fixtures/default/moduleoutputs.tf @@ -1,6 +1,6 @@ # This file has been created automatically. output "module_ods_quickstarters" { - value = module.stack-azure-quickstarter-test.* + value = module.stack-azure-quickstarter-test.* sensitive = true } diff --git a/inf-terraform-azure/files/variables.tf b/inf-terraform-azure/files/variables.tf index e7cbdb4db..0819cb876 100644 --- a/inf-terraform-azure/files/variables.tf +++ b/inf-terraform-azure/files/variables.tf @@ -23,7 +23,6 @@ variable "name" { description = "The name of the stack." type = string - default = "stack-azure-quickstarter" } variable "is_test" { @@ -35,6 +34,9 @@ variable "is_test" { variable "meta_environment" { description = "The type of the environment. Can be any of DEVELOPMENT, EVALUATION, PRODUCTIVE, QUALITYASSURANCE, TRAINING, VALIDATION." type = string - default = "DEVELOPMENT" } +variable "location" { + description = "The location of the stack." + type = string +} diff --git a/inf-terraform-azure/files/versions.tf b/inf-terraform-azure/files/versions.tf index 6d912b66f..f06cd523c 100644 --- a/inf-terraform-azure/files/versions.tf +++ b/inf-terraform-azure/files/versions.tf @@ -3,7 +3,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.114.0" + version = "~> 3.100" } local = { source = "hashicorp/local" From 2d1ebc309a7571ee27095add09a9f1c70c9a3b3a Mon Sep 17 00:00:00 2001 From: "Vazquez,Brais (IT EDP)" Date: Wed, 3 Dec 2025 09:59:59 +0100 Subject: [PATCH 2/9] changelog --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eaad0c80..e748ac573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,6 @@ ### Changed -<<<<<<< HEAD -======= - Upgrade with dynamic backend configuration of Azure quickstarter ([#1131](https://github.com/opendevstack/ods-quickstarters/pull/1131)) ### Fixed @@ -15,7 +13,6 @@ ## [4.10.0] - 2025-10-13 ### Changed ->>>>>>> c8c6fd9 (feat: INFIAAS-9233 Upgrade with dynamic backend configuration of Azure quickstarter (#1131)) - Use npm ci in e2e-cypress quickstarter & Better TypeScript support in e2e-cypress quickstarter ([#1114](https://github.com/opendevstack/ods-quickstarters/pull/1114)) - e2e-cypress get rid of additional chrome installation and switch to edge for pdf generation ([#1112](https://github.com/opendevstack/ods-quickstarters/pull/1112)) From 9c0e9f0feaa5f64de803a9d9a68267dcfbabf90e Mon Sep 17 00:00:00 2001 From: Ben <56521054+ElfenB@users.noreply.github.com> Date: Tue, 28 Oct 2025 09:57:28 +0100 Subject: [PATCH 3/9] Add jenkins-agent-nodejs24 (#1134) --- .../continuous-integration-workflow.yml | 16 +++++ CHANGELOG.md | 2 + Makefile | 22 ++++-- common/jenkins-agents/README.md | 1 + .../nodejs24/docker/Dockerfile.ubi8 | 63 +++++++++++++++++ .../docker/contrib/bin/configure-agent | 63 +++++++++++++++++ .../docker/yum.repos.d/almalinux.repo | 13 ++++ .../docker/yum.repos.d/google-chrome.repo | 6 ++ .../docker/yum.repos.d/microsoft-edge.repo | 6 ++ .../nodejs24/ocp-config/Tailorfile | 5 ++ .../jenkins-agents/nodejs24/ocp-config/bc.yml | 68 +++++++++++++++++++ .../jenkins-agents/nodejs24/ocp-config/is.yml | 14 ++++ .../jenkins-agents/pages/nodejs24.adoc | 17 +++++ docs/modules/quickstarters/nav.adoc | 1 + 14 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 common/jenkins-agents/nodejs24/docker/Dockerfile.ubi8 create mode 100644 common/jenkins-agents/nodejs24/docker/contrib/bin/configure-agent create mode 100644 common/jenkins-agents/nodejs24/docker/yum.repos.d/almalinux.repo create mode 100644 common/jenkins-agents/nodejs24/docker/yum.repos.d/google-chrome.repo create mode 100644 common/jenkins-agents/nodejs24/docker/yum.repos.d/microsoft-edge.repo create mode 100644 common/jenkins-agents/nodejs24/ocp-config/Tailorfile create mode 100644 common/jenkins-agents/nodejs24/ocp-config/bc.yml create mode 100644 common/jenkins-agents/nodejs24/ocp-config/is.yml create mode 100644 docs/modules/jenkins-agents/pages/nodejs24.adoc diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 676eaa853..586e030ef 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -83,6 +83,22 @@ jobs: --build-arg nexusAuth=developer:s3cr3t \ . + jenkins-agent-nodejs24-ubi8: + name: Jenkins agent NodeJS 24 (UBI8) + runs-on: ubuntu-22.04 + steps: + - + name: Checkout repository + uses: actions/checkout@v4.2.2 + - + name: Build docker image + working-directory: common/jenkins-agents/nodejs24/docker + run: | + docker build --tag agent-nodejs24-test-ubi8 --file Dockerfile.ubi8 \ + --build-arg nexusUrl=https://nexus.example.com \ + --build-arg nexusAuth=developer:s3cr3t \ + . + jenkins-agent-python-ubi8: name: Jenkins agent Python (UBI8) runs-on: ubuntu-22.04 diff --git a/CHANGELOG.md b/CHANGELOG.md index e748ac573..232dcbd3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added +- Added Node.js 24 Jenkins agent and related CI/Makefile/docs updates ([#1133]) (common/jenkins-agents/nodejs24) ([#1134](https://github.com/opendevstack/ods-quickstarters/pull/1134)) + ### Changed - Upgrade with dynamic backend configuration of Azure quickstarter ([#1131](https://github.com/opendevstack/ods-quickstarters/pull/1131)) diff --git a/Makefile b/Makefile index 2b1f086ab..aec015b1d 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,16 @@ ODS_NAMESPACE := $(shell grep ODS_NAMESPACE $(CURDIR)/../ods-configuration/ods-c install-jenkins-agent: install-jenkins-agent-golang install-jenkins-agent-jdk install-jenkins-agent-nodejs install-jenkins-agent-python install-jenkins-agent-scala install-jenkins-agent-terraform-2306 install-jenkins-agent-terraform-2408 install-jenkins-agent-rust .PHONY: install-jenkins-agent -## Update OpenShift resources related Jenkins agent resources. -apply-jenkins-agent-build: apply-jenkins-agent-golang-build apply-jenkins-agent-jdk-build apply-jenkins-agent-nodejs18-build apply-jenkins-agent-nodejs20-build apply-jenkins-agent-nodejs22-build apply-jenkins-agent-python-build apply-jenkins-agent-scala-build apply-jenkins-agent-terraform-build-2306 apply-jenkins-agent-terraform-build-2408 apply-jenkins-agent-rust-build +.## Update OpenShift resources related Jenkins agent resources. +apply-jenkins-agent-build: apply-jenkins-agent-golang-build apply-jenkins-agent-jdk-build apply-jenkins-agent-nodejs18-build apply-jenkins-agent-nodejs20-build apply-jenkins-agent-nodejs22-build apply-jenkins-agent-nodejs24-build apply-jenkins-agent-python-build apply-jenkins-agent-scala-build apply-jenkins-agent-terraform-build-2306 apply-jenkins-agent-terraform-build-2408 apply-jenkins-agent-rust-build .PHONY: apply-jenkins-agent-build ## Start builds of Jenkins agents. -start-jenkins-agent-build: start-jenkins-agent-golang-build start-jenkins-agent-jdk-build start-jenkins-agent-nodejs18-build start-jenkins-agent-nodejs20-build apply-jenkins-agent-nodejs22-build start-jenkins-agent-python-build start-jenkins-agent-scala-build start-jenkins-agent-terraform-build-2306 start-jenkins-agent-terraform-build-2408 start-jenkins-agent-rust-build +.PHONY: apply-jenkins-agent-build + +## Start builds of Jenkins agents. +start-jenkins-agent-build: start-jenkins-agent-golang-build start-jenkins-agent-jdk-build start-jenkins-agent-nodejs18-build start-jenkins-agent-nodejs20-build start-jenkins-agent-nodejs22-build start-jenkins-agent-nodejs24-build start-jenkins-agent-python-build start-jenkins-agent-scala-build start-jenkins-agent-terraform-build-2306 start-jenkins-agent-terraform-build-2408 start-jenkins-agent-rust-build +.PHONY: start-jenkins-agent-build .PHONY: start-jenkins-agent-build @@ -54,7 +58,7 @@ start-jenkins-agent-jdk-build: # JENKINS AGENT NODEJS ## Install or update Jenkins Node agent resources. -install-jenkins-agent-nodejs: apply-jenkins-agent-nodejs18-build apply-jenkins-agent-nodejs20-build apply-jenkins-agent-nodejs22-build start-jenkins-agent-nodejs18-build start-jenkins-agent-nodejs20-build start-jenkins-agent-nodejs22-build +install-jenkins-agent-nodejs: apply-jenkins-agent-nodejs18-build apply-jenkins-agent-nodejs20-build apply-jenkins-agent-nodejs22-build apply-jenkins-agent-nodejs24-build start-jenkins-agent-nodejs18-build start-jenkins-agent-nodejs20-build start-jenkins-agent-nodejs22-build start-jenkins-agent-nodejs24-build .PHONY: install-jenkins-agent-nodejs ## Update OpenShift resources related to Jenkins Node 18 agent image. @@ -72,6 +76,11 @@ apply-jenkins-agent-nodejs22-build: cd common/jenkins-agents/nodejs22/ocp-config && tailor apply --namespace $(ODS_NAMESPACE) .PHONY: apply-jenkins-agent-nodejs22-build +## Update OpenShift resources related to Jenkins Node 24 agent image. +apply-jenkins-agent-nodejs24-build: + cd common/jenkins-agents/nodejs24/ocp-config && tailor apply --namespace $(ODS_NAMESPACE) +.PHONY: apply-jenkins-agent-nodejs24-build + ## Start build of BuildConfig "jenkins-agent-nodejs18". start-jenkins-agent-nodejs18-build: oc -n $(ODS_NAMESPACE) start-build jenkins-agent-nodejs18 --follow @@ -87,6 +96,11 @@ start-jenkins-agent-nodejs22-build: oc -n $(ODS_NAMESPACE) start-build jenkins-agent-nodejs22 --follow .PHONY: start-jenkins-agent-nodejs22-build +## Start build of BuildConfig "jenkins-agent-nodejs24". +start-jenkins-agent-nodejs24-build: + oc -n $(ODS_NAMESPACE) start-build jenkins-agent-nodejs24 --follow +.PHONY: start-jenkins-agent-nodejs24-build + # JENKINS AGENT PYTHON ## Install or update Jenkins Python agent resources. install-jenkins-agent-python: apply-jenkins-agent-python-build start-jenkins-agent-python-build diff --git a/common/jenkins-agents/README.md b/common/jenkins-agents/README.md index 0b7e4e7b3..3c63f8fa0 100644 --- a/common/jenkins-agents/README.md +++ b/common/jenkins-agents/README.md @@ -20,6 +20,7 @@ The ODS [jenkins shared library](https://github.com/opendevstack/ods-jenkins-sha 3. [Node.js 18](nodejs18) 4. [Node.js 20](nodejs20) 5. [Node.js 22](nodejs22) +6. [Node.js 24](nodejs24) 6. [Python](python) 7. [Rust](rust) 8. [Scala & SBT](scala) diff --git a/common/jenkins-agents/nodejs24/docker/Dockerfile.ubi8 b/common/jenkins-agents/nodejs24/docker/Dockerfile.ubi8 new file mode 100644 index 000000000..d5112b2ed --- /dev/null +++ b/common/jenkins-agents/nodejs24/docker/Dockerfile.ubi8 @@ -0,0 +1,63 @@ +FROM opendevstackorg/ods-jenkins-agent-base-ubi8:latest + +# Labels consumed by Red Hat build service +LABEL com.redhat.component="jenkins-agent-nodejs-24-rhel7-container" \ + name="openshift4/jenkins-agent-nodejs-24-rhel7" \ + architecture="x86_64" \ + io.k8s.display-name="Jenkins Agent Nodejs" \ + io.k8s.description="The jenkins agent nodejs image has the nodejs tools on top of the jenkins agent base image." \ + io.openshift.tags="openshift,jenkins,agent,nodejs" \ + maintainer="openshift-dev-services+jenkins@redhat.com" + +ARG nexusUrl +ARG nexusAuth + +ENV NODEJS_VERSION=24 \ + YARN_VERSION=1.22.19 \ + NPM_CONFIG_PREFIX=$HOME/.npm-global \ + NPM_CONFIG_CAFILE=/etc/pki/ca-trust/extracted/pem/objsign-ca-bundle.pem \ + PATH=$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$HOME/node_modules/.bin/:$HOME/.npm-global/bin/:$PATH \ + LANG=en_US.UTF-8 \ + LC_ALL=en_US.UTF-8 + +COPY contrib/bin/configure-agent /usr/local/bin/configure-agent + +# Generate machine ID +RUN dbus-uuidgen > /etc/machine-id + +# Install NodeJS +RUN INSTALL_PKGS="nodejs nodejs-nodemon make gcc-c++" && \ + curl -fsSL https://rpm.nodesource.com/setup_${NODEJS_VERSION}.x | bash - && \ + yum install -y --setopt=tsflags=nodocs --disableplugin=subscription-manager $INSTALL_PKGS && \ + rpm -V $INSTALL_PKGS && \ + yum clean all -y + +# Install Yarn +# https://classic.yarnpkg.com/en/docs/install +RUN curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version $YARN_VERSION + +# Install Cypress dependencies +# https://docs.cypress.io/guides/getting-started/installing-cypress.html#System-requirements +COPY yum.repos.d/google-chrome.repo /etc/yum.repos.d/google-chrome.repo +COPY yum.repos.d/almalinux.repo /etc/yum.repos.d/almalinux.repo +COPY yum.repos.d/microsoft-edge.repo /etc/yum.repos.d/microsoft-edge.repo +RUN yum repolist \ + && yum install -y xorg-x11-server-Xvfb gtk2-devel gtk3-devel libnotify-devel GConf2 nss libXScrnSaver alsa-lib \ + && yum install -y --enablerepo google-chrome google-chrome-stable \ + && yum install -y --enablerepo edge microsoft-edge-stable \ + && yum clean all -y + +RUN npm config set registry=$nexusUrl/repository/npmjs/ && \ + npm config set //${nexusUrl#*://}/repository/npmjs/:_auth=$(echo -n $nexusAuth | base64) && \ + npm config set email=no-reply@opendevstack.org && \ + npm config set strict-ssl=true && \ + yarn config set registry $nexusUrl/repository/npmjs/ -g && \ + echo node version: $(node --version) && \ + echo npm version: $(npm --version) && \ + echo npx version: $(npx --version) && \ + echo yarn version: $(yarn --version) + +RUN chown -R 1001:0 $HOME && \ + chmod -R g+rwX $HOME + +USER 1001 diff --git a/common/jenkins-agents/nodejs24/docker/contrib/bin/configure-agent b/common/jenkins-agents/nodejs24/docker/contrib/bin/configure-agent new file mode 100644 index 000000000..02ab20288 --- /dev/null +++ b/common/jenkins-agents/nodejs24/docker/contrib/bin/configure-agent @@ -0,0 +1,63 @@ +#!/bin/bash + +# extract the different element of an url into a JSON structure +parse_url() { + # extract the protocol + proto="$(echo $1 | cut -f1 -d: )" + if [[ ! -z $proto ]] ; then + # remove the protocol + url="$(echo ${1/"$proto://"/})" + # extract the user (if any) + login="$(echo $url | grep @ | cut -d@ -f1)" + username="$(echo $login | cut -d: -f1)" + password="$(echo $login | cut -d: -f2)" + # extract the host + host_port="$(echo ${url/$login@/} | cut -d/ -f1) " + host="$(echo $host_port | cut -f1 -d:) " + + # by request - try to extract the port + port="$(echo $host_port | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g')" + # extract the uri (if any) + resource="/$(echo $url | grep / | cut -d/ -f2-)" + fi + echo -n "{ \"uri\": \"$1\" , \"url\": \"$url\" , \"proto\": \"$proto\" , \"login\": \"$login\" ," + echo " \"username\": \"$username\" , \"password\": \"$password\" , \"host\": \"$host\" , \"port\": \"$port\" }" +} + +get_npm_proxy_config(){ + local proto json + proto=$1 + json=$2 + username=$( echo $json | jq -r .username) + password=$( echo $json | jq -r .password) + host=$( echo $json | jq -r .host) + port=$( echo $json | jq -r .port) + proxy_url="$host:$port" + + if [ -n "$username" -a -n "$password" ]; then + proxy_url="$proto://$username:$password@$proxy_url" + fi + + echo $proxy_url +} + + +if [ -n "$http_proxy" ]; then + json=$( parse_url $http_proxy ) + proxy=$(get_npm_proxy_config http "$json") + npm -g config set proxy $proxy +fi + +if [ -n "$https_proxy" ]; then + json=$( parse_url $https_proxy ) + proxy=$(get_npm_proxy_config https "$json") + npm -g config set https_proxy $proxy +fi + +if [ -n "$no_proxy" ]; then + npm -g config set noproxy $no_proxy +fi + +if [ -n "$NPM_MIRROR_URL" ]; then + npm -g config set registry "$NPM_MIRROR_URL" +fi diff --git a/common/jenkins-agents/nodejs24/docker/yum.repos.d/almalinux.repo b/common/jenkins-agents/nodejs24/docker/yum.repos.d/almalinux.repo new file mode 100644 index 000000000..64ed35aa6 --- /dev/null +++ b/common/jenkins-agents/nodejs24/docker/yum.repos.d/almalinux.repo @@ -0,0 +1,13 @@ +[almalinux-baseos] +name=AlmaLinux-8-BaseOS +baseurl=https://repo.almalinux.org/almalinux/8/BaseOS/x86_64/os/ +enabled=1 +gpgcheck=1 +gpgkey=https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux + +[almalinux-appstream] +name=AlmaLinux-8-AppStream +baseurl=https://repo.almalinux.org/almalinux/8/AppStream/x86_64/os/ +enabled=1 +gpgcheck=1 +gpgkey=https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux diff --git a/common/jenkins-agents/nodejs24/docker/yum.repos.d/google-chrome.repo b/common/jenkins-agents/nodejs24/docker/yum.repos.d/google-chrome.repo new file mode 100644 index 000000000..96e7eece3 --- /dev/null +++ b/common/jenkins-agents/nodejs24/docker/yum.repos.d/google-chrome.repo @@ -0,0 +1,6 @@ +[google-chrome] +name=google-chrome +baseurl=https://dl.google.com/linux/chrome/rpm/stable/$basearch +enabled=0 +gpgcheck=1 +gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub diff --git a/common/jenkins-agents/nodejs24/docker/yum.repos.d/microsoft-edge.repo b/common/jenkins-agents/nodejs24/docker/yum.repos.d/microsoft-edge.repo new file mode 100644 index 000000000..a7b6d4800 --- /dev/null +++ b/common/jenkins-agents/nodejs24/docker/yum.repos.d/microsoft-edge.repo @@ -0,0 +1,6 @@ +[edge] +name=Microsoft Edge Browser +baseurl=https://packages.microsoft.com/yumrepos/edge +enabled=0 +gpgcheck=1 +gpgkey=https://packages.microsoft.com/keys/microsoft.asc diff --git a/common/jenkins-agents/nodejs24/ocp-config/Tailorfile b/common/jenkins-agents/nodejs24/ocp-config/Tailorfile new file mode 100644 index 000000000..45a373dce --- /dev/null +++ b/common/jenkins-agents/nodejs24/ocp-config/Tailorfile @@ -0,0 +1,5 @@ +namespace ods +selector app=jenkins-agent-nodejs24 +param-file ../../../../../ods-configuration/ods-core.env +ignore-unknown-parameters true +bc,is diff --git a/common/jenkins-agents/nodejs24/ocp-config/bc.yml b/common/jenkins-agents/nodejs24/ocp-config/bc.yml new file mode 100644 index 000000000..3338bd928 --- /dev/null +++ b/common/jenkins-agents/nodejs24/ocp-config/bc.yml @@ -0,0 +1,68 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: jenkins-agent-nodejs24 +parameters: +- name: ODS_BITBUCKET_PROJECT + description: Bitbucket project name. + value: opendevstack +- name: NEXUS_URL + required: true +- name: NEXUS_AUTH + required: true +- name: REPO_BASE + required: true +- name: ODS_IMAGE_TAG + required: true + value: latest +- name: ODS_GIT_REF + required: true +- name: JENKINS_AGENT_DOCKERFILE_PATH + value: Dockerfile.ubi8 + description: Dockerfile variant to use +objects: +- apiVersion: build.openshift.io/v1 + kind: BuildConfig + metadata: + name: jenkins-agent-nodejs24 + labels: + app: jenkins-agent-nodejs24 + spec: + failedBuildsHistoryLimit: 5 + nodeSelector: null + output: + to: + kind: ImageStreamTag + name: jenkins-agent-nodejs24:${ODS_IMAGE_TAG} + postCommit: {} + resources: + limits: + cpu: "1" + memory: "3Gi" + ephemeral-storage: "20Gi" + requests: + cpu: "200m" + memory: "2Gi" + ephemeral-storage: "15Gi" + runPolicy: Serial + source: + contextDir: common/jenkins-agents/nodejs24/docker + git: + ref: ${ODS_GIT_REF} + uri: ${REPO_BASE}/${ODS_BITBUCKET_PROJECT}/ods-quickstarters.git + sourceSecret: + name: cd-user-token + type: Git + strategy: + dockerStrategy: + buildArgs: + - name: nexusUrl + value: ${NEXUS_URL} + - name: nexusAuth + value: ${NEXUS_AUTH} + dockerfilePath: ${JENKINS_AGENT_DOCKERFILE_PATH} + from: + kind: ImageStreamTag + name: jenkins-agent-base:${ODS_IMAGE_TAG} + type: Docker + successfulBuildsHistoryLimit: 5 diff --git a/common/jenkins-agents/nodejs24/ocp-config/is.yml b/common/jenkins-agents/nodejs24/ocp-config/is.yml new file mode 100644 index 000000000..18efb37b9 --- /dev/null +++ b/common/jenkins-agents/nodejs24/ocp-config/is.yml @@ -0,0 +1,14 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: jenkins-agent-nodejs24 +objects: +- apiVersion: image.openshift.io/v1 + kind: ImageStream + metadata: + name: jenkins-agent-nodejs24 + labels: + app: jenkins-agent-nodejs24 + spec: + lookupPolicy: + local: false diff --git a/docs/modules/jenkins-agents/pages/nodejs24.adoc b/docs/modules/jenkins-agents/pages/nodejs24.adoc new file mode 100644 index 000000000..8acdbb378 --- /dev/null +++ b/docs/modules/jenkins-agents/pages/nodejs24.adoc @@ -0,0 +1,17 @@ += Node.js 24 - Jenkins agent + +== Introduction +This agent is used to build Node.js-based projects, through `npm` or `yarn`. + +The image is built in the global `ods` project and is named `jenkins-agent-nodejs24`. +It can be referenced in a `Jenkinsfile` with `ods/jenkins-agent-nodejs24`. + +IMPORTANT: Node.js 24 is an LTS release (see https://nodejs.org/) and will be supported while maintained upstream. + +== Features +1. Nexus configuration +2. HTTP proxy awareness +3. Yarn & Cypress dependencies pre-installed + +== Known limitations +n/a diff --git a/docs/modules/quickstarters/nav.adoc b/docs/modules/quickstarters/nav.adoc index 28de2a741..73963fe42 100644 --- a/docs/modules/quickstarters/nav.adoc +++ b/docs/modules/quickstarters/nav.adoc @@ -26,6 +26,7 @@ *** xref:jenkins-agents:nodejs18.adoc[Node.js 18] *** xref:jenkins-agents:nodejs20.adoc[Node.js 20] *** xref:jenkins-agents:nodejs22.adoc[Node.js 22] +*** xref:jenkins-agents:nodejs24.adoc[Node.js 24] *** xref:jenkins-agents:python.adoc[Python] *** xref:jenkins-agents:rust.adoc[Rust] *** xref:jenkins-agents:scala.adoc[Scala] From d68bf1622406f3048e2ea09406b3a06fdf49be16 Mon Sep 17 00:00:00 2001 From: brais <26645694+BraisVQ@users.noreply.github.com> Date: Tue, 28 Oct 2025 11:22:09 +0100 Subject: [PATCH 4/9] add gh action for custom e2e QS notification (#1137) --- .../pr-e2e-codeowners-notification.yml | 37 +++++++++++++++++++ CHANGELOG.md | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/pr-e2e-codeowners-notification.yml diff --git a/.github/workflows/pr-e2e-codeowners-notification.yml b/.github/workflows/pr-e2e-codeowners-notification.yml new file mode 100644 index 000000000..9cd498676 --- /dev/null +++ b/.github/workflows/pr-e2e-codeowners-notification.yml @@ -0,0 +1,37 @@ +name: PR Codeowners Notification + +on: + pull_request: + types: [opened, synchronize, reopened] + branches: + - '*.x' + +jobs: + notify-codeowners: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4.2.2 + + - name: Check for changes in CODEOWNERS paths + uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + owned_paths: + - 'e2e-cypress/**' + - 'e2e-spock-geb/**' + - 'e2e-etl-python/**' + + - name: Post notification comment + if: steps.filter.outputs.owned_paths == 'true' + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '⚠️ **Codeowners Notification**: Changes detected in paths owned by specific users. Please ensure the following reviewers are involved/notified:\n\n' + + '- @garcanam @roicarrera (for e2e-cypress, e2e-spock-geb, e2e-etl-python)\n' + }); diff --git a/CHANGELOG.md b/CHANGELOG.md index 232dcbd3b..a4f0fcc21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased ### Added - +- Added gh action for custom e2e QS notification ([#1137](https://github.com/opendevstack/ods-quickstarters/pull/1137)) - Added Node.js 24 Jenkins agent and related CI/Makefile/docs updates ([#1133]) (common/jenkins-agents/nodejs24) ([#1134](https://github.com/opendevstack/ods-quickstarters/pull/1134)) From 5bbfd1e162f5deb56ed2b0610e9d62d5e249b1ef Mon Sep 17 00:00:00 2001 From: gerardcl Date: Wed, 5 Nov 2025 09:25:08 +0100 Subject: [PATCH 5/9] Feat - Rust rustup dynamic toolchain selection enablement (#1120) --- .../continuous-integration-workflow.yml | 10 ++-- CHANGELOG.md | 5 +- be-rust-axum/Jenkinsfile.template | 23 +++++---- .../rust-template/.pre-commit-config.yaml | 4 +- be-rust-axum/rust-template/Cargo.toml | 47 +++++++++++++++---- be-rust-axum/rust-template/README.md | 10 ++++ .../rust-template/cargo-generate.toml | 2 + be-rust-axum/rust-template/metadata.yml | 2 +- .../rust-template/rust-toolchain.toml | 2 + be-rust-axum/rust-template/rustfmt.toml | 28 +++++++++-- be-rust-axum/rust-template/src/api/router.rs | 2 +- .../rust-template/src/api/routes/status.rs | 2 +- .../rust-template/src/config/settings.rs | 5 +- be-rust-axum/rust-template/src/main.rs | 2 +- .../rust-template/tests/status_test.rs | 2 +- .../testdata/golden/jenkins-build-stages.json | 4 -- .../rust/docker/Dockerfile.ubi8 | 19 ++++---- common/jenkins-agents/rust/ocp-config/bc.yml | 10 ++-- docs/modules/jenkins-agents/pages/rust.adoc | 35 ++++++++++---- .../quickstarters/pages/be-rust-axum.adoc | 19 ++++---- 20 files changed, 155 insertions(+), 78 deletions(-) create mode 100644 be-rust-axum/rust-template/cargo-generate.toml create mode 100644 be-rust-axum/rust-template/rust-toolchain.toml diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 586e030ef..8d73fddb2 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -124,12 +124,12 @@ jobs: working-directory: common/jenkins-agents/rust/docker run: | docker build --tag agent-rust-test-ubi8 --file Dockerfile.ubi8 \ - --build-arg rustVersion=1.83.0 \ + --build-arg rustVersion=1.88.0 \ --build-arg rustToolchain=x86_64-unknown-linux-gnu \ - --build-arg cargoNextestVersion=0.9.87 \ - --build-arg cargoLlvmCovVersion=0.6.15 \ - --build-arg cargoGenerateVersion=0.22.1 \ - --build-arg cargoDenyVersion=0.16.3 \ + --build-arg cargoNextestVersion=0.9.94 \ + --build-arg cargoLlvmCovVersion=0.6.16 \ + --build-arg cargoGenerateVersion=0.23.3 \ + --build-arg cargoDenyVersion=0.18.2 \ . jenkins-agent-terraform-2306-ubi8: diff --git a/CHANGELOG.md b/CHANGELOG.md index a4f0fcc21..f72ab9f57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ - Added gh action for custom e2e QS notification ([#1137](https://github.com/opendevstack/ods-quickstarters/pull/1137)) - Added Node.js 24 Jenkins agent and related CI/Makefile/docs updates ([#1133]) (common/jenkins-agents/nodejs24) ([#1134](https://github.com/opendevstack/ods-quickstarters/pull/1134)) - ### Changed - Upgrade with dynamic backend configuration of Azure quickstarter ([#1131](https://github.com/opendevstack/ods-quickstarters/pull/1131)) @@ -17,6 +16,10 @@ ### Changed - Use npm ci in e2e-cypress quickstarter & Better TypeScript support in e2e-cypress quickstarter ([#1114](https://github.com/opendevstack/ods-quickstarters/pull/1114)) - e2e-cypress get rid of additional chrome installation and switch to edge for pdf generation ([#1112](https://github.com/opendevstack/ods-quickstarters/pull/1112)) +- Fix for the integration of the Python Quickstarter and Zephyr ODS integration ([#1125](https://github.com/opendevstack/ods-quickstarters/pull/1125)) +- Rust QS flag to enable or disable cargo-deny for dependency linting, default to true ([#1107](https://github.com/opendevstack/ods-quickstarters/issues/1107)) +- Rust QS enable dynamic toolchain/version support ([#1119](https://github.com/opendevstack/ods-quickstarters/issues/1119)) +- Added e2e-cypress specific versioning ([#1128](https://github.com/opendevstack/ods-quickstarters/pull/1128)) ### Fixed diff --git a/be-rust-axum/Jenkinsfile.template b/be-rust-axum/Jenkinsfile.template index e56434569..00b402187 100644 --- a/be-rust-axum/Jenkinsfile.template +++ b/be-rust-axum/Jenkinsfile.template @@ -10,7 +10,7 @@ odsComponentPipeline( ] ) { context -> odsComponentFindOpenShiftImageOrElse(context) { - stageCI(context) + stageCI(context, false) // set `auditDependencies` to true if using cargo-deny odsComponentStageScanWithSonar(context) stageBuild(context) odsComponentStageBuildOpenShiftImage(context) @@ -27,7 +27,7 @@ def stageBuild(def context) { } } -def stageCI(def context) { +def stageCI(def context, def auditDependencies) { stage('Cargo Check') { sh """ cargo --version @@ -51,13 +51,15 @@ def stageCI(def context) { cargo clippy --message-format=json &> build/test-results/clippy/report.json """ } - stage('Cargo Deny') { - sh """ - mkdir -p build/test-results/deny - cargo deny --format json check &> build/test-results/deny/cargo-deny-report.json - cat build/test-results/deny/cargo-deny-report.json - """ - archiveArtifacts artifacts: 'build/test-results/deny/cargo-deny-report.json', fingerprint: true + if (auditDependencies) { + stage('Cargo Deny') { + sh """ + mkdir -p build/test-results/deny + cargo deny --format json check &> build/test-results/deny/cargo-deny-report.json + cat build/test-results/deny/cargo-deny-report.json + """ + archiveArtifacts artifacts: 'build/test-results/deny/cargo-deny-report.json', fingerprint: true + } } stage('Cargo Test') { sh """ @@ -65,6 +67,9 @@ def stageCI(def context) { mkdir -p build/test-results/test mkdir -p build/test-results/coverage + # If required, pre-install llvm-tools-preview to avoid interactive prompt when not using default Jenkins agent toolchain + # rustup component add llvm-tools-preview --toolchain ${RUST_VERSION} + # Tests with JUnit XML report, as defined in .config/nextest.toml, and coverage LLVM cov report cargo llvm-cov nextest --profile ci --lcov --output-path ./build/test-results/coverage/lcov.info cp -r target/nextest/ci/results.xml build/test-results/test diff --git a/be-rust-axum/rust-template/.pre-commit-config.yaml b/be-rust-axum/rust-template/.pre-commit-config.yaml index 1b31d29ca..24f4276c1 100644 --- a/be-rust-axum/rust-template/.pre-commit-config.yaml +++ b/be-rust-axum/rust-template/.pre-commit-config.yaml @@ -1,10 +1,10 @@ repos: - repo: https://github.com/gitleaks/gitleaks - rev: v8.22.1 + rev: v8.25.1 hooks: - id: gitleaks - repo: https://github.com/EmbarkStudios/cargo-deny - rev: 0.16.3 + rev: 0.18.2 hooks: - id: cargo-deny args: ["--all-features", "check"] # optionally modify the arguments for cargo-deny (default arguments shown here) diff --git a/be-rust-axum/rust-template/Cargo.toml b/be-rust-axum/rust-template/Cargo.toml index cd57a6b42..f0de2da4e 100644 --- a/be-rust-axum/rust-template/Cargo.toml +++ b/be-rust-axum/rust-template/Cargo.toml @@ -2,35 +2,37 @@ # Here the project-name (Rust project) is your ODS component name name = "{{project-name}}" version = "0.1.0" -edition = "2021" +edition = "2024" +rust-version = "1.88" description = "{{project-name}} component - from the OpenDevStack Rust QuickStarter." license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] # one can add more dependencies via cargo to Cargo.toml as shown next: cargo add axum -F axum/http2 -axum = { version = "0.8", features = ["http2"] } -tokio = { version = "1.40", features = ["rt-multi-thread", "macros", "signal"] } +axum = { version = "0.8", features = ["http2", "macros", "multipart"] } +tokio = { version = "1.44", features = ["rt-multi-thread", "macros", "signal"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -http = "1.1" +http = "1.3" http-body-util = "0.1" +# Highly encouraged to use URL encoding when working with any URI string +# urlencoding = "2.1" bytes = "1.7" thiserror = "2.0" tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["tracing", "env-filter"] } +tracing-subscriber = { version = "0.3", features = ["tracing", "env-filter", "json", "ansi", "fmt"] } lazy_static = "1.5" envy = "0.4" dotenvy = "0.15" -# Nice HTTP middlewares from Tower crate, to be added to router.rs. -# Uncomment as per need, check official docs. +# HTTP middlewares from Tower crate, to be added to router.rs. # tower-http = { version = "0.6", features = [ # "trace", # "compression-br", @@ -40,9 +42,36 @@ dotenvy = "0.15" # ] } # SQLx is the recommended safe and performant package to work with relational DBs like PostgreSQL; check official docs -# sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls", "postgres", "uuid", "json", "time", "macros", "migrate" ] } +# sqlx = { version = "0.8", features = [ +# "runtime-tokio", +# "tls-rustls-ring-native-roots", +# "postgres", +# "uuid", +# "json", +# "chrono", +# "macros", +# "migrate" +# ]} + +# OTEL; check official docs +# opentelemetry = { version = "0.29", features = ["trace", "metrics"] } +# opentelemetry_sdk = { version = "0.29", features = ["rt-tokio"] } +# opentelemetry-otlp = { version = "0.29", features = ["http-proto", "reqwest-client", "reqwest-rustls"] } +# opentelemetry-http = { version = "0.29" } +# opentelemetry-stdout = { version = "0.29", features = ["trace"] } +# opentelemetry-appender-tracing = { version = "0.29" } +# opentelemetry-semantic-conventions = { version = "0.29" } +# tracing-opentelemetry = { version = "0.30" } + +# OpenAPI; check official docs +# utoipa = { version = "5.3", features = ["axum_extras", "uuid", "chrono"] } +# utoipa-axum = "0.2" +# utoipa-swagger-ui = { version = "9.0", features = ["axum"] } +# # utoipa-redoc = { version = "5.0", features = ["axum"] } +# # utoipa-rapidoc = { version = "5.0", features = ["axum"] } +# # utoipa-scalar = { version = "0.2", features = ["axum"] } [dev-dependencies] -# mockito = "1.5" # mockito is recommended when requiring mocking network requests to either external or internal services +# mockito = "1.7" # mockito is recommended when requiring mocking network requests to either external or internal services temp-env = { version = "0.3", features = ["async_closure"] } # it is highly recommended the usage of cargo nextest run instead of cargo test; this way each test runs on its own process tower = { version = "0.5", features = ["util"] } diff --git a/be-rust-axum/rust-template/README.md b/be-rust-axum/rust-template/README.md index 312f57ee0..62a8211ec 100644 --- a/be-rust-axum/rust-template/README.md +++ b/be-rust-axum/rust-template/README.md @@ -2,6 +2,8 @@ The official OpenDevStack documentation for this QuickStarter can be found [here](https://www.opendevstack.org/ods-documentation/opendevstack/latest/quickstarters/be-rust-axum.html). +Check also the official docs of the [Jenkins Rust Agent](https://www.opendevstack.org/ods-documentation/opendevstack/latest/jenkins-agents/rust.html) this Quickstarter makes use of. There you can see the setup and tools it provides. + ## Pre-commit hooks This project uses [pre-commit](https://pre-commit.com). @@ -11,6 +13,14 @@ This project uses [pre-commit](https://pre-commit.com). pre-commit install ``` +The provided pre-commit hooks are: +- gitleaks (check for secrets) +- cargo-deny (dependency auditing, see/update `deny.toml` config file) +- cargo-fmt (formatter, see/update `rustfmt.toml` config file) +- cargo-clippy (linter) + +**NOTE**: the cargo hooks also run in Jenkins CICD, but cargo deny is disabled by default, see Jenkinsfile. + ## Adding caching in your CICD One can improve the build pipeline time by implementing a caching mechanism as shown next: diff --git a/be-rust-axum/rust-template/cargo-generate.toml b/be-rust-axum/rust-template/cargo-generate.toml new file mode 100644 index 000000000..a446997dd --- /dev/null +++ b/be-rust-axum/rust-template/cargo-generate.toml @@ -0,0 +1,2 @@ +[template] +exclude = ["chart/templates/*"] diff --git a/be-rust-axum/rust-template/metadata.yml b/be-rust-axum/rust-template/metadata.yml index 7002f343e..fba0aa1cf 100644 --- a/be-rust-axum/rust-template/metadata.yml +++ b/be-rust-axum/rust-template/metadata.yml @@ -1,6 +1,6 @@ --- name: Axum - Rust web framework -description: "Axum is an ergonomic and modular web framework built with Tokio, Tower, and Hyper; written in Rust. Technologies: Axum 0.8.x, Rust 1.83.x" +description: "Axum is an ergonomic and modular web framework built with Tokio, Tower, and Hyper; written in Rust. Technologies: Axum 0.8.x, Rust 1.88.x" supplier: https://github.com/tokio-rs/axum version: 4.x type: ods diff --git a/be-rust-axum/rust-template/rust-toolchain.toml b/be-rust-axum/rust-template/rust-toolchain.toml new file mode 100644 index 000000000..e88baf106 --- /dev/null +++ b/be-rust-axum/rust-template/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.88.0" diff --git a/be-rust-axum/rust-template/rustfmt.toml b/be-rust-axum/rust-template/rustfmt.toml index 10b47a0f3..f9a34f412 100644 --- a/be-rust-axum/rust-template/rustfmt.toml +++ b/be-rust-axum/rust-template/rustfmt.toml @@ -1,6 +1,24 @@ -# rustfmt configurations from master branch, check rustfmt version -# (cargo fmt --version). # https://github.com/rust-lang/rustfmt/blob/master/Configurations.md - -edition = "2021" -tab_spaces = 2 +# ───────────────────────────────────────────── +# 📦 Edition +edition = "2024" +# ───────────────────────────────────────────── +# 📚 Import organization +# group_imports = "StdExternalCrate" # Group: std, external crates, then local +# imports_granularity = "Module" # One use per module line (avoids merge conflicts) +reorder_imports = true # Ensure deterministic ordering +# ───────────────────────────────────────────── +# 📐 Formatting rules +use_field_init_shorthand = true # Use shorthand: { foo } instead of { foo: foo } +match_block_trailing_comma = true # Add trailing commas in multiline match arms +# trailing_comma = "Always" # Always use trailing commas in multiline +newline_style = "Unix" # Use \n (LF) for line endings +tab_spaces = 2 # Use 2 spaces per indentation level +max_width = 100 # Line length limit +# ───────────────────────────────────────────── +# 🧼 Style preferences +# normalize_doc_attributes = true # Normalize #[doc] to /// comments +# wrap_comments = true # Automatically wrap long doc/comments +# format_code_in_doc_comments = true # Format Rust code inside doc comments +# blank_lines_upper_bound = 1 # Avoid excessive vertical whitespace +# blank_lines_lower_bound = 0 # Allow compact block spacing when possible diff --git a/be-rust-axum/rust-template/src/api/router.rs b/be-rust-axum/rust-template/src/api/router.rs index 5e0d3b08f..1e33fed78 100644 --- a/be-rust-axum/rust-template/src/api/router.rs +++ b/be-rust-axum/rust-template/src/api/router.rs @@ -1,7 +1,7 @@ use axum::{Json, Router}; use http::StatusCode; use std::net::SocketAddr; -use tokio::signal::unix::{signal, SignalKind}; +use tokio::signal::unix::{SignalKind, signal}; use tracing::info; use crate::api::routes; diff --git a/be-rust-axum/rust-template/src/api/routes/status.rs b/be-rust-axum/rust-template/src/api/routes/status.rs index 5f23b213b..89481f0b3 100644 --- a/be-rust-axum/rust-template/src/api/routes/status.rs +++ b/be-rust-axum/rust-template/src/api/routes/status.rs @@ -1,4 +1,4 @@ -use axum::{routing::get, Json, Router}; +use axum::{Json, Router, routing::get}; use http::StatusCode; use tracing::debug; diff --git a/be-rust-axum/rust-template/src/config/settings.rs b/be-rust-axum/rust-template/src/config/settings.rs index 86af8c900..634f9aac5 100644 --- a/be-rust-axum/rust-template/src/config/settings.rs +++ b/be-rust-axum/rust-template/src/config/settings.rs @@ -30,10 +30,7 @@ impl Settings { pub fn new() -> Result { dotenv().ok(); - let settings = match envy::from_env::() { - Ok(config) => config, - Err(error) => return Err(error), - }; + let settings = envy::from_env::()?; Ok(settings) } } diff --git a/be-rust-axum/rust-template/src/main.rs b/be-rust-axum/rust-template/src/main.rs index 01a39d69c..f96ee6bec 100644 --- a/be-rust-axum/rust-template/src/main.rs +++ b/be-rust-axum/rust-template/src/main.rs @@ -2,7 +2,7 @@ use {{crate_name}}::api::router::serve; use {{crate_name}}::config::settings::SETTINGS; use std::net::SocketAddr; -use tracing_subscriber::{layer::SubscriberExt, Layer, Registry}; +use tracing_subscriber::{Layer, Registry, layer::SubscriberExt}; #[tokio::main] async fn main() { diff --git a/be-rust-axum/rust-template/tests/status_test.rs b/be-rust-axum/rust-template/tests/status_test.rs index 7d23bc7c5..d65afd982 100644 --- a/be-rust-axum/rust-template/tests/status_test.rs +++ b/be-rust-axum/rust-template/tests/status_test.rs @@ -4,7 +4,7 @@ use tower::ServiceExt; // for `call`, `oneshot`, and `ready` use {{crate_name}}::{api::router::app, models::status::Status}; mod common; -use common::{response_json, RequestBuilderExt}; +use common::{RequestBuilderExt, response_json}; #[tokio::test] async fn get_status_route_ok() { diff --git a/be-rust-axum/testdata/golden/jenkins-build-stages.json b/be-rust-axum/testdata/golden/jenkins-build-stages.json index 9335e6e90..0db2cef39 100644 --- a/be-rust-axum/testdata/golden/jenkins-build-stages.json +++ b/be-rust-axum/testdata/golden/jenkins-build-stages.json @@ -15,10 +15,6 @@ "stage": "Cargo Clippy", "status": "SUCCESS" }, - { - "stage": "Cargo Deny", - "status": "SUCCESS" - }, { "stage": "Cargo Test", "status": "SUCCESS" diff --git a/common/jenkins-agents/rust/docker/Dockerfile.ubi8 b/common/jenkins-agents/rust/docker/Dockerfile.ubi8 index cf206f3b2..21f5d10bd 100644 --- a/common/jenkins-agents/rust/docker/Dockerfile.ubi8 +++ b/common/jenkins-agents/rust/docker/Dockerfile.ubi8 @@ -14,26 +14,23 @@ ENV USER="rust-agent" ENV CARGO_NEXTEST_VERSION=${cargoNextestVersion} \ CARGO_LLVM_COV_VERSION=${cargoLlvmCovVersion} \ CARGO_GENERATE_VERSION=${cargoGenerateVersion} \ - CARGO_DENY_VERSION=${cargoDenyVersion} + CARGO_DENY_VERSION=${cargoDenyVersion} \ + RUST_VERSION=${rustVersion} RUN yum install -y binutils cpp gcc glibc-devel glibc-headers isl kernel-headers libasan libatomic \ libgomp libmpc libpkgconf libubsan libxcrypt-devel llvm-libs pkgconf pkgconf-m4 pkgconf-pkg-config \ openssl-devel cpan perl-IPC-Cmd && \ cpan install FindBin -RUN cd /tmp && \ - curl -LfSsO https://static.rust-lang.org/dist/rust-${rustVersion}-${rustToolchain}.tar.gz && \ - tar -xzf rust-${rustVersion}-${rustToolchain}.tar.gz && \ - rm -f rust-${rustVersion}-${rustToolchain}.tar.gz && \ - cd rust-${rustVersion}-${rustToolchain} && \ - ./install.sh && \ +RUN mkdir -p $HOME/.cargo/bin $HOME/.rustup && \ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \ + -y --profile complete --default-toolchain ${rustVersion} -t ${rustToolchain} && \ cargo -V && \ - mkdir -p $HOME/.cargo/bin && \ # Download binaries and install to $HOME/.cargo/bin curl --proto '=https' --tlsv1.2 -fsSL https://github.com/nextest-rs/nextest/releases/download/cargo-nextest-$CARGO_NEXTEST_VERSION/cargo-nextest-$CARGO_NEXTEST_VERSION-$rustToolchain.tar.gz | tar xzf - -C "$HOME/.cargo/bin" && \ curl --proto '=https' --tlsv1.2 -fsSL https://github.com/cargo-generate/cargo-generate/releases/download/v$CARGO_GENERATE_VERSION/cargo-generate-v$CARGO_GENERATE_VERSION-$rustToolchain.tar.gz | tar xzf - -C "$HOME/.cargo/bin" && \ # curl --proto '=https' --tlsv1.2 -fsSL https://github.com/taiki-e/cargo-llvm-cov/releases/download/v$CARGO_LLVM_COV_VERSION/cargo-llvm-cov-$rustToolchain.tar.gz | tar xzf - -C "$HOME/.cargo/bin" && \ - # cargo LLVM coverage crate is recommended to be compiled as it takes care to add the OS lib dependencies the proper way + # cargo LLVM coverage crate is recommended to be compiled as it takes care to add the OS lib dependencies the proper way cargo install cargo-llvm-cov --locked --version $CARGO_LLVM_COV_VERSION && \ # install cargo deny as release sources do not provide our rust toolchain target cargo install cargo-deny --locked --version $CARGO_DENY_VERSION && \ @@ -43,5 +40,5 @@ RUN cd /tmp && \ cargo generate --version && \ cargo deny --version -RUN chgrp -R 0 $HOME/.cargo && \ - chmod -R g=u $HOME/.cargo +RUN chgrp -R 0 $HOME/.cargo $HOME/.rustup && \ + chmod -R g=u $HOME/.cargo $HOME/.rustup diff --git a/common/jenkins-agents/rust/ocp-config/bc.yml b/common/jenkins-agents/rust/ocp-config/bc.yml index ab1f1f10a..b047284c0 100644 --- a/common/jenkins-agents/rust/ocp-config/bc.yml +++ b/common/jenkins-agents/rust/ocp-config/bc.yml @@ -19,7 +19,7 @@ parameters: - name: RUST_VERSION description: "The Rust version" required: true - value: "1.83.0" + value: "1.88.0" - name: RUST_TOOLCHAIN description: "The Rust target toolchain" required: true @@ -27,19 +27,19 @@ parameters: - name: CARGO_NEXTEST_VERSION description: "The Cargo Nextest testing framework version" required: true - value: "0.9.87" + value: "0.9.94" - name: CARGO_LLVM_COV_VERSION description: "The Cargo LLVM Coverage tool version" required: true - value: "0.6.15" + value: "0.6.16" - name: CARGO_GENERATE_VERSION description: "The Cargo Generate tool version" required: true - value: "0.22.1" + value: "0.23.3" - name: CARGO_DENY_VERSION description: "The Cargo Deny tool version" required: true - value: "0.16.3" + value: "0.18.2" objects: - apiVersion: build.openshift.io/v1 kind: BuildConfig diff --git a/docs/modules/jenkins-agents/pages/rust.adoc b/docs/modules/jenkins-agents/pages/rust.adoc index 2d5420dee..2a3f76590 100644 --- a/docs/modules/jenkins-agents/pages/rust.adoc +++ b/docs/modules/jenkins-agents/pages/rust.adoc @@ -6,18 +6,33 @@ This agent is used to build / execute Rust code and tools. The image is built in the global `ods` project and is named `jenkins-agent-rust`. It can be referenced in a `Jenkinsfile` with `ods/jenkins-agent-rust`. -Minimum Supported Rust Version (MSRV) **1.83**. +== Default MSRV and Edition +Rust installation is managed by `rustup`, which offers a convenient way to load and manage Rust versions automatically. -**NOTE**: Current Rust edition in use is **2021**. See https://doc.rust-lang.org/edition-guide/introduction.html[Rust Edition Guide] for further information. +And, the version defined in the `rust-toolchain` file is the one that the Jenkins Agent will use (even if it is not the default). That file MUST be in the root of the projects' repository. + +Default Minimum Supported Rust Version (MSRV) **1.88**. + +Default Rust edition in use is **2024**. See https://doc.rust-lang.org/edition-guide/introduction.html[Rust Edition Guide] for further information. == Features -1. https://www.rust-lang.org/[Rust] -2. https://github.com/tokio-rs/axum/[Axum] -3. https://doc.rust-lang.org/cargo/[Cargo] -4. https://nexte.st/[Nextest] -5. https://github.com/taiki-e/cargo-llvm-cov[LLVM Coverage] -6. https://cargo-generate.github.io/cargo-generate/[Generate] +The image contains the following tools: + +* Rust toolchain (https://www.rust-lang.org/tools/install[Rustup]) + +* Rust compiler (https://doc.rust-lang.org/rustc/[Rustc]) + +* Rust package manager (https://doc.rust-lang.org/cargo/[Cargo]) + +* Rust Next generation test runner (https://nexte.st/[Cargo Nextest]) + +* Rust code coverage (https://github.com/taiki-e/cargo-llvm-cov[Cargo LLVM Coverage]) + +* Rust code generation (https://cargo-generate.github.io/cargo-generate/[Cargo Generate]) + +* Rust project's dependency graph linter (https://embarkstudios.github.io/cargo-deny/[Cargo Deny]) + +**Do you miss any tool?** Open a https://github.com/opendevstack/ods-quickstarters/issues/new?template=feature_request.md[`feature request`] in the `ods-quickstarters` GitHub repository. == Known limitations -No special HTTP Proxy configuration. -Fixed Rust compiler version (although dependending on specific project's dependencies, it can build on lower or higher Minor **Rust** versions from **1.83.x**). +By default, no Nexus repository/registry nor mirror available for Rust packages and dependencies (only available in https://help.sonatype.com/en/rust-cargo.html[Nexus Pro from version 3.73.0] and/or https://help.sonatype.com/en/ce-onboarding.html[Nexus Community from version 3.77.0]). If not having Nexus Pro v3.37.0+, one can use git repositories or local file system to store and reference Rust packages and dependencies. diff --git a/docs/modules/quickstarters/pages/be-rust-axum.adoc b/docs/modules/quickstarters/pages/be-rust-axum.adoc index b606f764b..9b8f360c9 100644 --- a/docs/modules/quickstarters/pages/be-rust-axum.adoc +++ b/docs/modules/quickstarters/pages/be-rust-axum.adoc @@ -7,13 +7,13 @@ For Database/Postgres integration it is recommended to use https://github.com/la == Purpose of this quickstarter -This is a Rust project with a common Rust project folder and files structure, with its `main.rs` file for the final binary to be built, and that makes use of the `lib.rs` file, which exposes the crates (AKA modules or packages) of the project (where the business logic happens). Similar to Python project structures. +This is a Rust project with a common Rust project folder and files structure, with its `main.rs` file for the final binary to be built, and that makes use of the `lib.rs` file, which exposes the crates (AKA modules or packages) of the project (where the business logic happens). Similar to Python project structures. One could leverage the use of https://doc.rust-lang.org/cargo/reference/workspaces.html[workspaces] to split the project into multiple crates, but this is not the default behavior of this quickstarter. The quickstarter comes with a simple API-server example written in https://www.rust-lang.org/[Rust] and using the https://github.com/tokio-rs/axum/[Axum] web framework. The package allows to easily build a Rust project, using different Rust crates (packages). It contains the basic setup for Docker, Jenkins, SonarQube and OpenShift. -**NOTE** The project can be also extended to build other types of solutions with, for example, https://www.rust-lang.org/what/wasm/[WASM] or https://esp-rs.github.io/book/introduction.html[IoT]. +**NOTE** The project can be also extended to build other types of solutions like, for example, https://www.rust-lang.org/what/wasm/[WASM] or https://esp-rs.github.io/book/introduction.html[IoT]. == What files / architecture is generated? @@ -63,16 +63,19 @@ It contains the basic setup for Docker, Jenkins, SonarQube and OpenShift. ├── 📚 README.md - The README file ├── 📄 release-manager.yml - Configuration file for the Release Manager ├── 📄 rustfmt.toml - The Rust formatter configuration file (for cargo fmt) +├── 📄 rust-toolchain - Making sure teams and CICD use the same Rust toolchain for this project └── 📄 sonar-project.properties - This file contains SonarQube configuration settings ---- +**IMPORTANT**: The Jenkins Rust Agent is using `rustup` to manage the Rust toolchain, so it is important to keep the `rust-toolchain` file up to date, in the root of the project. This file should contain the version of Rust in use for the project. The agent will automatically load (if not being the default at the moment) and use this version when building the project. + == Frameworks used -* https://www.rust-lang.org/[Rust 1.83.x] +* https://www.rust-lang.org/[Rust 1.88.x] * https://github.com/tokio-rs/axum[Axum 0.8.x] * https://nexte.st/[Nextest 0.9.x] * https://github.com/taiki-e/cargo-llvm-cov/[LLVM coverage 0.6.x] -* https://github.com/EmbarkStudios/cargo-deny/[Deny 0.16.x] +* https://github.com/EmbarkStudios/cargo-deny/[Deny 0.18.x] == Usage - how do you start after you provisioned this quickstarter @@ -123,7 +126,7 @@ cargo install cargo-deny # ... ---- -**NOTE** For an extended list of awesome `cargo` extensions and Rust tools check https://blessed.rs/crates/[here] and https://github.com/rust-unofficial/awesome-rust/[here]. +**NOTE** For an extended list of awesome `cargo` extensions and Rust tools check https://blessed.rs/[here] and https://github.com/rust-unofficial/awesome-rust/[here]. === Metadata @@ -190,11 +193,11 @@ The Jenkinsfile is provisioned with this quick starter to ease CI/CD process. In include::partials$secret-scanning-with-gitleaks.adoc -== Builder agent used +== Builder agent used by the Jenkins CICD This quickstarter uses https://www.opendevstack.org/ods-documentation/opendevstack/latest/jenkins-agents/rust.html[Rust] Jenkins builder agent. -**NOTE**: The ODS Jenkins Rust Agent supports Rust versions 1.83.x, and depending on project's dependencies it can also handle Minor Rust versions above and below the supported one. +**IMPORTANT**: By using `rustup` to manage the Rust toolchain, one can easily switch between different versions of Rust and toolchain as needed. This is particularly useful for projects that require specific versions of Rust or when working with multiple projects that have different requirements. See the docker spec and openshift templates https://github.com/opendevstack/ods-quickstarters/tree/master/common/jenkins-agents/rust[here]. @@ -209,7 +212,7 @@ The following main Rust technologies are in use when running this boilerplate: == Known limitations -First of all, please, let us know if you find any limitation or issue to comment on, thanks! +If any, please, open a https://github.com/opendevstack/ods-quickstarters/issues/new?template=feature_request.md[`feature request`] or https://github.com/opendevstack/ods-quickstarters/issues/new?template=bug_report.md[bug report] in the `ods-quickstarters` GitHub repository. === Building with OpenSSL crate or using alternatives === From 1d2626d8423c081ff3ca4a3516b06fd477eb4986 Mon Sep 17 00:00:00 2001 From: brais <26645694+BraisVQ@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:26:17 +0100 Subject: [PATCH 6/9] Fix changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f72ab9f57..b821c40e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ### Changed - Upgrade with dynamic backend configuration of Azure quickstarter ([#1131](https://github.com/opendevstack/ods-quickstarters/pull/1131)) +- Rust QS flag to enable or disable cargo-deny for dependency linting, default to true ([#1107](https://github.com/opendevstack/ods-quickstarters/issues/1107)) +- Rust QS enable dynamic toolchain/version support ([#1119](https://github.com/opendevstack/ods-quickstarters/issues/1119)) ### Fixed @@ -17,8 +19,6 @@ - Use npm ci in e2e-cypress quickstarter & Better TypeScript support in e2e-cypress quickstarter ([#1114](https://github.com/opendevstack/ods-quickstarters/pull/1114)) - e2e-cypress get rid of additional chrome installation and switch to edge for pdf generation ([#1112](https://github.com/opendevstack/ods-quickstarters/pull/1112)) - Fix for the integration of the Python Quickstarter and Zephyr ODS integration ([#1125](https://github.com/opendevstack/ods-quickstarters/pull/1125)) -- Rust QS flag to enable or disable cargo-deny for dependency linting, default to true ([#1107](https://github.com/opendevstack/ods-quickstarters/issues/1107)) -- Rust QS enable dynamic toolchain/version support ([#1119](https://github.com/opendevstack/ods-quickstarters/issues/1119)) - Added e2e-cypress specific versioning ([#1128](https://github.com/opendevstack/ods-quickstarters/pull/1128)) ### Fixed From 6f87eb395f50c2d950ec8342fe7d98f639ef0d27 Mon Sep 17 00:00:00 2001 From: Segfault16 Date: Fri, 14 Nov 2025 11:32:39 +0100 Subject: [PATCH 7/9] Docker plain helm chart (#1035) --- CHANGELOG.md | 1 + docker-plain/Chart.yaml.template | 24 + docker-plain/Jenkinsfile | 15 +- docker-plain/Jenkinsfile.template | 8 +- docker-plain/files/README.md | 60 +- docker-plain/files/chart/.helmignore | 23 + docker-plain/files/chart/Chart.yaml | 26 + docker-plain/files/chart/templates/NOTES.txt | 10 + .../files/chart/templates/_affinity.tpl | 51 ++ .../files/chart/templates/_helpers.tpl | 62 ++ docker-plain/files/chart/templates/_image.tpl | 19 + .../files/chart/templates/_labels.tpl | 11 + .../files/chart/templates/deployment.yaml | 68 +++ docker-plain/files/chart/templates/hpa.yaml | 23 + .../files/chart/templates/ingress.yaml | 51 ++ .../files/chart/templates/service.yaml | 17 + .../files/chart/templates/serviceaccount.yaml | 13 + .../templates/tests/test-connection.yaml | 15 + docker-plain/files/chart/values.dev.yaml | 1 + docker-plain/files/chart/values.prod.yaml | 3 + docker-plain/files/chart/values.schema.json | 552 ++++++++++++++++++ docker-plain/files/chart/values.test.yaml | 3 + docker-plain/files/chart/values.yaml | 131 +++++ docker-plain/files/docker/Dockerfile | 19 +- docker-plain/files/docker/default.conf | 24 + docker-plain/testdata/steps.yml | 5 - 26 files changed, 1209 insertions(+), 26 deletions(-) create mode 100644 docker-plain/Chart.yaml.template create mode 100644 docker-plain/files/chart/.helmignore create mode 100644 docker-plain/files/chart/Chart.yaml create mode 100644 docker-plain/files/chart/templates/NOTES.txt create mode 100644 docker-plain/files/chart/templates/_affinity.tpl create mode 100644 docker-plain/files/chart/templates/_helpers.tpl create mode 100644 docker-plain/files/chart/templates/_image.tpl create mode 100644 docker-plain/files/chart/templates/_labels.tpl create mode 100644 docker-plain/files/chart/templates/deployment.yaml create mode 100644 docker-plain/files/chart/templates/hpa.yaml create mode 100644 docker-plain/files/chart/templates/ingress.yaml create mode 100644 docker-plain/files/chart/templates/service.yaml create mode 100644 docker-plain/files/chart/templates/serviceaccount.yaml create mode 100644 docker-plain/files/chart/templates/tests/test-connection.yaml create mode 100644 docker-plain/files/chart/values.dev.yaml create mode 100644 docker-plain/files/chart/values.prod.yaml create mode 100644 docker-plain/files/chart/values.schema.json create mode 100644 docker-plain/files/chart/values.test.yaml create mode 100644 docker-plain/files/chart/values.yaml create mode 100644 docker-plain/files/docker/default.conf diff --git a/CHANGELOG.md b/CHANGELOG.md index b821c40e8..f5b50c72a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ ## [4.8.0] - 2025-4-10 ### Changed +- Add Helm Chart to Docker Plain Quickstarter ([#1035](https://github.com/opendevstack/ods-quickstarters/pull/1035)) - Update CODEOWNERS ([#1108](https://github.com/opendevstack/ods-quickstarters/issues/1108)) - Fix Nginx related Quickstarters worker processes value to 1 as default ([#1092](https://github.com/opendevstack/ods-quickstarters/issues/1092)) - Set Request and Limits with Ephemeral storage and Make use of Nexus in terraform agents ([#1104](https://github.com/opendevstack/ods-quickstarters/pull/1104)) 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..deea5bdf2 100644 --- a/docker-plain/Jenkinsfile.template +++ b/docker-plain/Jenkinsfile.template @@ -20,7 +20,13 @@ odsComponentPipeline( */ odsComponentStageBuildOpenShiftImage(context) } - odsComponentStageRolloutOpenShiftDeployment(context) + def releaseName = context.componentId // can be customized as needed, the value provided here will be used as the release name for the installation + def componentId = context.componentId // needs to match name in Chart.yaml + odsComponentStageRolloutOpenShiftDeployment(context, [ + 'selector': "app.kubernetes.io/instance=${releaseName},app.kubernetes.io/name=${componentId}", + 'helmEnvBasedValuesFiles': ["values.env.yaml"], + 'helmReleaseName': releaseName + ]) } 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/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..d5905eee3 --- /dev/null +++ b/docker-plain/files/chart/values.yaml @@ -0,0 +1,131 @@ +## 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 +## Before enabling check the official ODS documentation about replicate support: https://www.opendevstack.org/ods-documentation/opendevstack/latest/jenkins-shared-library/orchestration-pipeline.html#_known_limitations +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 + ephemeral-storage: 1Mi + requests: + cpu: 10m + memory: 16Mi + ephemeral-storage: 1Mi + +## Before enabling check the official ODS documentation about replicate support: https://www.opendevstack.org/ods-documentation/opendevstack/latest/jenkins-shared-library/orchestration-pipeline.html#_known_limitations +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 1 + 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 b6704a397..4c85be11e 100644 --- a/docker-plain/files/docker/Dockerfile +++ b/docker-plain/files/docker/Dockerfile @@ -1,16 +1,7 @@ -# add /overwrite FROM with your base image, and do whatever you like here :) -FROM registry.access.redhat.com/ubi9/ubi-minimal:latest +# this is a sample Dockerfile with nginx as example, you can use it as a reference to create your own custom image +FROM public.ecr.aws/nginx/nginx-unprivileged:alpine-slim -RUN echo "Building simple container" - -USER root - -RUN microdnf -y update && \ - microdnf clean all - -USER 1001 - -EXPOSE 8080 - -CMD ["sh", "-c", "while true; do echo 'Container running on port 8080'; sleep 5; done"] +ENV NGINX_ENTRYPOINT_WORKER_PROCESSES_AUTOTUNE=set +# nginx config file +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..d5a71cc64 --- /dev/null +++ b/docker-plain/files/docker/default.conf @@ -0,0 +1,24 @@ +# config file for nginx, this file can be removed if you don't want to use nginx + +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}}" From aed42756491af626fa6847e9adafb75fce98015a Mon Sep 17 00:00:00 2001 From: brais <26645694+BraisVQ@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:27:51 +0100 Subject: [PATCH 8/9] Fix parameter name for Spring Boot version and update to 4.0.0 (#1138) --- CHANGELOG.md | 2 +- be-java-springboot/Jenkinsfile | 31 ++++++++++--------- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../pages/be-java-springboot.adoc | 10 +++--- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b50c72a..9285edcb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ - Rust QS enable dynamic toolchain/version support ([#1119](https://github.com/opendevstack/ods-quickstarters/issues/1119)) ### Fixed - +- Fix Springboot version parameter ([#1138](https://github.com/opendevstack/ods-quickstarters/issues/1138)) ## [4.10.0] - 2025-10-13 ### Changed diff --git a/be-java-springboot/Jenkinsfile b/be-java-springboot/Jenkinsfile index cc2ee64f3..db5fc4e6a 100644 --- a/be-java-springboot/Jenkinsfile +++ b/be-java-springboot/Jenkinsfile @@ -21,22 +21,25 @@ odsQuickstarterPipeline( stage('Build spring project') { dir(context.targetDir) { - def springBootVersion = '3.3.3' + def springBootVersion = '4.0.0' echo "--- create spring boot (v${springBootVersion}) project via spring initializr ---" - sh "curl https://start.spring.io/starter.tgz \ - -d type=gradle-project \ - -d language=java \ - -d dependencies='web,data-rest,data-jpa,h2,security,devtools' \ - -d platformVersion=${springBootVersion} \ - -d packaging=jar \ - -d jvmVersion=${javaVersion} \ - -d groupId=${context.projectId} \ - -d artifactId=${context.componentId} \ - -d name=${context.componentId} \ - -d description='OpenDevStack%20Demo%20Project%20for%20Spring%20Boot' \ - -d packageName='${context.packageName}' \ - | tar -zxvf - " + def springInitializrUrl = "https://start.spring.io/starter.tgz?" + + "type=gradle-project&" + + "language=java&" + + "dependencies=web,data-rest,data-jpa,h2,security,devtools&" + + "bootVersion=${springBootVersion}&" + + "packaging=jar&" + + "jvmVersion=${javaVersion}&" + + "groupId=${context.projectId}&" + + "artifactId=${context.componentId}&" + + "name=${context.componentId}&" + + "description=OpenDevStack%20Demo%20Project%20for%20Spring%20Boot&" + + "packageName=${context.packageName}" + + sh """ + curl -L "${springInitializrUrl}" | tar -xzvf - + """ echo "--- configure application.properties ---" diff --git a/be-java-springboot/files/gradle/wrapper/gradle-wrapper.properties b/be-java-springboot/files/gradle/wrapper/gradle-wrapper.properties index e1adfb493..ac57dd155 100644 --- a/be-java-springboot/files/gradle/wrapper/gradle-wrapper.properties +++ b/be-java-springboot/files/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/docs/modules/quickstarters/pages/be-java-springboot.adoc b/docs/modules/quickstarters/pages/be-java-springboot.adoc index 2fca904ed..25d05639d 100644 --- a/docs/modules/quickstarters/pages/be-java-springboot.adoc +++ b/docs/modules/quickstarters/pages/be-java-springboot.adoc @@ -12,9 +12,9 @@ The quickstarter uses the spring boot project generator service hosted by Spring This is implemented in the quickstarter jenkins build script called `Jenkinsfile` (open it to understand the internal of the code generation of this quickstarter). -When provisioning this quickstarter in the provisioning app a spring boot project based on version `3.3.3` will be generated and pushed to your git repository. +When provisioning this quickstarter in the provisioning app a spring boot project based on version `4.0.0` will be generated and pushed to your git repository. -The generated project requires `java 21` and includes the required gradle build and wrapper artifact based on gradle version `8.10`. +The generated project requires `java 21` and includes the required gradle build and wrapper artifact based on gradle version `9.2.1`. == Project Structure @@ -111,7 +111,7 @@ version: 1.0.1 type: ods role: backend runtime: spring-boot -runtimeVersion: 3.3.3 +runtimeVersion: 4.0.0 ``` == How this quickstarter is built thru jenkins @@ -149,9 +149,9 @@ The default version is `java 17`. This makes the agent backwards compatible. If you are migrating an ODS-based spring boot project to `java 21` following instructions provide a some guidance about the typical steps to achieve this: - change the agent version as indicated in https://github.com/opendevstack/ods-quickstarters/tree/master/common/jenkins-agents/jdk[jdk builder agent] -- upgrade the gradle wrapper to version `8.10` running this gradle command at the root folder of your spring project: +- upgrade the gradle wrapper to version `9.2.1` running this gradle command at the root folder of your spring project: ``` -gradle wrapper --gradle-version 8.10 --distribution-type bin +gradle wrapper --gradle-version 9.2.1 --distribution-type bin ``` NOTE: this command use just `gradle` instead of the wrapper. From 8dc07fc9721947b918cc2bfdc55e0f818f6e5342 Mon Sep 17 00:00:00 2001 From: brais <26645694+BraisVQ@users.noreply.github.com> Date: Wed, 3 Dec 2025 09:47:51 +0100 Subject: [PATCH 9/9] Update CHANGELOG for version 4.11.0 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9285edcb7..6b858de59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## Unreleased +### Added + +### Changed + +### Fixed + +## [4.11.0] - 2025-12-03 + ### Added - Added gh action for custom e2e QS notification ([#1137](https://github.com/opendevstack/ods-quickstarters/pull/1137)) - Added Node.js 24 Jenkins agent and related CI/Makefile/docs updates ([#1133]) (common/jenkins-agents/nodejs24) ([#1134](https://github.com/opendevstack/ods-quickstarters/pull/1134)) @@ -10,6 +18,7 @@ - Upgrade with dynamic backend configuration of Azure quickstarter ([#1131](https://github.com/opendevstack/ods-quickstarters/pull/1131)) - Rust QS flag to enable or disable cargo-deny for dependency linting, default to true ([#1107](https://github.com/opendevstack/ods-quickstarters/issues/1107)) - Rust QS enable dynamic toolchain/version support ([#1119](https://github.com/opendevstack/ods-quickstarters/issues/1119)) +- Docker Plain Helm chart ([#1135](https://github.com/opendevstack/ods-quickstarters/pull/1135)) ### Fixed - Fix Springboot version parameter ([#1138](https://github.com/opendevstack/ods-quickstarters/issues/1138))