blob: 7e8fbc46bbc430952385ec18e72a0e20b371251a [file] [log] [blame]
garciadeblas57c12492025-12-23 09:48:52 +01001/*
2 Licensed under the Apache License, Version 2.0 (the "License");
3 you may not use this file except in compliance with the License.
4 You may obtain a copy of the License at
5
6 http://www.apache.org/licenses/LICENSE-2.0
7
8 Unless required by applicable law or agreed to in writing, software
9 distributed under the License is distributed on an "AS IS" BASIS,
10 WITHOUT WARRANTIES OR CONDITIONS OF ANY, either express or
11 implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14*/
15
garciadeblasafa3db92025-12-26 10:39:20 +010016def DEFAULT_MODULE_NAME = 'lcm'
17
garciadeblas57c12492025-12-23 09:48:52 +010018pipeline {
garciadeblasafa3db92025-12-26 10:39:20 +010019 agent { label 'pool' }
20 options { disableConcurrentBuilds() }
21 parameters {
22 // Core Gerrit / multibranch inputs
23 string(name: 'GERRIT_BRANCH', defaultValue: env.BRANCH_NAME ?: 'master', description: '')
24 string(name: 'GERRIT_PROJECT', defaultValue: 'osm/LCM', description: '')
25 string(name: 'GERRIT_REFSPEC', defaultValue: env.GERRIT_REFSPEC ?: '', description: '')
26 string(name: 'GERRIT_PATCHSET_REVISION', defaultValue: env.GERRIT_PATCHSET_REVISION ?: '', description: '')
27 string(name: 'PROJECT_URL_PREFIX', defaultValue: 'https://osm.etsi.org/gerrit', description: '')
28 string(name: 'ARTIFACTORY_SERVER', defaultValue: 'artifactory-osm', description: '')
29 string(name: 'DOCKER_ARGS', defaultValue: '', description: 'Extra docker args for docker run')
30
31 // Stage 3 parameters (mirroring central ci_stage_3.groovy)
32 // Core installer/E2E toggles
33 string(name: 'DOCKER_TAG', defaultValue: 'testing-daily', description: 'Tests image tag for opensourcemano/tests')
34 string(name: 'INSTALLER', defaultValue: 'Default', description: '')
35 string(name: 'OPENSTACK_BASE_IMAGE', defaultValue: 'ubuntu22.04', description: '')
36 string(name: 'OPENSTACK_OSM_FLAVOR', defaultValue: 'osm.sanity', description: '')
37
38 booleanParam(name: 'DO_BUILD', defaultValue: true, description: '')
39 booleanParam(name: 'DO_INSTALL', defaultValue: true, description: '')
40 booleanParam(name: 'DO_DOCKERPUSH', defaultValue: true, description: '')
41 booleanParam(name: 'DO_ROBOT', defaultValue: true, description: '')
42 string(name: 'ROBOT_TAG_NAME', defaultValue: 'sanity', description: 'Robot tag (sanity/regression/daily)')
43 string(name: 'ROBOT_PASS_THRESHOLD', defaultValue: '100.0', description: 'Pass threshold (%)')
44 string(name: 'ROBOT_UNSTABLE_THRESHOLD', defaultValue: '80.0', description: 'Unstable threshold (%)')
45 string(name: 'MODULE_NAME', defaultValue: 'LCM', description: 'Name of the module under test')
46
47 // Paths and configs
48 string(name: 'KUBECONFIG', defaultValue: '/home/jenkins/hive/kubeconfig.yaml', description: '')
49 string(name: 'CLOUDS', defaultValue: '/home/jenkins/hive/clouds.yaml', description: '')
50 string(name: 'ROBOT_VIM', defaultValue: '/home/jenkins/hive/robot-systest.cfg', description: '')
51 string(name: 'ROBOT_PORT_MAPPING_VIM', defaultValue: '/home/jenkins/hive/port-mapping-etsi-vim.yaml', description: '')
52 string(name: 'PROMETHEUS_CONFIG_VIM', defaultValue: '/home/jenkins/hive/etsi-vim-prometheus.json', description: '')
53 string(name: 'HIVE_VIM_1', defaultValue: '/home/jenkins/hive/openstack-etsi.rc', description: '')
54
55 // Feature flags and saves
56 booleanParam(name: 'TRY_JUJU_INSTALLATION', defaultValue: true, description: '')
57 booleanParam(name: 'TRY_OLD_SERVICE_ASSURANCE', defaultValue: false, description: '')
58 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', defaultValue: false, description: '')
59 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', defaultValue: false, description: '')
60 booleanParam(name: 'SAVE_ARTIFACTS_ON_SMOKE_SUCCESS', defaultValue: true, description: '')
61 booleanParam(name: 'SAVE_ARTIFACTS_OVERRIDE', defaultValue: false, description: '')
62
63 // Optional publishing/flow controls
64 string(name: 'GPG_KEY_NAME', defaultValue: 'OSMETSI', description: '')
65 string(name: 'RELEASE', defaultValue: 'release', description: '')
66 string(name: 'REPO_DISTRO', defaultValue: 'unstable', description: '')
67 string(name: 'REPO_KEY_NAME', defaultValue: 'pubkey.asc', description: '')
68 string(name: 'COMMIT_ID', defaultValue: '', description: '')
69 string(name: 'UPSTREAM_JOB_NAME', defaultValue: '', description: '')
70 string(name: 'UPSTREAM_JOB_NUMBER', defaultValue: '', description: '')
71 string(name: 'UPSTREAM_SUFFIX', defaultValue: '-stage_2', description: '')
72 string(name: 'DOWNSTREAM_STAGE_NAME', defaultValue: 'osm-stage_4', description: '')
73 // Downstream params from LCM/Jenkinsfile NEW PIPELINE
74 booleanParam(name: 'TEST_INSTALL', defaultValue: false, description: 'Enable Stage 3/E2E in future')
75 string(name: 'IMAGENAME', defaultValue: 'opensourcemano/lcm', description: 'Image name for publish (reserved)')
76 }
77 environment {
78 MDG = "${params.GERRIT_PROJECT?.contains('/') ? params.GERRIT_PROJECT.split('/')[1] : params.GERRIT_PROJECT}"
79 CONTAINER_NAME = "${params.GERRIT_PROJECT}-${params.GERRIT_BRANCH}".toLowerCase()
80 TEST_IMAGE = 'overdrive3000/tox-osm:v1.6'
81 DOCKER_REGISTRY = 'osm.etsi.org:5050/devops/cicd/'
82 }
83 stages {
84 stage('Prepare') { steps { sh 'env' } }
85
86 stage('Checkout') {
87 steps {
88 checkout scm
89 script {
90 sh "git fetch --tags"
91 if (params.GERRIT_REFSPEC?.trim()) { sh "git fetch origin ${params.GERRIT_REFSPEC}" }
92 if (params.GERRIT_PATCHSET_REVISION?.trim()) { sh "git checkout -f ${params.GERRIT_PATCHSET_REVISION}" }
93 sh "sudo git clean -dfx || git clean -dfx"
garciadeblas57c12492025-12-23 09:48:52 +010094 }
garciadeblasafa3db92025-12-26 10:39:20 +010095 }
garciadeblas57c12492025-12-23 09:48:52 +010096 }
garciadeblasafa3db92025-12-26 10:39:20 +010097
98 stage('Clone devops (central)') {
99 steps {
100 dir('devops') {
101 sh "git init ."
102 sh "git remote remove origin || true"
103 sh "git remote add origin ${params.PROJECT_URL_PREFIX}/osm/devops"
104 sh "git fetch --depth=1 origin ${params.GERRIT_BRANCH}"
105 sh "git checkout -f FETCH_HEAD"
106 }
107 }
108 }
109
110 stage('License Scan') {
111 steps {
112 script {
113 def isMergeJob = env.JOB_NAME?.contains('merge')
114 if (!isMergeJob) { sh 'devops/tools/license_scan.sh' } else { echo 'skip the scan for merge' }
115 }
116 }
117 }
118
119 stage('Prepare Test Image') {
120 steps {
121 script {
122 // Use shared test image from registry; no local build needed
123 sh "docker pull ${env.TEST_IMAGE} || true"
124 }
125 }
126 }
127
128 stage('Tests') {
129 steps {
130 script {
131 def UID = sh(returnStdout: true, script: 'id -u').trim()
132 def GID = sh(returnStdout: true, script: 'id -g').trim()
133 def common = "-v ${env.WORKSPACE}:/tests -e UID=${UID} -e GID=${GID} " + (params.DOCKER_ARGS ?: '')
134
135 stage('Linting Tests') {
136 sh """
137 docker run --rm ${common} \
138 ${env.TEST_IMAGE} \
139 /tests/devops-stages/stage-lint.sh
140 """
141 }
142
143 stage('Unit Tests') {
144 sh """
145 docker run --rm ${common} \
146 ${env.TEST_IMAGE} \
147 /tests/devops-stages/stage-test.sh
148 """
149 if (fileExists('coverage.xml')) { cobertura coberturaReportFile: 'coverage.xml' }
150 if (fileExists('nosetests.xml')) { junit 'nosetests.xml' }
151 }
152
153 stage('Changelog') {
154 sh 'mkdir -p changelog'
155 sh """
156 docker run --rm ${common} \
157 ${env.TEST_IMAGE} \
158 /bin/sh -lc 'devops/tools/generatechangelog-pipeline.sh > /tests/changelog/changelog-${MDG}.html'
159 """
160 }
161 }
162 }
163 }
164
165 stage('Build & Push Image') {
166 when { expression { return params.DO_DOCKERPUSH } }
167 steps {
168 script {
169 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
170
171 if (!params.GERRIT_BRANCH) {
172 error 'GERRIT_BRANCH is required to tag the Docker image'
173 }
174 def sanitizedBranchName = params.GERRIT_BRANCH
175 .toLowerCase()
176 .replaceAll('[^a-z0-9._-]', '-')
177 def baseTagPrefix = "osm-${sanitizedBranchName}"
178 def buildNumber = env.BUILD_NUMBER ?: '0'
179 def isMergeJob = env.JOB_NAME?.contains('merge')
180 // Remove promotion logic from this stage
181 def moduleTags = []
182 if (isMergeJob) {
183 moduleTags << "${baseTagPrefix}-merge-${buildNumber}"
184 } else {
185 moduleTags << "${baseTagPrefix}-patchset-${buildNumber}"
186 }
187
188 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
189 def primaryLocalImage = "${imageName}:${moduleTags[0]}"
190
191 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
192 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
193 sh """
194 docker build -f Dockerfile.production -t ${primaryLocalImage} .
195 """
196 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
197 // Push build-scope tag(s) only. Promotion happens after tests.
198 moduleTags.each { tag ->
199 def localImage = "${imageName}:${tag}"
200 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${tag}"
201 sh "docker tag ${localImage} ${remoteImage}"
202 sh "docker push ${remoteImage}"
203 }
204 // Stash the built image id for later promotion without rebuild
205 env.BUILT_IMAGE = primaryLocalImage
206 env.BUILT_TAG = moduleTags[0]
207 }
208 }
209 }
210 }
211
212 stage('E2E Test (robot)') {
213 when { expression { return params.DO_ROBOT && !env.JOB_NAME.contains('merge') } }
214 steps {
215 script {
216 def dowstreamJob = "osm-e2e/${params.GERRIT_BRANCH ?: 'master'}"
217 build job: dowstreamJob,
218 parameters: [
219 string(name: 'GERRIT_BRANCH', value: params.GERRIT_BRANCH ?: 'master'),
220 string(name: 'GERRIT_REFSPEC', value: params.GERRIT_REFSPEC ?: ''),
221 string(name: 'OPENSTACK_BASE_IMAGE', value: params.OPENSTACK_BASE_IMAGE),
222 string(name: 'OPENSTACK_OSM_FLAVOR', value: params.OPENSTACK_OSM_FLAVOR),
223 string(name: 'MODULE_NAME', value: params.MODULE_NAME ?: 'LCM'),
224 string(name: 'CONTAINER_NAME', value: env.BUILT_TAG),
225 booleanParam(name: 'DO_ROBOT', value: params.DO_ROBOT),
226 booleanParam(name: 'DO_INSTALL', value: params.DO_INSTALL),
227 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', value: params.SAVE_CONTAINER_ON_FAIL),
228 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', value: params.SAVE_CONTAINER_ON_PASS)
229 ]
230 }
231 }
232 }
233 stage('Image Promotion') {
234 when { expression { return env.JOB_NAME?.contains('merge') } }
235 steps {
236 script {
237 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
238 def sanitizedBranchName = params.GERRIT_BRANCH
239 .toLowerCase()
240 .replaceAll('[^a-z0-9._-]', '-')
241 def baseTagPrefix = "osm-${sanitizedBranchName}"
242 def targetTag = "${baseTagPrefix}-merge"
243 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
244
245 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
246 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
247 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
248 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${targetTag}"
249 sh "docker tag ${env.BUILT_IMAGE} ${remoteImage}"
250 sh "docker push ${remoteImage}"
251 }
252 }
253 }
254 }
255
256 /*
257 Promotion should be done in a separate downstream job after E2E success to avoid
258 */
259 }
260 post {
261 always {
262 cleanWs()
263 }
264 }
garciadeblas57c12492025-12-23 09:48:52 +0100265}
266