Rename com.meesho/org.meesho namespace to com.homelab/org.homelab
Renames src/com/meesho -> src/com/homelab, resources/com/meesho -> resources/com/homelab, resources/org/meesho -> resources/org/homelab (via git mv, preserving history), and sweeps every remaining occurrence of "meesho" (any casing) out of package declarations, imports, libraryResource() paths, and comments across the whole repo. Also drops the per-user allowlist in vars/eksCICD.groovy, which hardcoded real former-colleagues' emails and doesn't apply to a single-person homelab — that branch is now permanently skipped rather than deleted outright, to avoid hand-editing the escape-sequence-heavy echo blocks it guards (eksCICD.groovy itself is unused legacy code, not called by homelabPipeline.groovy). Does not touch the ~114 files that were already missing from the working tree but still tracked in the prior commit — that's unrelated pre-existing state, left as-is.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.stages.deployJar
|
||||
|
||||
|
||||
def run(Map config){
|
||||
// get required variables from config
|
||||
def automation_repo_name = config.run_automation.repo_name
|
||||
def branch = config.run_automation.branch
|
||||
def deployObj = new deployJar()
|
||||
|
||||
//deploy service(farmiso) on specified machine
|
||||
deployObj.run(config)
|
||||
|
||||
|
||||
checkoutAutomationRepo(automation_repo_name, branch)
|
||||
|
||||
runAutomationSuite(automation_repo_name)
|
||||
|
||||
}
|
||||
|
||||
def checkoutAutomationRepo(String automation_repo_name, String branch){
|
||||
try{
|
||||
stage('Checkout automation repo'){
|
||||
sh "rm -rf ${automation_repo_name}; git clone git@github.com:Homelab/${automation_repo_name}.git -b ${branch}"
|
||||
echo "automation repo cloned"
|
||||
}
|
||||
}
|
||||
catch( Exception e) {
|
||||
env.msg = "Error cloning automation repo. Please check console output for more details."
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def runAutomationSuite(String automation_repo_name){
|
||||
try{
|
||||
stage('Run Automation Suite'){
|
||||
dir("$automation_repo_name"){
|
||||
run_cmd = "mvn test" //specific to Farmiso as of now.
|
||||
sh "$run_cmd"
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e) {
|
||||
env.msg = "Error running automation suite. Please check console output for more details."
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
finally{
|
||||
//publish HTML test report
|
||||
dir("$automation_repo_name"){
|
||||
archiveArtifacts artifacts: "test-output/*.*"
|
||||
|
||||
|
||||
publishHTML (target: [
|
||||
allowMissing: false,
|
||||
alwaysLinkToLastBuild: false,
|
||||
keepAll: true,
|
||||
reportDir: 'test-output',
|
||||
reportFiles: 'index.html',
|
||||
reportName: "Farmiso-Test-Automation-Report"
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.constructTemplate
|
||||
|
||||
// Rewritten for this homelab. The original's run() delegated to a
|
||||
// language-specific build object (buildNode/buildPython/etc.) and its
|
||||
// release() function drove ArgoCD sync via Ringmaster, Slack, CDN
|
||||
// invalidation, GCP/AWS branching — none of that applies to one solo app
|
||||
// on one cluster. Tag bumping in the target Helm chart is its own
|
||||
// separate stage now (updateHelmTag.groovy) rather than bundled in here.
|
||||
//
|
||||
// Kept faithfully: the real buildGo.groovy's fallback rule (line ~156)
|
||||
// — if the repo has no Dockerfile, render one from a language template;
|
||||
// if it does, just use it. That fallback now covers go/node/python/
|
||||
// java/php (matching resources/com/homelab/<lang>-Dockerfile), selected
|
||||
// via config.dockerBuildVersion — e.g. 'go-1.22', 'node-20',
|
||||
// 'python-3.12', 'java-21', 'php-8.3'. Simplified versions of the real
|
||||
// templates: no Homelab private-registry/SSH-keyed module auth, no Sonar,
|
||||
// no JFrog — see each template's own header comment.
|
||||
def run(Map config) {
|
||||
def tag = "${env.BUILD_NUMBER}-${env.GIT_COMMIT?.take(7) ?: 'dev'}"
|
||||
env.TAG = tag
|
||||
def image = "harbor-core.harbor.svc.cluster.local/${config.harbor_project}/${config.repo_name}:${tag}"
|
||||
try {
|
||||
stage(stageName('Build & push image')) {
|
||||
container('docker-cli') {
|
||||
dir("${config.repo_name}") {
|
||||
if (!fileExists('Dockerfile')) {
|
||||
renderDockerfile(config)
|
||||
}
|
||||
sh "docker build -t ${image} ."
|
||||
sh "docker push ${image}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error building/pushing image. Error: ' + e.toString()
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
// Only reached when the repo has no Dockerfile of its own. Requires
|
||||
// config.dockerBuildVersion (e.g. 'go-1.22') — there's no sensible
|
||||
// default to fall back to if a repo has neither a Dockerfile nor this
|
||||
// set, unlike every other config key in this pipeline.
|
||||
def renderDockerfile(Map config) {
|
||||
if (!config.dockerBuildVersion) {
|
||||
error("buildDocker: no Dockerfile in repo and config.dockerBuildVersion not set — can't pick a base image. Either add a Dockerfile to the repo, or set dockerBuildVersion (e.g. 'go-1.22') in config.yaml or the Jenkinsfile call.")
|
||||
}
|
||||
def parts = config.dockerBuildVersion.split('-', 2)
|
||||
def lang = parts[0]
|
||||
def version = parts.size() > 1 ? parts[1] : null
|
||||
|
||||
def templates = [
|
||||
go : ['go-Dockerfile', '1.22'],
|
||||
node : ['node-Dockerfile', '20'],
|
||||
python: ['python-Dockerfile', '3.12'],
|
||||
java : ['java-Dockerfile', '21'],
|
||||
maven : ['java-Dockerfile', '21'],
|
||||
php : ['php-Dockerfile', '8.3'],
|
||||
]
|
||||
def entry = templates[lang]
|
||||
if (!entry) {
|
||||
error("buildDocker: unrecognised language '${lang}' in dockerBuildVersion '${config.dockerBuildVersion}'. Supported: ${templates.keySet().join(', ')}. Or just add a Dockerfile to the repo instead — that always takes priority over this fallback.")
|
||||
}
|
||||
def (templateFile, defaultVersion) = entry
|
||||
def resolvedVersion = version ?: defaultVersion
|
||||
|
||||
log.info("buildDocker: no Dockerfile in repo — rendering ${templateFile} for ${lang} ${resolvedVersion}")
|
||||
def constructObj = new constructTemplate()
|
||||
constructObj.renderTemplate([version: resolvedVersion], templateFile, 'Dockerfile')
|
||||
sh 'cat Dockerfile'
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.buTeamMapping
|
||||
import com.homelab.utilities.constructTemplate
|
||||
import com.homelab.utilities.getDockerParams
|
||||
import com.homelab.utilities.addSSHKey
|
||||
import com.homelab.utilities.gitActions
|
||||
import com.homelab.utilities.constructParam
|
||||
import com.homelab.utilities.dockerUtilities
|
||||
|
||||
|
||||
def buildDckr(Map config) {
|
||||
env.GOPRIVATE = 'github.com/Homelab'
|
||||
def buTeamMappingObj = new buTeamMapping()
|
||||
def constructObj = new constructTemplate()
|
||||
def dockerParamObj = new getDockerParams()
|
||||
def addSshKeyObj = new addSSHKey()
|
||||
def dockerUtilObj = new dockerUtilities()
|
||||
|
||||
def team = buTeamMappingObj.get_team_initials(config.team)
|
||||
def modules = config.modules ?: ['module_less']
|
||||
def repoName = config.repo_name
|
||||
def dockerRepository = "${env.cicd_environment}/${team}/${repoName.toLowerCase()}"
|
||||
def tag = dockerParamObj.getTag(repoName)
|
||||
def dockerBindings = [:]
|
||||
def version = config.dockerBuildVersion.split('-')[-1]
|
||||
def constructParamObj = new constructParam()
|
||||
def repoType = config.repo_type ?: 'microservice'
|
||||
boolean skipSonarAndQualityGate = constructParamObj.skipSonarCheckForGo(config)
|
||||
echo "skipSonarAndQualityGate: ${skipSonarAndQualityGate}"
|
||||
String goVersion = config.goVersion ?: version
|
||||
boolean shouldDeployArgo = (config.deployArgo ?: false).toBoolean()
|
||||
boolean isConfigOnlyChange = false
|
||||
|
||||
// Ensure goVersion is in x.x.x format
|
||||
echo "goVersion: ${goVersion}"
|
||||
if (goVersion.split('\\.').size() == 2) {
|
||||
goVersion += '.0'
|
||||
}
|
||||
if (env.INFRA_ENV == 'toolchain') {
|
||||
tag = dockerParamObj.getTag(repoName)
|
||||
boolean allImagesExist = true
|
||||
|
||||
for (module in modules) {
|
||||
def moduleName = (module instanceof LinkedHashMap) ? module.keySet()[0] : module
|
||||
def modulePath = (moduleName == 'module_less') ? dockerRepository : "${dockerRepository}/${moduleName}"
|
||||
//TODO: building everything if one is missing -> create list of modules unbuild will only build those
|
||||
if (!dockerUtilObj.imageExists(env.registry, modulePath, tag)) {
|
||||
log.info("Toolchain: Image missing for ${moduleName} at ${modulePath}:${tag}. Proceeding with build.")
|
||||
allImagesExist = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (allImagesExist) {
|
||||
log.info("Toolchain: All images found in registry for tag ${tag}. Skipping build step.")
|
||||
return [tag, shouldDeployArgo]
|
||||
}
|
||||
}
|
||||
stage(stageName('Check Config Changes and Get Latest Image Tag')) {
|
||||
(isConfigOnlyChange, shouldDeployArgo) = is_config_only_change_and_should_deploy_argo(repoName, shouldDeployArgo)
|
||||
log.info("Is just application config change? $isConfigOnlyChange")
|
||||
def param = new constructParam()
|
||||
if (env.CHANGE_ID && repoType == 'microservice') {
|
||||
def shouldValidateConfig = param.ValidateCacConfigForRepo(false, repoName)
|
||||
if (shouldValidateConfig) {
|
||||
log.info('************ Validate Config for CAC application.yml files ************')
|
||||
dir("$repoName") {
|
||||
writeFile file: 'validate_configs.py', text: libraryResource('com/homelab/validate_configs_v2.py')
|
||||
sh 'python3 validate_configs.py'
|
||||
}
|
||||
} else {
|
||||
log.info('************ Skipping Validation of CAC Config ************')
|
||||
}
|
||||
} else {
|
||||
log.info("Skipping Validation of CAC Config for ${repoType} repo type")
|
||||
log.info('************ Skipping Validation of CAC Config ************')
|
||||
}
|
||||
|
||||
if (isConfigOnlyChange) {
|
||||
log.info('Skipping the build, since it is just application config change')
|
||||
log.info('Getting latest image tag from GAR')
|
||||
def firstModule = modules[0]
|
||||
def firstModuleName = (firstModule instanceof LinkedHashMap) ? firstModule.keySet()[0] : firstModule
|
||||
def moduleDockerRepository = (firstModuleName == 'module_less') ? dockerRepository : "${dockerRepository}/${firstModuleName}"
|
||||
try {
|
||||
def latest_image = sh(
|
||||
script: "gcloud container images list-tags ${env.registry}/${moduleDockerRepository} --format='value(tags)' | sed '/^\$/d' | awk -F'-' '{print \$NF}' | sort | tail -1",
|
||||
returnStdout: true
|
||||
).trim()
|
||||
echo "latest_image: ${latest_image}"
|
||||
if (latest_image == '') {
|
||||
log.info("No existing images found for ${env.registry}/${moduleDockerRepository}. Using generated tag: ${tag}")
|
||||
} else {
|
||||
tag = sh(
|
||||
script: "gcloud container images list-tags ${env.registry}/${moduleDockerRepository} --format='value(tags)' | tr ',' '\n' | grep ${latest_image}",
|
||||
returnStdout: true
|
||||
).trim()
|
||||
}
|
||||
} catch (Exception e) {
|
||||
env.msg = 'Error getting latest docker image from GAR'
|
||||
env.error_msg_to_db = env.msg
|
||||
log.error("${env.msg}\n${e.toString()}")
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
return [tag, shouldDeployArgo]
|
||||
}
|
||||
log.info('Proceeding with the build')
|
||||
}
|
||||
|
||||
stage(stageName('Scanning Sonar and Quality Gate')) {
|
||||
dir(repoName) {
|
||||
sonar_scan(repoName, skipSonarAndQualityGate, goVersion)
|
||||
}
|
||||
}
|
||||
if (currentBuild.result == 'UNSTABLE')
|
||||
{ return [tag, false] }
|
||||
|
||||
if (repoType != 'microservice') {
|
||||
log.info("Skipping Docker build for ${repoType} repo type")
|
||||
return [tag, shouldDeployArgo]
|
||||
}
|
||||
|
||||
stage(stageName('Building docker images')) {
|
||||
try {
|
||||
sh(script: 'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
|
||||
} catch (Exception e) {
|
||||
env.msg = "[Failure] Can't login to gcloud docker registry."
|
||||
env.error_msg_to_db = env.msg
|
||||
log.error("${env.msg}. Error: ${e.toString()}")
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
dockerBindings['version'] = (version == 'go') ? '1.24.4' : version
|
||||
dockerBindings['base_dir'] = config.base_dir ?: false
|
||||
dockerBindings['build_registry'] = env.buildRegistry
|
||||
dockerBindings['go_proxy'] = env.goProxyUrl
|
||||
dockerBindings['repo_name'] = repoName
|
||||
if (config.containsKey('copy_file')) {
|
||||
String recursive = config.copy_file.recursive ? ' -r' : ''
|
||||
dir(repoName) {
|
||||
dir('copied_files') {
|
||||
sh(script: "gsutil cp${recursive} ${config.copy_file.path} .")
|
||||
}
|
||||
}
|
||||
dockerBindings['copy_file'] = true
|
||||
dockerBindings['copy_target'] = config.copy_file.target ?: '/app/'
|
||||
dockerBindings['base_dir'] = config.base_dir ?: false
|
||||
} else {
|
||||
dockerBindings['copy_file'] = false
|
||||
}
|
||||
|
||||
try {
|
||||
dir(repoName) {
|
||||
addSshKeyObj.create()
|
||||
if (!fileExists('Dockerfile')) {
|
||||
def moduleBuilds = [:]
|
||||
for (m in modules) {
|
||||
def moduleRef = m
|
||||
def moduleName = (moduleRef instanceof LinkedHashMap) ? moduleRef.keySet()[0] : moduleRef
|
||||
moduleBuilds["build-${moduleName}"] = {
|
||||
def localBindings = dockerBindings.clone()
|
||||
localBindings['module_property'] = (moduleRef instanceof LinkedHashMap) ? moduleRef[moduleName] : [:]
|
||||
localBindings['kafka'] = (localBindings['module_property'].kafka) ? '-kafka' : ''
|
||||
localBindings['module'] = moduleName
|
||||
constructObj.renderTemplate(localBindings, 'go-Dockerfile', "Dockerfile-${moduleName}")
|
||||
sh "cat Dockerfile-${moduleName}"
|
||||
def moduleDockerRepository = (moduleRef == 'module_less') ? dockerRepository : "${dockerRepository}/${moduleName}"
|
||||
sh(script: 'tar -cf only-mods.tar $(git ls-files "go.mod" "go.sum" "**/go.mod" "**/go.sum" 2>/dev/null || find . -name go.mod -o -name go.sum)')
|
||||
sh(script: "set +x && docker build --tag ${env.registry}/${moduleDockerRepository}:${tag} -f Dockerfile-${moduleName} .")
|
||||
if (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') {
|
||||
dockerUtilObj.retryDockerPush("docker push ${env.registry}/${moduleDockerRepository}:${tag}")
|
||||
} else {
|
||||
log.info("Skipping Docker Push - ${env.cicd_environment} env")
|
||||
}
|
||||
sh(script: "rm -rf Dockerfile-${moduleName}")
|
||||
}
|
||||
}
|
||||
parallel moduleBuilds
|
||||
} else {
|
||||
sh 'cat Dockerfile'
|
||||
def moduleDockerRepository = dockerRepository // default for repo root
|
||||
if (modules && modules[0] != 'module_less') {
|
||||
def firstModule = modules[0]
|
||||
def firstModuleName = (firstModule instanceof LinkedHashMap) ? firstModule.keySet()[0] : firstModule
|
||||
moduleDockerRepository = dockerRepository + '/' + firstModuleName
|
||||
}
|
||||
def pushOrNot = (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') ? '--push' : ''
|
||||
if (!pushOrNot) {
|
||||
log.info("Skipping Docker Push - ${env.cicd_environment} env")
|
||||
}
|
||||
sh(script: "docker build --tag ${env.registry}/${moduleDockerRepository}:${tag} ${pushOrNot} .")
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
env.msg = 'Error in building DockerFile Or Pushing To ECR'
|
||||
env.error_msg_to_db = env.msg
|
||||
log.error("${env.msg}. Error: ${e.toString()}")
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
return [tag, shouldDeployArgo]
|
||||
}
|
||||
}
|
||||
|
||||
def is_config_only_change_and_should_deploy_argo(String repoName, boolean deployArgo) {
|
||||
if (env.INFRA_ENV == 'toolchain') {
|
||||
return [false, deployArgo]
|
||||
}
|
||||
def gitObj = new gitActions()
|
||||
def configFiles = []
|
||||
boolean shouldDeployArgoResult = deployArgo
|
||||
boolean isConfigOnlyChange = false
|
||||
|
||||
def changedFiles = env.CHANGE_ID ?
|
||||
gitObj.fetchDiffFilesForPullRequest(repoName, env.CHANGE_TARGET) :
|
||||
gitObj.fetchDiffFilesForPushRequest(repoName)
|
||||
|
||||
if (!changedFiles) {
|
||||
return [isConfigOnlyChange, shouldDeployArgoResult]
|
||||
}
|
||||
|
||||
for (file in changedFiles.split('\n')) {
|
||||
if (!file.startsWith('configs/')) {
|
||||
isConfigOnlyChange = false
|
||||
return [isConfigOnlyChange, shouldDeployArgoResult]
|
||||
}
|
||||
isConfigOnlyChange = true
|
||||
configFiles << file
|
||||
}
|
||||
|
||||
if (configFiles && deployArgo && isConfigOnlyChange) {
|
||||
def hasEnvironmentConfig = configFiles.any { it.contains(env.cicd_environment) }
|
||||
if (!hasEnvironmentConfig) {
|
||||
shouldDeployArgoResult = false
|
||||
echo "Config changes don't contain environment: ${env.cicd_environment}"
|
||||
}
|
||||
}
|
||||
return [isConfigOnlyChange, shouldDeployArgoResult]
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to execute sonar scan
|
||||
* @param repoName: repository name
|
||||
* @param skipSonarAndQualityGate: boolean parameter to skip sonar scan
|
||||
* @param goVersion: Go version string
|
||||
*/
|
||||
def sonar_scan(String repoName, boolean skipSonarAndQualityGate, String goVersion) {
|
||||
if (skipSonarAndQualityGate) {
|
||||
log.info('Sonar scan is skipped. Marking this stage as passed.')
|
||||
return
|
||||
}
|
||||
stage(stageName('Running sonar scan')) {
|
||||
try {
|
||||
withSonarQubeEnv(env.sonarEnv) {
|
||||
echo "env.sonarEnv: ${env.sonarEnv}"
|
||||
echo "env.BRANCH_NAME: ${env.BRANCH_NAME}"
|
||||
def exclusions = ""
|
||||
if (!fileExists('sonar-project.properties')) {
|
||||
exclusions = " -Dsonar.exclusions=**/*_test.go,**/mock_*.go,**/mock.go,**/*.pb.go,**/*.proto,**/model.go"
|
||||
}else{
|
||||
exclusions = " -Dproject.settings=`pwd`/sonar-project.properties"
|
||||
}
|
||||
|
||||
def scannerCommand = "sonar-scanner -Dsonar.projectKey=${repoName} -Dsonar.go.coverage.reportPaths=./cov.out -Dsonar.branch.name=${env.BRANCH_NAME} -Dsonar.ws.timeout=120 ${exclusions}"
|
||||
echo "env.SONAR_HOST_URL: ${env.SONAR_HOST_URL}"
|
||||
env.PATH = "/usr/local/sonar-scanner/sonar-scanner-5.0.1.3006-linux/bin:${env.PATH}"
|
||||
def response = sh(
|
||||
script: "curl -s -w '\n%{http_code}' -u ${env.SONAR_AUTH_TOKEN}: ${env.SONAR_HOST_URL}/api/navigation/component?component=${repoName}",
|
||||
returnStdout: true
|
||||
).trim()
|
||||
|
||||
def responseLines = response.split('\n')
|
||||
def statusCode = responseLines[-1]
|
||||
def responseBody = responseLines[0..-2].join('\n')
|
||||
|
||||
log.info("SonarQube API Response Code: ${statusCode}")
|
||||
log.info("SonarQube API Response Body: ${responseBody}")
|
||||
|
||||
def projectExists = (statusCode == '200')
|
||||
if (!projectExists && env.CHANGE_ID) {
|
||||
log.info("Project ${repoName} does not exist. Creating it via a bootstrap scan on the target branch.")
|
||||
|
||||
def targetBranch = env.CHANGE_TARGET ?: 'develop'
|
||||
echo "targetBranch: ${targetBranch}"
|
||||
sh "sonar-scanner \
|
||||
-Dsonar.projectKey=${repoName} \
|
||||
-Dsonar.projectName=${repoName} \
|
||||
-Dsonar.branch.name=${targetBranch} \
|
||||
-Dsonar.sources=. \
|
||||
-Dsonar.scm.disabled=true \
|
||||
-Dsonar.ws.timeout=120"
|
||||
log.info("Bootstrap complete. Project created.")
|
||||
}else{
|
||||
echo "Project ${repoName} already exists. Skipping bootstrap scan."
|
||||
}
|
||||
|
||||
if (env.CHANGE_ID) {
|
||||
echo "env.CHANGE_ID: ${env.CHANGE_ID}"
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
sh "git fetch origin ${env.CHANGE_TARGET}:refs/remotes/origin/${env.CHANGE_TARGET}"
|
||||
}
|
||||
scannerCommand = "sonar-scanner -Dsonar.projectKey=${repoName} -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repoName} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET} -Dsonar.go.coverage.reportPaths=./cov.out -Dsonar.ws.timeout=120${exclusions}"
|
||||
echo "scannerCommand: ${scannerCommand}"
|
||||
}
|
||||
downloadGoFromJFrog(goVersion)
|
||||
sh(script: "GOPROXY=${env.goProxyUrl},direct && go mod tidy")
|
||||
int testExitCode = sh(script: 'go test -short -coverprofile=./cov.out ./...', returnStatus: true)
|
||||
if (testExitCode != 0) {
|
||||
error('Go tests failed.')
|
||||
}
|
||||
echo "scannerCommand: ${scannerCommand}"
|
||||
sh(script: scannerCommand)
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error in running sonar scan: ${e}")
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
stage(stageName('Quality Gate')) {
|
||||
try {
|
||||
if (!env.CHANGE_ID || env.cicd_environment == 'int') {
|
||||
log.info('Skipping quality gate check on Branches/Pre-Prod. Marking this stage as passed.')
|
||||
} else {
|
||||
timeout(time: 600, unit: 'SECONDS') {
|
||||
def qg = waitForQualityGate()
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
if (qg.status != 'OK') {
|
||||
log.warning("Quality gate failed: ${qg.status}")
|
||||
error "Stopping pipeline due to quality gate failure."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch (Exception e) {
|
||||
echo "Error in quality gate: ${e.message}"
|
||||
unstable('Quality Gate Failed !')
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to download Go binary from JFrog
|
||||
* @param goVersion: Go version string
|
||||
*/
|
||||
def downloadGoFromJFrog(String goVersion) {
|
||||
try {
|
||||
echo "env.cicd_environment: ${env.cicd_environment}"
|
||||
|
||||
def jfrogUrl = (env.cicd_environment == 'prd' || env.cicd_environment == 'int') ?
|
||||
'https://jfrog-prd.homelabgcp.in' :
|
||||
'https://jfrog-dev.homelabgcp.in'
|
||||
|
||||
def repo = 'devops-tools-local'
|
||||
def goTarball = "go${goVersion}.linux-amd64.tar.gz"
|
||||
def jfrogPath = "${jfrogUrl}/artifactory/${repo}/go/${goTarball}"
|
||||
|
||||
def jfrogUser = ''
|
||||
def jfrogPass = ''
|
||||
def credentialId = (env.cicd_environment == 'prd' || env.cicd_environment == 'int') ?
|
||||
'jfrog-prd-credentials' : 'jfrog-stg-credentials'
|
||||
echo "Using credential ID: ${credentialId}"
|
||||
withCredentials([usernamePassword(credentialsId: credentialId,
|
||||
usernameVariable: 'JFROG_USER',
|
||||
passwordVariable: 'JFROG_PASS')]) {
|
||||
jfrogUser = env.JFROG_USER
|
||||
jfrogPass = env.JFROG_PASS
|
||||
|
||||
log.info("Attempting to download Go ${goVersion} from JFrog: ${jfrogPath}")
|
||||
|
||||
def downloadStatus = sh(script: """
|
||||
curl -u "${env.JFROG_USER}:${env.JFROG_PASS}" \
|
||||
-fLO "${jfrogPath}" \
|
||||
--fail --silent --show-error
|
||||
""", returnStatus: true)
|
||||
|
||||
if (downloadStatus == 0 && fileExists(goTarball)) {
|
||||
log.info("Successfully downloaded Go ${goVersion} from JFrog")
|
||||
} else {
|
||||
echo("WARN: Go ${goVersion} not found in JFrog. Falling back to go.dev...")
|
||||
sh(script: "curl -LO https://go.dev/dl/${goTarball}")
|
||||
|
||||
log.info("Uploading downloaded Go ${goVersion} to JFrog for future use")
|
||||
sh(script: """
|
||||
curl -u "${env.JFROG_USER}:${env.JFROG_PASS}" \
|
||||
-T "${goTarball}" \
|
||||
"${jfrogPath}" \
|
||||
--fail --silent --show-error || echo "Upload to JFrog failed, but continuing..."
|
||||
""")
|
||||
}
|
||||
}
|
||||
|
||||
sh(script: "tar -xvzf ${goTarball} -C /usr/local", returnStdout: true)
|
||||
sh(script: "rm -Rf ${goTarball}")
|
||||
env.PATH = "/usr/local/go/bin:${env.PATH}"
|
||||
|
||||
// Verify installation
|
||||
sh(script: "go version")
|
||||
|
||||
} catch (Exception e) {
|
||||
echo("ERROR: Failed to download Go from JFrog: ${e.message}")
|
||||
|
||||
// Final fallback
|
||||
sh(script: "curl -LO https://go.dev/dl/go${goVersion}.linux-amd64.tar.gz")
|
||||
sh(script: "tar -xvzf go${goVersion}.linux-amd64.tar.gz -C /usr/local", returnStdout: true)
|
||||
sh(script: "rm -Rf go${goVersion}.linux-amd64.tar.gz")
|
||||
env.PATH = "/usr/local/go/bin:${env.PATH}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,542 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.buTeamMapping
|
||||
import com.homelab.utilities.constructTemplate
|
||||
import com.homelab.utilities.getDockerParams
|
||||
import com.homelab.stages.checkOut
|
||||
|
||||
/*
|
||||
Function to define the flow of entire build, this function will call different stages related to maven build
|
||||
*/
|
||||
def run(Map config) {
|
||||
// get required variables from config
|
||||
def checkoutObj = new checkOut()
|
||||
def repo_name = config.repo_name
|
||||
def args = config.build_args ?: ''
|
||||
def skip_test = config.skip_test ?: false
|
||||
def skip_sonar = config.skip_sonar ?: false
|
||||
def push_to_jfrog = config.push_to_jfrog ?: false
|
||||
def push_to_s3 = config.push_to_s3 ?: false
|
||||
def branch_name = "${env.BRANCH_NAME}"
|
||||
if (branch_name == 'gcp-main' || branch_name == 'gcp-master') {
|
||||
push_to_jfrog = config.containsKey('push_to_jfrog') ? config.push_to_jfrog : false
|
||||
}
|
||||
def version = getVersion("${repo_name}")
|
||||
|
||||
if (env.hot_fix) {
|
||||
skip_test = true
|
||||
skip_sonar = true
|
||||
push_to_jfrog = false
|
||||
}
|
||||
|
||||
// call the stages
|
||||
checkS3(repo_name, branch_name, version, push_to_s3)
|
||||
build(repo_name, skip_test, args)
|
||||
sonar_scan(repo_name, skip_sonar)
|
||||
if (!env.CHANGE_ID) {
|
||||
pushArtifactToJFrog(repo_name, push_to_jfrog)
|
||||
pushArtifactToS3(repo_name, branch_name, push_to_s3)
|
||||
}
|
||||
else {
|
||||
log.info('Artifact Push is disabled for Pull Requests')
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function to check if the artifact exist in s3 or not
|
||||
input argument
|
||||
*/
|
||||
def checkS3(String repo_name, String branch_name, String version, boolean push_to_s3) {
|
||||
stage(stageName('Checking if artifact already exists')) {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
if (env.hot_fix) {
|
||||
env.TAG = "v${version}-HOT"
|
||||
}
|
||||
else {
|
||||
if (("${branch_name}" == 'master' || "${branch_name}" == 'main' || push_to_s3) && !env.CHANGE_ID) {
|
||||
env.TAG = "v${version}"
|
||||
log.info('########################## Checking if Artifact Already Exists. ###########################')
|
||||
artifact_exists = sh(returnStdout: true, script: "aws s3 ls \"s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/\" 2>/dev/null || echo ''").trim()
|
||||
log.info("${artifact_exists}")
|
||||
if ( artifact_exists ) {
|
||||
env.msg = "CI for this\n version - ${version}\n branch - ${branch_name}\n TAG - ${TAG}\nis already done. Please proceed with CD."
|
||||
log.error(env.msg)
|
||||
currentBuild.result = 'FAILURE'
|
||||
throw new Exception(env.msg)
|
||||
}
|
||||
else {
|
||||
log.info("Proceeding with building artifact for TAG - ${TAG}.")
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.info('Skipping - Building artifact')
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
if (env.hot_fix) {
|
||||
env.TAG = "v${version}-HOT"
|
||||
}
|
||||
else {
|
||||
if (("${branch_name}" == 'master' || "${branch_name}" == 'main' || "${branch_name}" == 'gcp-main' || "${branch_name}" == 'gcp-master' || push_to_s3) && !env.CHANGE_ID) {
|
||||
env.TAG = "v${version}"
|
||||
log.info('########################## Checking if Artifact Already Exists. ###########################')
|
||||
artifact_exists = sh(returnStdout: true, script: "gsutil ls \"gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/\" 2>/dev/null || echo ''").trim()
|
||||
log.info("${artifact_exists}")
|
||||
if ( artifact_exists ) {
|
||||
env.msg = "CI for this\n version - ${version}\n branch - ${branch_name}\n TAG - ${TAG}\nis already done. Please proceed with CD."
|
||||
log.error(env.msg)
|
||||
currentBuild.result = 'FAILURE'
|
||||
throw new Exception(env.msg)
|
||||
}
|
||||
else {
|
||||
log.info("Proceeding with building artifact for TAG - ${TAG}.")
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.info('Skipping - Building artifact.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function to build the maven package
|
||||
input arguments:
|
||||
repo_name: repository name for changing dirctory
|
||||
skip_test: boolen value to skip unit tests
|
||||
skip_sonar: boolen value to skip sonar quality gate
|
||||
args: string parameter to provide additional arguments to build cmd, example: '-U'
|
||||
*/
|
||||
def build(String repo_name, boolean skip_test, String args) {
|
||||
try {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
stage(stageName('Building gradle package')) {
|
||||
String build_cmd = 'gradle clean build'
|
||||
dir("$repo_name") {
|
||||
sh(script:"cp ~/.m2/settings.xml .;${build_cmd} ${args}")
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
stage(stageName('Building gradle package')) {
|
||||
String build_cmd = 'gradle clean build'
|
||||
dir("$repo_name") {
|
||||
sh(script:"gradle -v;cp ~/.m2/settings.xml .;${build_cmd} ${args}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( Exception e) {
|
||||
env.msg = "Error Building the maven package. Please check console output for more details - ${e}"
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
/*
|
||||
Fuction to execute sonar scan
|
||||
Input Arguments:
|
||||
repo_name: repository name
|
||||
skip_sonar: boolena parameter to skip sonar scan
|
||||
*/
|
||||
def sonar_scan(String repo_name, boolean skip_sonar) {
|
||||
try {
|
||||
stage(stageName('Running sonar scan')) {
|
||||
if (!skip_sonar) {
|
||||
dir("$repo_name") {
|
||||
withSonarQubeEnv(env.sonarEnv) {
|
||||
if (env.CHANGE_ID) {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script: "gradle sonar:sonar -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repo_name} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script: "gradle sonar:sonar -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repo_name} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET}")
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script: "gradle sonar:sonar -Dsonar.branch.name=${env.BRANCH_NAME}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script: "gradle sonar:sonar -Dsonar.branch.name=${env.BRANCH_NAME}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.info('Skipping - Sonar Scan')
|
||||
}
|
||||
}
|
||||
// if (!skip_sonar) {
|
||||
// stage('Quality Gate') {
|
||||
// timeout(time: 300, unit: 'SECONDS') {
|
||||
// def qg = waitForQualityGate()
|
||||
// catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
// if (qg.status != 'OK') {
|
||||
// error "stage failed due to quality gate failure: ${qg.status}"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
catch (Exception e) {
|
||||
dir("$repo_name") {
|
||||
withSonarQubeEnv(env.sonarEnv) {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script: 'gradle sonar:sonar')
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script: 'gradle sonar:sonar')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Function to get the version from build.gradle
|
||||
input arguments:
|
||||
repo_name: String parameter to change the directory where build.gradle is located
|
||||
*/
|
||||
def getVersion(String repo_name) {
|
||||
try {
|
||||
dir("$repo_name") {
|
||||
return sh(returnStdout: true, script: "grep version build.gradle | head -1 | cut -d \"'\" -f2").trim()
|
||||
}
|
||||
}
|
||||
catch ( Exception e) {
|
||||
env.msg = 'Error while getting the version from build.gradle . Please check console output for more details.'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function to get the modules from build.gradle
|
||||
input arguments:
|
||||
repo_name: String parameter to change the directory where build.gradle is located
|
||||
*/
|
||||
def getModules(String repo_name) {
|
||||
try {
|
||||
dir("$repo_name") {
|
||||
modules = sh(returnStdout: true, script: 'xq -r .project.modules.module[] build.gradle 2>/dev/null || xq -r .project.modules.module build.gradle 2>/dev/null || echo empty').trim()
|
||||
if (modules == 'empty' || modules == 'null') {
|
||||
modules = 'module_less'
|
||||
}
|
||||
modules = modules.split('\n') as List
|
||||
return modules
|
||||
}
|
||||
}
|
||||
catch ( Exception e) {
|
||||
env.msg = 'Error getting the modules from build.gradle . Please check console output for more details.'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function to push artifacts to jfrog artifactory
|
||||
input arguments:
|
||||
repo_name: repository name
|
||||
push_to_jfrog: boolen argument to push_to_jfrog
|
||||
*/
|
||||
def pushArtifactToJFrog(String repo_name, boolean push_to_jfrog) {
|
||||
try {
|
||||
stage(stageName('Deploying to JFrog')) {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
branch_name = 'repo'
|
||||
if (branch_name == 'master' || branch_name == 'main' || push_to_jfrog) {
|
||||
dir("${repo_name}") {
|
||||
profiles = sh(returnStdout:true, script: 'xq -r .project.profiles build.gradle ').trim()
|
||||
block_dist = sh(returnStdout:true, script: 'xq -r .project.distributionManagement build.gradle ').trim()
|
||||
// Check for profiles tag in the build.gradle
|
||||
if (profiles != 'null') {
|
||||
// Check if profiles has distiributionManagement defined
|
||||
profile_dist = sh(returnStdout:true, script: 'xq -r .project.profiles.profile[].distributionManagement build.gradle 2>/dev/null || xq -r .project.profiles.profile.distributionManagement build.gradle 2>/dev/null || echo null').trim()
|
||||
if (profile_dist != 'null' ) {
|
||||
publish_repo = (branch_name == 'master' || branch_name == 'main' || branch_name == 'gcp-main' || branch_name == 'gcp-master') ? 'useProdRepo' : 'useTestRepo'
|
||||
log.info('########################### Pushing artifact to Jfrog. ###########################')
|
||||
sh "gradle package deploy -DskipTests=true -D${publish_repo}=true"
|
||||
}
|
||||
else {
|
||||
log.info('distributionManagement is not defined in the build.gradle . Skipping - Push to Jfrog Artifactory')
|
||||
}
|
||||
}
|
||||
else if (block_dist != 'null') {
|
||||
// Check if distributionManagement is defined without profiles
|
||||
log.info('########################### Pushing artifact to Jfrog. ###########################')
|
||||
sh 'gradle package deploy -DskipTests=true'
|
||||
}
|
||||
else {
|
||||
log.info('distributionManagement is not defined in the build.gradle . Skipping - Push to Jfrog Artifactory')
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.info('########################### Skipping - Push to Jfrog Artifactory. ###########################')
|
||||
}
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
branch_name = 'repo'
|
||||
if (branch_name == 'master' || branch_name == 'main' || push_to_jfrog) {
|
||||
dir("${repo_name}") {
|
||||
profiles = sh(returnStdout:true, script: 'xq -r .project.profiles build.gradle ').trim()
|
||||
block_dist = sh(returnStdout:true, script: 'xq -r .project.distributionManagement build.gradle ').trim()
|
||||
// Check for profiles tag in the build.gradle
|
||||
if (profiles != 'null') {
|
||||
// Check if profiles has distiributionManagement defined
|
||||
profile_dist = sh(returnStdout:true, script: 'xq -r .project.profiles.profile[].distributionManagement build.gradle 2>/dev/null || xq -r .project.profiles.profile.distributionManagement build.gradle 2>/dev/null || echo null').trim()
|
||||
if (profile_dist != 'null' ) {
|
||||
publish_repo = (branch_name == 'master' || branch_name == 'main' || branch_name == 'gcp-main' || branch_name == 'gcp-master') ? 'useProdRepo' : 'useTestRepo'
|
||||
log.info('########################### Pushing artifact to Jfrog. ###########################')
|
||||
sh "gradle package deploy -DskipTests=true -D${publish_repo}=true"
|
||||
echo 'execute gradle dependency'
|
||||
sh 'gradle dependency:list > dependency_tree.txt'
|
||||
echo ' uploading gradle dependency'
|
||||
sh 'ls -ltr'
|
||||
echo 'lets run copy command'
|
||||
try {
|
||||
sh "gsutil cp dependency_tree.txt 'gs://${env.objBucket}/common-dependencies/${repo_name}/'"
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
echo "Skipping - copying txt file to GCS - ${e}"
|
||||
}
|
||||
echo 'uploading gradle dependency'
|
||||
}
|
||||
else {
|
||||
log.info('distributionManagement is not defined in the build.gradle . Skipping - Push to Jfrog Artifactory')
|
||||
}
|
||||
}
|
||||
else if (block_dist != 'null') {
|
||||
// Check if distributionManagement is defined without profiles
|
||||
log.info('########################### Pushing artifact to Jfrog. ###########################')
|
||||
sh 'gradle package deploy -DskipTests=true'
|
||||
}
|
||||
else {
|
||||
log.info('distributionManagement is not defined in the build.gradle . Skipping - Push to Jfrog Artifactory')
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.info('########################### Skipping - Push to Jfrog Artifactory. ###########################')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
env.msg = 'Error in pushing artifacts to jfrog . Please check console output for more details.'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function to push artifacts to s3
|
||||
input arguments:
|
||||
repo_name: repository name
|
||||
push_to_s3: boolen argument to push to s3
|
||||
*/
|
||||
def pushArtifactToS3(String repo_name, String branch_name, boolean push_to_s3) {
|
||||
try {
|
||||
stage(stageName('Pushing artifacts to object storage')) {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
if ("${branch_name}" == 'master' || "${branch_name}" == 'main' || push_to_s3) {
|
||||
def modules = getModules("${repo_name}")
|
||||
log.info('}########################### Pushing artifacts to S3. ###########################')
|
||||
j = 0
|
||||
for (module in modules) {
|
||||
j += 1
|
||||
sh """
|
||||
echo "${j}. ${module}"
|
||||
if [ -f ${repo_name}/${module}/target/*.jar ]
|
||||
then
|
||||
ls -al ${repo_name}/${module}/target/*.jar
|
||||
echo "Uploading artifacts to - ${repo_name}/${branch_name}/${TAG}"
|
||||
aws s3 cp ${repo_name}/${module}/target/*.jar "s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/"
|
||||
echo "Listing ${TAG} artifacts -"
|
||||
aws s3 ls "s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/" || echo Nothing to display here.
|
||||
elif [ -f ${repo_name}/target/*.jar ] && [ ${module} = 'module_less' ]
|
||||
then
|
||||
ls -al ${repo_name}/target/*.jar
|
||||
echo "Uploading artifacts to - ${repo_name}/${branch_name}/${TAG}"
|
||||
aws s3 cp ${repo_name}/target/*.jar "s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/"
|
||||
echo "Uploading Archived Code to - ${repo_name}/${branch_name}/${TAG}"
|
||||
echo "Listing ${TAG} artifacts -"
|
||||
aws s3 ls "s3://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/" || echo Nothing to display here.
|
||||
else
|
||||
echo "No jar file found."
|
||||
fi
|
||||
"""
|
||||
}
|
||||
log.info("TAG for CD - ${TAG}")
|
||||
}
|
||||
else {
|
||||
log.info("Skipping - Artifact push. As it is not supported for ${env.BRANCH_NAME}")
|
||||
}
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
if ("${branch_name}" == 'master' || "${branch_name}" == 'main' || "${branch_name}" == 'gcp-main' || branch_name == 'gcp-master' || push_to_s3) {
|
||||
def modules = getModules("${repo_name}")
|
||||
log.info('}########################### Pushing artifacts to S3. ###########################')
|
||||
j = 0
|
||||
for (module in modules) {
|
||||
j += 1
|
||||
sh """
|
||||
echo "${j}. ${module}"
|
||||
if [ -f ${repo_name}/${module}/target/*.jar ]
|
||||
then
|
||||
ls -al ${repo_name}/${module}/target/*.jar
|
||||
echo "Uploading artifacts to - ${repo_name}/${branch_name}/${TAG}"
|
||||
gsutil cp ${repo_name}/${module}/target/*.jar "gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/"
|
||||
echo "Listing ${TAG} artifacts -"
|
||||
gsutil ls "gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/" || echo Nothing to display here.
|
||||
elif [ -f ${repo_name}/target/*.jar ] && [ ${module} = 'module_less' ]
|
||||
then
|
||||
ls -al ${repo_name}/target/*.jar
|
||||
echo "Uploading artifacts to - ${repo_name}/${branch_name}/${TAG}"
|
||||
gsutil cp ${repo_name}/target/*.jar "gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/"
|
||||
echo "Uploading Archived Code to - ${repo_name}/${branch_name}/${TAG}"
|
||||
echo "Listing ${TAG} artifacts -"
|
||||
gsutil ls "gs://${env.objBucket}/${repo_name}/${branch_name}/${TAG}/${module}/" || echo Nothing to display here.
|
||||
else
|
||||
echo "No jar file found."
|
||||
fi
|
||||
"""
|
||||
}
|
||||
log.info("TAG for CD - ${TAG}")
|
||||
}
|
||||
else {
|
||||
log.info("Skipping - Artifact push. As it is not supported for ${env.BRANCH_NAME}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
env.msg = "Error in pushing artifacts to s3 bucket. Please check console output for more details - ${e}"
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Fuction for java version
|
||||
*/
|
||||
def getDockerBuildVersion(String java_version) {
|
||||
switch (java_version) {
|
||||
case 'gradle': return '8-jdk-slim-secure-multiarch_v3.0'
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Fuction to build maven docker repo
|
||||
*/
|
||||
|
||||
def buildDckr(Map config) {
|
||||
def btObj = new buTeamMapping()
|
||||
def dparam_obj = new getDockerParams()
|
||||
def constructObj = new constructTemplate()
|
||||
|
||||
def team = btObj.get_team_initials(config.team)
|
||||
def repo_name = config.repo_name
|
||||
def tag = dparam_obj.getTag(repo_name)
|
||||
def skip_test = config.skip_test ?: false
|
||||
def modules = getModules("${repo_name}")
|
||||
def excludedModules = config.excludedModules ?: []
|
||||
def deployArgo = config.deployArgo ?: false
|
||||
def repoType = config.repo_type ?: 'microservice'
|
||||
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
|
||||
env.JAVA_HOME = '/usr/lib/jvm/java-8-openjdk-amd64/'
|
||||
env.PATH = "/opt/gradle/gradle-5.6.1/bin:${env.PATH}"
|
||||
|
||||
def docker_bindings = [
|
||||
'repo_name': repo_name,
|
||||
'buildRegistry': env.buildRegistry
|
||||
]
|
||||
if (config.containsKey('copy_file')) {
|
||||
// if (env.CLOUD_PROVIDER == "AWS"){
|
||||
// def recursive = config.copy_file.recursive ? ' --recursive' : ''
|
||||
// }
|
||||
// else if (env.CLOUD_PROVIDER == "GCP"){
|
||||
// def recursive = config.copy_file.recursive ? ' -r' : ''
|
||||
// }
|
||||
def recursive = config.copy_file.recursive ? ' --recursive' : ''
|
||||
dir(repo_name) {
|
||||
dir('copied_files') {
|
||||
sh(script:"aws s3 cp${recursive} ${config.copy_file.path} .")
|
||||
}
|
||||
}
|
||||
docker_bindings['copy_file'] = true
|
||||
docker_bindings['copy_target'] = config.copy_file.target ?: '/opt/target'
|
||||
}
|
||||
else {
|
||||
docker_bindings['copy_file'] = false
|
||||
}
|
||||
|
||||
if (modules != 'module_less') {
|
||||
excludedModules.each { modules.removeElement(it) }
|
||||
}
|
||||
else {
|
||||
modules = ['module_less']
|
||||
}
|
||||
docker_bindings['arch'] = config.arch
|
||||
def java_version = config.dockerBuildVersion
|
||||
dockerBuildVersion = getDockerBuildVersion(java_version)
|
||||
docker_bindings['dockerBuildVersion'] = dockerBuildVersion
|
||||
run(config)
|
||||
if (docker_bindings.copy_file) {
|
||||
dir(repo_name) {
|
||||
dir('copied_files') {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
// delete any old data
|
||||
sh(script:'rm -rf *')
|
||||
sh(script:"aws s3 cp${docker_bindings.recursive} ${docker_bindings.copy_file_path} .")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
// delete any old data
|
||||
docker_bindings['recursive'] = '-r'
|
||||
sh(script:'rm -rf *')
|
||||
sh(script:"gsutil cp ${docker_bindings.recursive} ${docker_bindings.copy_file_path}* .")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage(stageName('Building docker images')) {
|
||||
// Login to docker
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
|
||||
}
|
||||
dir(repo_name) {
|
||||
for (module in modules) {
|
||||
docker_bindings['module'] = module
|
||||
constructObj.renderTemplate(docker_bindings,'java-Dockerfile','Dockerfile-' + module)
|
||||
def module_repo = (module == 'module_less') ? docker_repo : docker_repo + '/' + module
|
||||
if ( module == 'module_less') {
|
||||
sh(script: 'mkdir target;cp build/libs/*jar target')
|
||||
}
|
||||
else {
|
||||
sh(script: "mkdir -p ${module}/target;cp build/libs/*jar ${module}/target")
|
||||
}
|
||||
if (env.cicd_environment != 'ftr' && repoType == 'microservice') {
|
||||
sh(script: "ls target; docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module} . && docker push ${env.registry}/${module_repo}:${tag}")
|
||||
}
|
||||
else {
|
||||
log.info("Skipping Docker Build - ${env.cicd_environment} env")
|
||||
log.info("Skipping Docker Build - ${repoType} repo type")
|
||||
}
|
||||
//Remove dockerfile
|
||||
sh(script: "rm -rf Dockerfile-${module}")
|
||||
}
|
||||
}
|
||||
return [tag , deployArgo]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,568 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.buTeamMapping
|
||||
import com.homelab.utilities.constructTemplate
|
||||
import com.homelab.utilities.getDockerParams
|
||||
import com.homelab.utilities.addSSHKey
|
||||
import com.homelab.utilities.dockerUtilities
|
||||
import com.homelab.utilities.getYamlParameter
|
||||
|
||||
def removePackageLock() {
|
||||
sh(script: 'rm -rf package-lock.json')
|
||||
}
|
||||
|
||||
def buildNode(def build_cmd) {
|
||||
try {
|
||||
if (fileExists('package-lock.json')) {
|
||||
sh(script: 'npm ci')
|
||||
}
|
||||
else {
|
||||
sh(script: 'npm install')
|
||||
}
|
||||
sh(script: build_cmd)
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
env.msg = 'Error in building node packages . Please check console output for more details.'
|
||||
env.error_msg_to_db = 'Error Building Node Packages'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def getArtifactId(String repo_name) {
|
||||
dir("$repo_name") {
|
||||
if (fileExists('package.json')) {
|
||||
return sh(returnStdout: true, script: 'jq -r .name package.json').trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getAwsSecret(def secret_name, def destination_file, def team, def bu) {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws secretsmanager get-secret-value --secret-id ${secret_name} --query SecretString --output text > ${destination_file}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
getVaultSecret("vault kv get -format=json homelab/${env.cicd_environment}/${bu}/${team}/${secret_name} | jq -r .data.data > ${destination_file}")
|
||||
}
|
||||
}
|
||||
|
||||
def getNpmRc(def secret_name, def team, def bu) {
|
||||
def npmrc_file = secret_name + '-npmrc-' + env.cicd_environment
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws secretsmanager get-secret-value --secret-id ${npmrc_file} --query SecretString --output text| jq -r .HOMELAB_NPMRC_SECRET > .npmrc")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
getVaultSecret("vault kv get -format=json homelab/${env.cicd_environment}/${bu}/${team}/${npmrc_file} | jq -r .data.data.HOMELAB_NPMRC_SECRET > .npmrc")
|
||||
}
|
||||
}
|
||||
|
||||
def getPemFile(def secret_name, def team, def bu) {
|
||||
def pem_secret_name = secret_name + '-secrets-' + env.cicd_environment
|
||||
if (env.BUILD_ENV == 'stage') {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws secretsmanager get-secret-value --secret-id ${pem_secret_name} --query SecretString --output text| jq -r .public_secret_dev > 1_public_secret_dev.pem")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
getVaultSecret("vault kv get -format=json homelab/${env.cicd_environment}/${bu}/${team}/${pem_secret_name} | jq -r .data.data.public_secret_dev > 1_public_secret_dev.pem")
|
||||
}
|
||||
sh(script:"cat 1_public_secret_dev.pem | sed -e 's/-----BEGIN PUBLIC KEY-----/& \\n/' -e 's/-----END PUBLIC KEY-----/\\n-----END PUBLIC KEY-----/g' > public_secret_dev.pem")
|
||||
}
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws secretsmanager get-secret-value --secret-id ${pem_secret_name} --query SecretString --output text| jq -r .public_secret_prod > 1_public_secret_prod.pem")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
getVaultSecret("vault kv get -format=json homelab/${env.cicd_environment}/${bu}/${team}/${pem_secret_name} | jq -r .data.data.public_secret_prod > 1_public_secret_prod.pem")
|
||||
}
|
||||
|
||||
sh(script:"cat 1_public_secret_prod.pem | sed -e 's/-----BEGIN PUBLIC KEY-----/& \\n/' -e 's/-----END PUBLIC KEY-----/\\n-----END PUBLIC KEY-----/g' > public_secret_prod.pem")
|
||||
}
|
||||
|
||||
def getEnvFile(def secret_name, def team, def bu, boolean useCacPath = false) {
|
||||
def env_file = secret_name + '-env-' + env.cicd_environment
|
||||
def gcp_env_file = secret_name
|
||||
def destination_file = '.env'
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws secretsmanager get-secret-value --secret-id ${env_file} | jq --raw-output '.SecretString' | jq '.' | jq -r 'to_entries|map(\"\\(.key)=\\(.value|tostring)\")|.[]' > .env")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
def vault_path = useCacPath
|
||||
? "homelab/${env.cicd_environment}-cac/${bu}/${team}/${gcp_env_file}-client"
|
||||
: "homelab/${env.cicd_environment}/${bu}/${team}/${gcp_env_file}"
|
||||
getVaultSecret("vault kv get -format=json ${vault_path} | jq -r '.data.data | to_entries|map(\"\\(.key)=\\(.value|tostring)\")|.[]' > .env")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read appConfigEnabled from deployment yaml(s) in repo (deployments/<name>.yaml).
|
||||
* Uses config.deployment_order for deployment names, or repo_name if not set.
|
||||
* Aligns with deploy/Helm which reads appConfigEnabled from the same files.
|
||||
*/
|
||||
def isAppConfigEnabledFromDeployments(String repo_name, Map config) {
|
||||
def deploymentNames = (config.deployment_order instanceof List && !config.deployment_order.isEmpty())
|
||||
? config.deployment_order
|
||||
: [repo_name]
|
||||
def yamlObj = new getYamlParameter()
|
||||
for (def deployment in deploymentNames) {
|
||||
try {
|
||||
def depYaml = yamlObj.getParam(repo_name, "deployments/${deployment}.yaml")
|
||||
def enabled = depYaml?.appConfigEnabled
|
||||
if (enabled == true || enabled?.toString()?.equalsIgnoreCase('true')) {
|
||||
return true
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("No appConfigEnabled in ${repo_name}/deployments/${deployment}.yaml or file missing: ${e.message}")
|
||||
continue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
def getManifestJson(def secret_name, def team, def bu) {
|
||||
def manifest_file = secret_name + '-manifest-' + env.cicd_environment
|
||||
def destination_file = 'public/manifest.json'
|
||||
getAwsSecret(manifest_file, destination_file, team, bu)
|
||||
}
|
||||
|
||||
def generateExcludePattern(String excludeS3Files) {
|
||||
def files = excludeS3Files.split(/\s*,\s*/) // Split the string by comma and trim whitespace
|
||||
return '|' + files.join('|') // Join file names with '|' as an "or" operator in regex
|
||||
}
|
||||
|
||||
def shouldSkipPm2Metrics(Map config) {
|
||||
return config.skip_pm2_metrics?.toString()?.toBoolean() ?: false
|
||||
}
|
||||
|
||||
/*
|
||||
Fuction to build maven docker repo
|
||||
*/
|
||||
|
||||
def getCommitSHA(String repo_name) {
|
||||
dir(repo_name) {
|
||||
return sh(returnStdout: true, script: 'git log -1 --format=%H').trim()
|
||||
}
|
||||
}
|
||||
|
||||
def buildDckr(Map config) {
|
||||
// // Block all Node.js builds - throwing exception
|
||||
// throw new Exception("Node.js builds are currently blocked, As a precautionary measure. Due to some packages that got hacked.")
|
||||
def btObj = new buTeamMapping()
|
||||
def constructObj = new constructTemplate()
|
||||
def dparam_obj = new getDockerParams()
|
||||
def addSSHKey = new addSSHKey()
|
||||
def dockerUtilObj = new dockerUtilities()
|
||||
|
||||
def team = btObj.get_team_initials(config.team)
|
||||
def bu = btObj.get_bu_initials(config.bu)
|
||||
def repo_name = config.repo_name
|
||||
// CAC: read appConfigEnabled from deployment folder (deployments/<name>.yaml), same source as deploy/Helm
|
||||
def useCacPath = isAppConfigEnabledFromDeployments(repo_name, config)
|
||||
def build_cmd = config.build_cmd ?: 'npm run build'
|
||||
// Resolve secret_name from deployment YAML app_name.
|
||||
// For multi-deployment repos, env/secret files are kept in sync so the first valid app_name is used.
|
||||
def secret_name = repo_name
|
||||
def keep_package_lock = config.keep_package_lock != null ? config.keep_package_lock : true
|
||||
def skip_npmrc = config.skip_npmrc != null ? config.skip_npmrc : true
|
||||
if (config.secret_name) {
|
||||
secret_name = config.secret_name
|
||||
} else {
|
||||
try {
|
||||
def yamlObj = new getYamlParameter()
|
||||
def deploymentsPath = "${repo_name}/deployments"
|
||||
def yamlFilesOutput = sh(
|
||||
script: "ls ${deploymentsPath}/*.yaml 2>/dev/null | xargs -r -n1 basename",
|
||||
returnStdout: true
|
||||
).trim()
|
||||
if (yamlFilesOutput) {
|
||||
def yamlFiles = yamlFilesOutput.split('\n').collect { it.trim() }.findAll { it }
|
||||
for (yamlFile in yamlFiles) {
|
||||
def appConfig = yamlObj.getParam(deploymentsPath, yamlFile)
|
||||
def appName = appConfig?.app_name?.toString()?.trim()
|
||||
if (appName) {
|
||||
echo "Resolved secret_name to ${appName} from ${deploymentsPath}/${yamlFile}"
|
||||
secret_name = appName
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (secret_name == repo_name) {
|
||||
echo "No app_name found in deployment yamls; using repo_name: ${repo_name}"
|
||||
}
|
||||
} catch (Exception e) {
|
||||
echo "Error reading deployment yaml: ${e.getMessage()}; using repo_name: ${repo_name}"
|
||||
}
|
||||
}
|
||||
def require_mainfest = config.require_mainfest ?: false
|
||||
def pbac_enabled = config.pbac_enabled ?: false
|
||||
def pbac_scope_name = config.pbac_scope_name ?: ''
|
||||
def npm_install_arg = config.npm_install_arg ?: ''
|
||||
if (!npm_install_arg) {
|
||||
if (fileExists("${repo_name}/pnpm-lock.yaml") || build_cmd.contains('pnpm')) {
|
||||
npm_install_arg = 'npm install -g pnpm@10.33.0 && pnpm install --frozen-lockfile'
|
||||
}
|
||||
else if (!keep_package_lock || !fileExists("${repo_name}/package-lock.json")) {
|
||||
npm_install_arg = 'npm install'
|
||||
}
|
||||
// else: package-lock.json exists + keep_package_lock=true → Dockerfile uses `npm ci`
|
||||
}
|
||||
def require_pemfiles = config.require_pemfiles ?: false
|
||||
def deployArgo = config.deployArgo ?: false
|
||||
def artifactId = getArtifactId("${repo_name}")
|
||||
// def region = dparam_obj.getRegion(env.cicd_environment)
|
||||
// def registry = dparam_obj.getRegistry(env.cicd_environment)
|
||||
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
|
||||
def push_to_s3 = config.push_to_s3 ?: false
|
||||
def s3_path = config.s3_path ?: "homelab-${env.BUILD_ENV}-artifacts/${repo_name}/${env.BRANCH_NAME}"
|
||||
if (env.INFRA_ENV == 'toolchain' && env.TOOLCHAIN_ENV) {
|
||||
docker_repo = "${env.cicd_environment}/${env.TOOLCHAIN_ENV}/${team}/${repo_name.toLowerCase()}"
|
||||
log.info("Toolchain: Overriding docker_repo to ${docker_repo}")
|
||||
echo "docker_repo: ${docker_repo}"
|
||||
|
||||
if (config.s3_path && config.push_to_s3) {
|
||||
echo" {env.cicd_environment} should be stg due ot ovveride"
|
||||
s3_path = s3_path.replaceFirst("/${env.cicd_environment}/", "/${env.TOOLCHAIN_ENV}/")
|
||||
log.info("TOOLCHAIN OVERRIDE:")
|
||||
log.info(" Original S3 Path: ${config.s3_path}")
|
||||
log.info(" New Toolchain S3 Path: ${s3_path}")
|
||||
log.info(" Toolchain Env ID: ${env.TOOLCHAIN_ENV}")
|
||||
|
||||
} else if (!config.push_to_s3) {
|
||||
log.info("Toolchain: push_to_s3 is false, skipping s3_path override as it wont be pushed")
|
||||
} else if (!config.s3_path && config.push_to_s3) {
|
||||
log.info("Toolchain: s3_path is not set but push_to_s3 is true, skipping s3_path override as it wont be pushed")
|
||||
error("CRITICAL: 's3_path' is missing in config.yaml for the ${env.cicd_environment} environment. Toolchain builds require an s3_path.")
|
||||
}
|
||||
}
|
||||
if (env.TOOLCHAIN_ENV) {
|
||||
log.info("TOOLCHAIN_ENV: ${env.TOOLCHAIN_ENV}")
|
||||
}else{
|
||||
log.info("TOOLCHAIN_ENV: not set")
|
||||
}
|
||||
def local_path = config.local_path ?: 'build/'
|
||||
def acl = config.acl ? ' --acl ' + config.acl : ''
|
||||
def custom_pm2_metrics = config.custom_pm2_metrics ?: false
|
||||
def skip_pm2_metrics = shouldSkipPm2Metrics(config)
|
||||
def include_s3_files = ''
|
||||
def exclude_s3_files = ''
|
||||
if (!s3_path.endsWith('/')) {
|
||||
s3_path += '/'
|
||||
}
|
||||
if (!local_path.endsWith('/')) {
|
||||
local_path += '/'
|
||||
}
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
if (config.include_s3_files) {
|
||||
include_s3_files = ' --exclude "*"'
|
||||
def include_file_map = config.include_s3_files.split(',')
|
||||
for (pattern in include_file_map) {
|
||||
include_s3_files += ' --include "'+pattern+'"'
|
||||
}
|
||||
}
|
||||
exclude_s3_files = config.exclude_s3_files ? ' --exclude "'+config.exclude_s3_files+'"' : ''
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
if (config.include_s3_files) {
|
||||
def includeFileMap = config.include_s3_files.split(',')
|
||||
def file_types = ''
|
||||
def i = 0
|
||||
includeFileMap.each { pattern ->
|
||||
if ( i == 0 ) {
|
||||
file_types = "${pattern.replaceAll('\\*.', '')}"
|
||||
}
|
||||
else {
|
||||
file_types += "|${pattern.replaceAll('\\*.', '')}"
|
||||
}
|
||||
i++
|
||||
}
|
||||
if ( file_types == '*' ) {
|
||||
include_s3_files = '^(?!.*\\.*$)'
|
||||
}
|
||||
else {
|
||||
include_s3_files = "^(?!.*\\.(${file_types})\$)"
|
||||
}
|
||||
echo "include_s3_files is ${include_s3_files}"
|
||||
}
|
||||
else {
|
||||
include_s3_files = '^(?!.*\\.*$)'
|
||||
}
|
||||
|
||||
exclude_s3_files = config.exclude_s3_files != null && config.exclude_s3_files.trim()? generateExcludePattern(config.exclude_s3_files.trim()) : ''
|
||||
|
||||
}
|
||||
def phantomjs = config.phantomjs ?: false
|
||||
def version = config.dockerBuildVersion.split('-')[-1]
|
||||
def tag = dparam_obj.getTag(repo_name)
|
||||
// TODO: create value binding for dockerfile render
|
||||
|
||||
def skip_sonar = config.skip_sonar ?: false
|
||||
if (env.hot_fix || env.INFRA_ENV == 'toolchain') {
|
||||
skip_sonar = true
|
||||
}
|
||||
def testCMD = config.testCMD ?: 'test-report'
|
||||
def scmType = 'branch'
|
||||
def commit_sha = getCommitSHA(config.repo_name)
|
||||
def docker_bindings = [
|
||||
'buildRegistry': env.buildRegistry,
|
||||
'version': version,
|
||||
'build_cmd': build_cmd,
|
||||
'push_to_s3': push_to_s3,
|
||||
's3_path': s3_path,
|
||||
'local_path': local_path,
|
||||
'include_s3_files': include_s3_files,
|
||||
'exclude_s3_files': exclude_s3_files,
|
||||
'acl': acl,
|
||||
'phantomjs': phantomjs,
|
||||
'skip_sonar': skip_sonar,
|
||||
'testCMD': testCMD,
|
||||
'CLOUD_PROVIDER': env.CLOUD_PROVIDER,
|
||||
'npm_install_arg': npm_install_arg
|
||||
]
|
||||
docker_bindings['arch'] = config.arch
|
||||
docker_bindings['pbac_enabled'] = pbac_enabled
|
||||
docker_bindings['pbac_scope_name'] = pbac_scope_name ?: ''
|
||||
// Map BUILD_ENV to pbac format (stg/int/prd)
|
||||
def pbac_env = env.cicd_environment
|
||||
docker_bindings['pbac_env'] = pbac_env
|
||||
docker_bindings['useCacPath'] = useCacPath
|
||||
|
||||
// Validate pbac configuration
|
||||
if (pbac_enabled && !pbac_scope_name) {
|
||||
error('pbac_scope_name is required when pbac_enabled is true in config.yaml')
|
||||
}
|
||||
|
||||
stage(stageName('Creating build files')) {
|
||||
dir(repo_name) {
|
||||
if (!keep_package_lock) {
|
||||
removePackageLock()
|
||||
}
|
||||
if (!skip_npmrc) {
|
||||
getNpmRc(secret_name, team, bu)
|
||||
}
|
||||
|
||||
getEnvFile(secret_name, team, bu, useCacPath)
|
||||
|
||||
if (require_pemfiles) {
|
||||
getPemFile(secret_name, team, bu)
|
||||
}
|
||||
if (require_mainfest) {
|
||||
getManifestJson(secret_name, team, bu)
|
||||
}
|
||||
def npm_registry = ''
|
||||
if (fileExists('.npmrc')) {
|
||||
npm_registry = sh(
|
||||
returnStdout: true,
|
||||
script: "grep '^@homelab:registry=' .npmrc | head -1 | cut -d'=' -f2 | tr -d '\\r\\n'"
|
||||
).trim()
|
||||
echo "Detected npm registry: ${npm_registry ?: 'default (none found)'}"
|
||||
} else {
|
||||
echo ".npmrc not found, skipping registry extraction"
|
||||
}
|
||||
docker_bindings['npm_registry'] = npm_registry
|
||||
}
|
||||
}
|
||||
|
||||
if (skip_sonar){
|
||||
stage(stageName('Checking quality gate')){
|
||||
log.info("Skipping the quality gate check as sonar scan is skipped or this is a hotfix ")
|
||||
}
|
||||
}
|
||||
stage(stageName('Building docker images')) {
|
||||
// Login to docker
|
||||
try {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error in Docker login'
|
||||
env.error_msg_to_db = 'Error in Docker login'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
try {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws ecr describe-repositories --region ${env.region} --repository-names ${docker_repo} || aws ecr create-repository --region ${env.region} --repository-name ${docker_repo}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
echo 'Skipping - Registry Creation in GCP.'
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = "Error in creating ECR repository ${docker_repo}"
|
||||
env.error_msg_to_db = "Error in creating ECR repository ${docker_repo}"
|
||||
currentBuild.result = 'FAILURE'
|
||||
}
|
||||
try {
|
||||
dir(repo_name) {
|
||||
addSSHKey.create()
|
||||
withCredentials([usernamePassword(credentialsId: "svc-devops-homelab-token", usernameVariable: 'GIT_USER', passwordVariable: 'GIT_TOKEN')]) {
|
||||
sh """
|
||||
echo "GITHUB_TOKEN=${GIT_TOKEN}" >> .env
|
||||
echo "GIT_COMMIT_SHA='${commit_sha}'" >> .env
|
||||
"""
|
||||
}
|
||||
withCredentials([string(credentialsId: env.sonarToken, variable: 'TOKEN')]) {
|
||||
sh """
|
||||
echo "SONAR_HOST_URL='${env.sonarURL}'" >> .env
|
||||
echo "SONAR_TOKEN='${TOKEN}'" >> .env
|
||||
echo "SONAR_WS_TIMEOUT=120" >> .env
|
||||
"""
|
||||
if (env.CHANGE_ID) {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
sh "git fetch origin ${env.CHANGE_TARGET}:refs/remotes/origin/${env.CHANGE_TARGET}"
|
||||
}
|
||||
sh """
|
||||
echo "SONAR_CHANGE_ID='${env.CHANGE_ID}'" >> .env
|
||||
echo "SONAR_CHANGE_BRANCH='${env.CHANGE_BRANCH}'" >> .env
|
||||
echo "SONAR_CHANGE_TARGET='${env.CHANGE_TARGET}'" >> .env
|
||||
"""
|
||||
scmType = 'pr'
|
||||
}
|
||||
else {
|
||||
sh """
|
||||
echo "SONAR_BRANCH_NAME='${env.BRANCH_NAME}'" >> .env
|
||||
"""
|
||||
}
|
||||
echo 'Make sure to update script section in package.json for Sonar Analysis to be successfull.'
|
||||
docker_bindings['scmType'] = scmType
|
||||
}
|
||||
if (!fileExists('Dockerfile')) {
|
||||
constructObj.renderTemplate(docker_bindings, 'node-Dockerfile', 'Dockerfile-' + artifactId)
|
||||
sh "cat Dockerfile-${artifactId}"
|
||||
if (push_to_s3) {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh """
|
||||
echo 'FROM amazon/aws-cli:2.2.0 as push_env' >> Dockerfile-${artifactId}
|
||||
echo 'COPY --from=build-env /usr/src/app /app' >> Dockerfile-${artifactId}
|
||||
echo 'RUN ls -al && aws s3 cp /app/${local_path} s3://${s3_path}${include_s3_files}${exclude_s3_files} --recursive --cache-control max-age=31536000,public${acl}' >> Dockerfile-${artifactId}
|
||||
"""
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP' ) {
|
||||
sh """
|
||||
echo 'FROM asia-southeast1-docker.pkg.dev/homelab-devops-admin-0622/admin/devops/google-cloud-sdk:458.0.0-alpine as push_env' >> Dockerfile-${artifactId}
|
||||
echo 'COPY --from=build-env /usr/src/app /app' >> Dockerfile-${artifactId}
|
||||
# echo 'RUN ls -al /app && find /app/${local_path} -type f \\( -name "*.js" -o -name "*.map" \\) && find /app/${local_path} -type f \\( -name "*.js" -o -name "*.map" \\) | wc -l && gsutil -m cp -r /app/${local_path}* gs://${s3_path}' >> Dockerfile-${artifactId}
|
||||
echo "RUN ls -al /app && gsutil -m rsync -r -x \'${include_s3_files} ${exclude_s3_files}\' /app/${local_path} gs://${s3_path}" >> Dockerfile-${artifactId}
|
||||
echo "RUN if [ -f /app/${local_path}index.html ]; then gsutil cp /app/${local_path}index.html gs://${s3_path}; fi" >> Dockerfile-${artifactId}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
if (phantomjs) {
|
||||
sh """
|
||||
echo 'FROM build-env' >> Dockerfile-${artifactId}
|
||||
echo 'RUN apt-get update && apt-get install -y libfontconfig' >> Dockerfile-${artifactId}
|
||||
echo 'WORKDIR /usr/src/app' >> Dockerfile-${artifactId}
|
||||
echo 'RUN npm install pm2 -g' >> Dockerfile-${artifactId}
|
||||
echo 'RUN pm2 install pm2-metrics' >> Dockerfile-${artifactId}
|
||||
if [ "${custom_pm2_metrics}" = "true" ]; then
|
||||
echo 'RUN pm2 install pm2-prom-module' >> Dockerfile-${artifactId}
|
||||
echo 'RUN pm2 set pm2-prom-module:port 9200' >> Dockerfile-${artifactId}
|
||||
echo 'RUN pm2 restart pm2-prom-module' >> Dockerfile-${artifactId}
|
||||
fi
|
||||
echo 'RUN rm -rf /root/.ssh/id_rsa && apt-get remove -y git openssh-client bzip2' >> Dockerfile-${artifactId}
|
||||
"""
|
||||
}
|
||||
else {
|
||||
sh """
|
||||
echo 'FROM ${env.buildRegistry}/build/node:${version}-alpine-secure-multiarch_v1.0' >> Dockerfile-${artifactId}
|
||||
echo 'RUN npm install pm2 -g' >> Dockerfile-${artifactId}
|
||||
if [ "${skip_pm2_metrics}" != "true" ]; then
|
||||
echo 'RUN pm2 install pm2-metrics' >> Dockerfile-${artifactId}
|
||||
fi
|
||||
if [ "${custom_pm2_metrics}" = "true" ]; then
|
||||
echo 'RUN pm2 install pm2-prom-module' >> Dockerfile-${artifactId}
|
||||
echo 'RUN pm2 set pm2-prom-module:port 9200' >> Dockerfile-${artifactId}
|
||||
echo 'RUN pm2 restart pm2-prom-module' >> Dockerfile-${artifactId}
|
||||
fi
|
||||
echo 'WORKDIR /app' >> Dockerfile-${artifactId}
|
||||
echo 'COPY --from=build-env /usr/src/app /app' >> Dockerfile-${artifactId}
|
||||
"""
|
||||
}
|
||||
if (docker_bindings['useCacPath']) {
|
||||
sh "echo 'RUN truncate -s 0 .env' >> Dockerfile-${artifactId}"
|
||||
}
|
||||
sh "cat Dockerfile-${artifactId}"
|
||||
def imageList = "${env.buildRegistry}/build/node:${version}-alpine-secure-multiarch_v1.0 " +
|
||||
"asia-southeast1-docker.pkg.dev/homelab-devops-admin-0622/admin/devops/google-cloud-sdk:458.0.0-alpine " +
|
||||
"${env.buildRegistry}/build/node:${version}-slim-secure-multiarch_v2.0"
|
||||
|
||||
sh """
|
||||
max_attempts=5
|
||||
images=\"$imageList\"
|
||||
|
||||
for image in \$images; do
|
||||
attempt=1
|
||||
until docker pull \"\$image\"; do
|
||||
if [ \$attempt -eq \$max_attempts ]; then
|
||||
echo \"Failed to pull \$image after \$attempt attempts.\"
|
||||
exit 1
|
||||
fi
|
||||
echo \"Pull failed for \$image, retrying in 2 seconds... (Attempt \$attempt/\$max_attempts)\"
|
||||
attempt=\$((attempt + 1))
|
||||
sleep 2
|
||||
done
|
||||
done
|
||||
"""
|
||||
if (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') {
|
||||
def docker_cmd = "export DOCKER_BUILDKIT=0;docker build --tag ${env.registry}/${docker_repo}:${tag} -f Dockerfile-${artifactId} . "
|
||||
sh(script: docker_cmd)
|
||||
dockerUtilObj.retryDockerPush("docker push ${env.registry}/${docker_repo}:${tag}")
|
||||
}
|
||||
else {
|
||||
def docker_cmd = "export DOCKER_BUILDKIT=0;docker build --tag ${env.registry}/${docker_repo}:${tag} -f Dockerfile-${artifactId} ."
|
||||
sh(script: docker_cmd)
|
||||
}
|
||||
//Remove dockerfile
|
||||
sh(script: "rm -rf Dockerfile-${artifactId}")
|
||||
}
|
||||
else {
|
||||
if (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') {
|
||||
sh(script: "export DOCKER_BUILDKIT=0;docker build --tag ${env.registry}/${docker_repo}:${tag} . ")
|
||||
dockerUtilObj.retryDockerPush("docker push ${env.registry}/${docker_repo}:${tag}")
|
||||
}
|
||||
else {
|
||||
log.info("Skipping Docker Push - ${env.cicd_environment} env")
|
||||
sh(script: "export DOCKER_BUILDKIT=0;docker build --tag ${env.registry}/${docker_repo}:${tag} .")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error in building DockerFile Or Pushing To ECR'
|
||||
env.error_msg_to_db = env.msg
|
||||
log.error(env.msg + '\n' + e.toString())
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
return [tag , deployArgo]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def getVaultSecret(String vault_cmd) {
|
||||
log.info("Fetching secrets from Vault with vault-${env.cicd_environment}-token with CMD - ${vault_cmd}")
|
||||
if (env.INFRA_ENV == 'toolchain' && env.TOOLCHAIN_ENV) {
|
||||
boolean skipToolchainOverride = vault_cmd.contains("npmrc") || vault_cmd.contains("-secrets-")
|
||||
if (!skipToolchainOverride) {
|
||||
vault_cmd = vault_cmd.replace("homelab/${env.cicd_environment}-cac/", "homelab/toolchain/${env.TOOLCHAIN_ENV}/${env.cicd_environment}-cac/")
|
||||
vault_cmd = vault_cmd.replace("homelab/${env.cicd_environment}/", "homelab/toolchain/${env.TOOLCHAIN_ENV}/${env.cicd_environment}/")
|
||||
log.info("TOOLCHAIN OVERRIDE (Application Config):")
|
||||
log.info(" New Toolchain Vault CMD: ${vault_cmd}")
|
||||
} else {
|
||||
log.info("TOOLCHAIN: Reading from standard (non-toolchain) Vault path for npmrc / pem secret.")
|
||||
}
|
||||
}
|
||||
|
||||
withCredentials([string(credentialsId: "${env.vaultToken}", variable: 'TOKEN')]) {
|
||||
env.VAULT_ADDR = "${env.vaultURL}"
|
||||
env.VAULT_TOKEN = "${TOKEN}"
|
||||
sh(script:"${vault_cmd}")
|
||||
env.VAULT_TOKEN = 'empty'
|
||||
env.VAULT_ADDR = env.VAULT_TOKEN
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.homelab.stages
|
||||
|
||||
def run(String build_tool){
|
||||
try {
|
||||
switch (build_tool) {
|
||||
case 'maven':
|
||||
return new buildMaven()
|
||||
break;
|
||||
case 'docker':
|
||||
return new buildDocker()
|
||||
break;
|
||||
case ~/^maven-.*/:
|
||||
return new buildMaven()
|
||||
break;
|
||||
case ~/^python-.*/:
|
||||
return new buildPython()
|
||||
break;
|
||||
case ~/^node-.*/:
|
||||
return new buildNode()
|
||||
break;
|
||||
case ~/^rust.*/:
|
||||
return new buildRust()
|
||||
break;
|
||||
case ~/^go.*/:
|
||||
return new buildGo()
|
||||
break;
|
||||
case 'gradle':
|
||||
return new buildGradle()
|
||||
break;
|
||||
case 'php':
|
||||
return new buildPhp()
|
||||
default:
|
||||
return defaultBuild()
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error in selecting the build tool (check the spell) Error: ' + e.toString()
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.buTeamMapping
|
||||
import com.homelab.utilities.constructTemplate
|
||||
import com.homelab.utilities.getDockerParams
|
||||
|
||||
def buildDckr(Map config){
|
||||
def btObj = new buTeamMapping()
|
||||
def constructObj = new constructTemplate()
|
||||
def dparam_obj = new getDockerParams()
|
||||
|
||||
def team = btObj.get_team_initials(config.team)
|
||||
def repo_name = config.repo_name
|
||||
def deployArgo = config.deployArgo ?: false
|
||||
// def region = dparam_obj.getRegion(env.cicd_environment)
|
||||
// def registry = dparam_obj.getRegistry(env.cicd_environment)
|
||||
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
|
||||
def tag = dparam_obj.getTag(repo_name)
|
||||
def docker_bindings = [
|
||||
"repo_name": repo_name,
|
||||
'buildRegistry': env.buildRegistry
|
||||
|
||||
]
|
||||
if (config.containsKey("copy_file")){
|
||||
if (env.CLOUD_PROVIDER == 'AWS'){
|
||||
def recursive = config.copy_file.recursive ? " --recursive" : ""
|
||||
dir(repo_name){
|
||||
dir('copied_files'){
|
||||
sh(script:"aws s3 cp${recursive} ${config.copy_file.path} .")
|
||||
}
|
||||
}
|
||||
} else if (env.CLOUD_PROVIDER == 'GCP'){
|
||||
def recursive = config.copy_file.recursive ? " -r" : ""
|
||||
dir(repo_name){
|
||||
dir('copied_files'){
|
||||
sh(script:"gsutil cp${recursive} ${config.copy_file.path} .")
|
||||
}
|
||||
}
|
||||
}
|
||||
docker_bindings["copy_file"] = true
|
||||
docker_bindings["copy_target"] = config.copy_file.target ?: "/opt/target"
|
||||
}
|
||||
else{
|
||||
docker_bindings["copy_file"] = false
|
||||
}
|
||||
stage(stageName('Building docker images')){
|
||||
// Login to docker
|
||||
try {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
env.msg = "Error in Docker login"
|
||||
env.error_msg_to_db = "Error in Docker login"
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
try{
|
||||
if (env.CLOUD_PROVIDER == "AWS"){
|
||||
sh(script:"aws ecr describe-repositories --region ${env.region} --repository-names ${docker_repo} || aws ecr create-repository --region ${env.region} --repository-name ${docker_repo}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP'){
|
||||
echo "Skipping - Registry Creation in GCP."
|
||||
}
|
||||
}
|
||||
catch(Exception e){
|
||||
env.msg = "Error in creating ECR repository ${docker_repo}"
|
||||
env.error_msg_to_db = "Error in creating ECR repository ${docker_repo}"
|
||||
currentBuild.result = "FAILURE"
|
||||
}
|
||||
try {
|
||||
dir(repo_name){
|
||||
if(!fileExists("Dockerfile")){
|
||||
constructObj.renderTemplate(docker_bindings,'php-Dockerfile','Dockerfile-php')
|
||||
if (env.cicd_environment != 'ftr') {
|
||||
sh(script: "docker build --tag ${env.registry}/${docker_repo}:${tag} -f Dockerfile-php . && docker push ${env.registry}/${docker_repo}:${tag}")
|
||||
}
|
||||
else {
|
||||
log.info("Skipping Docker builds for PHP in ${env.cicd_environment} env")
|
||||
}
|
||||
|
||||
//Remove dockerfile
|
||||
sh(script: "rm -rf Dockerfile-php")
|
||||
}
|
||||
else {
|
||||
sh(script: "docker build --tag ${env.registry}/${docker_repo}:${tag} . && docker push ${env.registry}/${docker_repo}:${tag}")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = "Error in building DockerFile Or Pushing To artifact registry"
|
||||
env.error_msg_to_db = "Error in building DockerFile Or Pushing To image registry"
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
return [tag , deployArgo]
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.buTeamMapping
|
||||
import com.homelab.utilities.constructTemplate
|
||||
import com.homelab.utilities.getDockerParams
|
||||
import com.homelab.utilities.addSSHKey
|
||||
import com.homelab.utilities.dockerUtilities
|
||||
/*
|
||||
Function to get the version from pom.xml
|
||||
input arguments:
|
||||
repo_name: String parameter to change the directory where pom.xml is located
|
||||
*/
|
||||
// def getArtifactId(String repo_name){
|
||||
// dir("$repo_name"){
|
||||
// if (fileExists("package.json")) {
|
||||
// return sh(returnStdout: true, script: 'jq -r .name package.json').trim()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// def getAwsSecret(def secret_name, def destination_file){
|
||||
// sh (script:"aws secretsmanager get-secret-value --secret-id ${secret_name} --query SecretString --output text > ${destination_file}")
|
||||
// }
|
||||
|
||||
// def getCommitid(String repo_name){
|
||||
// dir("$repo_name"){
|
||||
// return sh(returnStdout: true, script: 'git log -1 --format=%h').trim()
|
||||
// }
|
||||
// }
|
||||
|
||||
// def getNpmRc(def secret_name){
|
||||
// def npmrc_file = secret_name + "-npmrc"
|
||||
// sh (script:"aws secretsmanager get-secret-value --secret-id ${npmrc_file} --query SecretString --output text| jq -r .HOMELAB_NPMRC_SECRET > .npmrc")
|
||||
// }
|
||||
|
||||
// def getPemFile(def secret_name){
|
||||
// def pem_secret_name = secret_name + "-secrets"
|
||||
// if(env.BUILD_ENV == 'stage'){
|
||||
// sh (script:"aws secretsmanager get-secret-value --secret-id ${pem_secret_name} --query SecretString --output text| jq -r .public_secret_dev > 1_public_secret_dev.pem")
|
||||
// sh (script:"cat 1_public_secret_dev.pem | sed -e 's/-----BEGIN PUBLIC KEY-----/& \\n/' -e 's/-----END PUBLIC KEY-----/\\n-----END PUBLIC KEY-----/g' > public_secret_dev.pem")
|
||||
// }
|
||||
// sh (script:"aws secretsmanager get-secret-value --secret-id ${pem_secret_name} --query SecretString --output text| jq -r .public_secret_prod > 1_public_secret_prod.pem")
|
||||
// sh (script:"cat 1_public_secret_prod.pem | sed -e 's/-----BEGIN PUBLIC KEY-----/& \\n/' -e 's/-----END PUBLIC KEY-----/\\n-----END PUBLIC KEY-----/g' > public_secret_prod.pem")
|
||||
// }
|
||||
|
||||
// def getEnvFile(def secret_name){
|
||||
// def env_file = secret_name + "-env"
|
||||
// def destination_file = ".env"
|
||||
// sh(script:"aws secretsmanager get-secret-value --secret-id ${env_file} | jq --raw-output '.SecretString' | jq '.' | jq -r 'to_entries|map(\"\\(.key)=\\(.value|tostring)\")|.[]' > .env")
|
||||
// }
|
||||
|
||||
// def getManifestJson(def secret_name){
|
||||
// def manifest_file = secret_name+"-manifest"
|
||||
// def destination_file = "public/manifest.json"
|
||||
// getAwsSecret(manifest_file,destination_file)
|
||||
// }
|
||||
/*
|
||||
Fuction to build maven docker repo
|
||||
*/
|
||||
|
||||
def buildDckr(Map config) {
|
||||
def btObj = new buTeamMapping()
|
||||
def constructObj = new constructTemplate()
|
||||
def dparam_obj = new getDockerParams()
|
||||
def addSSHKey = new addSSHKey()
|
||||
|
||||
def team = btObj.get_team_initials(config.team)
|
||||
def repo_name = config.repo_name
|
||||
def deployArgo = config.deployArgo ?: false
|
||||
def modules_requirements_file = config.modules_requirements_file ?: 'requirements.txt'
|
||||
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
|
||||
def tag = dparam_obj.getTag(repo_name)
|
||||
if (env.INFRA_ENV == 'toolchain') {
|
||||
def dockerUtilObj = new dockerUtilities()
|
||||
if (dockerUtilObj.imageExists(env.registry, docker_repo, tag)) {
|
||||
log.info("Toolchain: Image found in registry for ${docker_repo}:${tag}. Skipping build step.")
|
||||
return [tag, deployArgo]
|
||||
} else {
|
||||
log.info("Toolchain: Image missing for ${docker_repo}:${tag}. Proceeding with build.")
|
||||
}
|
||||
}
|
||||
// TODO: create value binding for dockerfile render
|
||||
def docker_bindings = [
|
||||
'buildRegistry': env.buildRegistry,
|
||||
'modules_requirements_file': modules_requirements_file,
|
||||
'arch': config.arch,
|
||||
'buildRegistry': env.buildRegistry
|
||||
]
|
||||
docker_bindings['arch'] = config.arch
|
||||
// stage("Create build files"){
|
||||
// dir(repo_name){
|
||||
// getNpmRc(secret_name)
|
||||
// getEnvFile(secret_name)
|
||||
// if (require_pemfiles){getPemFile(secret_name)}
|
||||
// if (require_mainfest){getManifestJson(secret_name)}
|
||||
// }
|
||||
// }
|
||||
stage(stageName('Building docker images')) {
|
||||
// Login to docker
|
||||
try {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error in Docker login'
|
||||
env.error_msg_to_db = 'Error in Docker login'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
if (config.containsKey('copy_file')) {
|
||||
def recursive = config.copy_file.recursive ? ' --recursive' : ''
|
||||
docker_bindings['copy_file_path'] = config.copy_file.path
|
||||
dir(repo_name) {
|
||||
dir('copied_files') {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
// delete any old data
|
||||
sh(script:'rm -rf *')
|
||||
sh(script:"aws s3 cp${recursive} ${config.copy_file.path} .")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
// delete any old data
|
||||
docker_bindings['recursive'] = '-r'
|
||||
sh(script:'rm -rf *')
|
||||
sh(script:"gsutil cp ${docker_bindings.recursive} ${docker_bindings.copy_file_path} .")
|
||||
}
|
||||
}
|
||||
}
|
||||
docker_bindings['copy_file'] = true
|
||||
docker_bindings['copy_target'] = config.copy_file.target ?: '/app/'
|
||||
}
|
||||
else {
|
||||
docker_bindings['copy_file'] = false
|
||||
}
|
||||
try {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws ecr describe-repositories --region ${env.region} --repository-names ${docker_repo} || aws ecr create-repository --region ${env.region} --repository-name ${docker_repo}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
echo 'Skipping - Registry Creation in GCP.'
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = "Error in creating ECR repository ${docker_repo}"
|
||||
env.error_msg_to_db = "Error in creating ECR repository ${docker_repo}"
|
||||
currentBuild.result = env.FAILURE
|
||||
}
|
||||
try {
|
||||
dir(repo_name) {
|
||||
addSSHKey.create()
|
||||
if (!fileExists('Dockerfile')) {
|
||||
// print(config)
|
||||
constructObj.renderTemplate(docker_bindings,config.dockerBuildVersion+'-Dockerfile','Dockerfile-'+repo_name)
|
||||
sh "cat Dockerfile-${repo_name}"
|
||||
// withCredentials([string(credentialsId: 'homelab-github-ssh-prv-key', variable: 'SSH_PRIVATE_KEY_S')]) {
|
||||
// withCredentials(bindings: [sshUserPrivateKey(credentialsId: 'homelab-ssh-github-key', \
|
||||
// keyFileVariable: 'SSH_PRIVATE_KEY', \
|
||||
// passphraseVariable: '', \
|
||||
// usernameVariable: '')]) {
|
||||
//withCredentials([string(credentialsId: 'git_private_key', variable: 'gitkey')]) {
|
||||
// sh """
|
||||
// set +x
|
||||
// docker build --tag ${env.registry}/${docker_repo}:${tag} --build-arg SSH_PRIVATE_KEY="\$(cat ~/.ssh/id_github_jenkins)" -f "Dockerfile-${repo_name}" . && docker push ${env.registry}/${docker_repo}:${tag}
|
||||
// set -x
|
||||
// """
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh """
|
||||
set +x
|
||||
docker build --tag ${env.registry}/${docker_repo}:${tag} --build-arg SSH_PRIVATE_KEY="\$(cat ~/.ssh/id_github_jenkins)" -f "Dockerfile-${repo_name}" .
|
||||
docker push ${env.registry}/${docker_repo}:${tag}
|
||||
set -x
|
||||
"""
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
if (env.cicd_environment != 'ftr' || env.INFRA_ENV == 'toolchain') {
|
||||
sh """
|
||||
set +x
|
||||
docker build --tag ${env.registry}/${docker_repo}:${tag} --build-arg SSH_PRIVATE_KEY="\$(cat ~/.ssh/id_github_jenkins)" -f "Dockerfile-${repo_name}" .
|
||||
docker push ${env.registry}/${docker_repo}:${tag}
|
||||
set -x
|
||||
"""
|
||||
}
|
||||
else {
|
||||
log.info("Skipping Docker builds for Python in ${env.cicd_environment} env")
|
||||
}
|
||||
}
|
||||
// }
|
||||
// }
|
||||
//Remove dockerfile
|
||||
sh(script: "rm -rf Dockerfile-${repo_name}")
|
||||
}
|
||||
else {
|
||||
sh "cat Dockerfile"
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} --push .")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh """
|
||||
set +x
|
||||
docker build --tag ${env.registry}/${docker_repo}:${tag} --build-arg SSH_PRIVATE_KEY="\$(cat ~/.ssh/id_github_jenkins)" -f "Dockerfile" .
|
||||
docker push ${env.registry}/${docker_repo}:${tag}
|
||||
set -x
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = "Error in building DockerFile Or Pushing To ECR. For Full Error Details - ${e}"
|
||||
env.error_msg_to_db = 'Error in building DockerFile Or Pushing To ECR'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
return [tag , deployArgo]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.buTeamMapping
|
||||
import com.homelab.utilities.constructTemplate
|
||||
import com.homelab.utilities.getDockerParams
|
||||
import com.homelab.utilities.addSSHKey
|
||||
|
||||
|
||||
|
||||
def buildDckr(Map config) {
|
||||
env.RUSTPRIVATE = 'github.com/Homelab'
|
||||
def btObj = new buTeamMapping()
|
||||
def constructObj = new constructTemplate()
|
||||
def dparam_obj = new getDockerParams()
|
||||
def addSSHKey = new addSSHKey()
|
||||
def deployArgo = config.deployArgo ?: true
|
||||
|
||||
def team = btObj.get_team_initials(config.team)
|
||||
def modules = config.modules ?: ['module_less']
|
||||
def repo_name = config.repo_name
|
||||
def docker_repo = "${env.cicd_environment}/${team}/${repo_name.toLowerCase()}"
|
||||
def tag = dparam_obj.getTag(repo_name)
|
||||
def buildx = config.containsKey('buildx') ? config.buildx : true
|
||||
def docker_bindings = [:]
|
||||
def skip_sonar = config.skip_sonar ?: false
|
||||
def version = config.dockerBuildVersion.split('-')[-1]
|
||||
def repoType = config.repo_type ?: 'microservice'
|
||||
docker_bindings['version'] = version
|
||||
docker_bindings['base_dir'] = config.base_dir ?: false
|
||||
docker_bindings['buildRegistry'] = env.buildRegistry
|
||||
docker_bindings['build_packages'] = getSystemPackages(config, 'build_packages')
|
||||
docker_bindings['runtime_packages'] = getSystemPackages(config, 'runtime_packages')
|
||||
stage('Build docker images') {
|
||||
|
||||
|
||||
// Login to docker
|
||||
try {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws ecr get-login-password --region ${env.region} | docker login --username AWS --password-stdin ${env.registry}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
|
||||
sh(script:'gcloud auth configure-docker asia-southeast1-docker.pkg.dev --quiet')
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error in Docker login'
|
||||
env.error_msg_to_db = env.msg
|
||||
log.error(env.msg + '. Error: ' + e.toString())
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
if (config.containsKey('copy_file')) {
|
||||
def recursive = config.copy_file.recursive ? ' --recursive' : ''
|
||||
dir(repo_name) {
|
||||
dir('copied_files') {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws s3 cp${recursive} ${config.copy_file.path} .")
|
||||
} else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script:"gsutil cp${recursive} ${config.copy_file.path} .")
|
||||
}
|
||||
}
|
||||
}
|
||||
docker_bindings['copy_file'] = true
|
||||
docker_bindings['copy_target'] = config.copy_file.target ?: '/app/'
|
||||
docker_bindings['base_dir'] = config.base_dir ?: false
|
||||
}
|
||||
else {
|
||||
docker_bindings['copy_file'] = false
|
||||
}
|
||||
|
||||
try {
|
||||
dir(repo_name) {
|
||||
sonar_scan(repo_name, skip_sonar, version )
|
||||
|
||||
if (repoType != 'microservice') {
|
||||
log.info("Skipping Docker build for ${repoType} repo type")
|
||||
return [tag, deployArgo]
|
||||
}
|
||||
|
||||
addSSHKey.create()
|
||||
if (!fileExists('Dockerfile')) {
|
||||
for (module in modules) {
|
||||
def module_name = (module instanceof LinkedHashMap) ? module.keySet()[0] : module
|
||||
docker_bindings['module_property'] = (module instanceof LinkedHashMap) ? module[module_name] : [ : ]
|
||||
docker_bindings['module'] = module_name
|
||||
docker_bindings['binary_name'] = (module == 'module_less') ? repo_name : module_name
|
||||
constructObj.renderTemplate(docker_bindings, 'rust-Dockerfile', 'Dockerfile-' + module_name)
|
||||
sh "cat Dockerfile-${module_name}"
|
||||
module_repo = (module == 'module_less') ? docker_repo : docker_repo + '/' + module_name
|
||||
try {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script:"aws ecr describe-repositories --region ${env.region} --repository-names ${module_repo} || aws ecr create-repository --region ${env.region} --repository-name ${module_repo}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
echo 'Skipping - Registry Creation in GCP.'
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = "Error in creating ECR repository ${module_repo}"
|
||||
env.error_msg_to_db = "Error in creating ECR repository ${module_repo}"
|
||||
currentBuild.result = 'FAILURE'
|
||||
}
|
||||
if (buildx) {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script: "set +x && docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} --push .")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
if (env.cicd_environment != 'ftr') {
|
||||
sh(script: "set +x && docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} . && docker push ${env.registry}/${module_repo}:${tag}")
|
||||
}
|
||||
else {
|
||||
log.info("Skipping Docker Push - ${env.cicd_environment} env")
|
||||
sh(script: "set +x && docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} .")
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script: "set +x && docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} . && docker push ${env.registry}/${module_repo}:${tag}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
if (env.cicd_environment != 'ftr') {
|
||||
sh(script: """
|
||||
set +x
|
||||
# Setup buildx for ARM64 builds
|
||||
docker buildx rm mybuilder || true
|
||||
docker buildx create --name mybuilder --driver docker-container --bootstrap
|
||||
docker buildx use mybuilder
|
||||
|
||||
# Build for ARM64
|
||||
docker buildx build --platform linux/arm64 --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} --push .
|
||||
""")
|
||||
}
|
||||
else {
|
||||
log.info("Skipping Docker Push - ${env.cicd_environment} env")
|
||||
sh(script: "set +x && docker build --tag ${env.registry}/${module_repo}:${tag} -f Dockerfile-${module_name} .")
|
||||
}
|
||||
}
|
||||
}
|
||||
//Remove dockerfile
|
||||
sh(script: "rm -rf Dockerfile-${module_name}")
|
||||
}
|
||||
}
|
||||
else {
|
||||
sh "cat Dockerfile"
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} --push .")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} --push .")
|
||||
if (env.cicd_environment != 'ftr') {
|
||||
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} --push .")
|
||||
}
|
||||
else {
|
||||
log.info("Skipping Docker Push - ${env.cicd_environment} env")
|
||||
sh(script: "docker buildx build --platform linux/arm64,linux/amd64 --tag ${env.registry}/${docker_repo}:${tag} .")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error in building DockerFile Or Pushing To ECR'
|
||||
env.error_msg_to_db = env.msg
|
||||
log.error(env.msg + '. Error: ' + e.toString())
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
return [tag, deployArgo]
|
||||
}
|
||||
}
|
||||
|
||||
// Services may need native libraries that are not part of a shared base image
|
||||
// (for example, database client headers used by a crate's build script). The
|
||||
// keys are language-neutral for future reuse; currently only the Rust builder
|
||||
// consumes them. Restrict values to Debian package names so config data cannot
|
||||
// alter the rendered Dockerfile instruction.
|
||||
def getSystemPackages(Map config, String key) {
|
||||
def configuredPackages = config[key]
|
||||
if (configuredPackages == null) {
|
||||
return []
|
||||
}
|
||||
if (!(configuredPackages instanceof List)) {
|
||||
throw new IllegalArgumentException("${key} must be a YAML list of system package names")
|
||||
}
|
||||
|
||||
def packages = configuredPackages.collect { packageName -> packageName?.toString()?.trim() }
|
||||
if (packages.any { packageName -> !packageName || !(packageName ==~ /^[a-z0-9][a-z0-9+.-]*$/) }) {
|
||||
throw new IllegalArgumentException("${key} contains an invalid system package name")
|
||||
}
|
||||
return packages.unique()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Fuction to execute sonar scan
|
||||
Input Arguments:
|
||||
repo_name: repository name
|
||||
skip_sonar: boolena parameter to skip sonar scan
|
||||
*/
|
||||
|
||||
def sonar_scan(String repo_name, boolean skip_sonar , String version ) {
|
||||
try {
|
||||
stage('Run sonar scan') {
|
||||
if (!skip_sonar) {
|
||||
|
||||
withSonarQubeEnv('sonarqube-test') {
|
||||
if (env.CHANGE_ID) {
|
||||
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
sh "git fetch origin ${env.CHANGE_TARGET}:refs/remotes/origin/${env.CHANGE_TARGET}"
|
||||
}
|
||||
sh(script: "mvn sonar:sonar -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repo_name} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
sh "git fetch origin ${env.CHANGE_TARGET}:refs/remotes/origin/${env.CHANGE_TARGET}"
|
||||
}
|
||||
|
||||
sh(script: "curl -LO https://go.dev/dl/go${go_version}.linux-amd64.tar.gz")
|
||||
sh(script: "tar -xvzf go${go_version}.linux-amd64.tar.gz -C /usr/local",returnStdout: true)
|
||||
sh(script:"rm -Rf go${go_version}.linux-amd64.tar.gz")
|
||||
// Set Go environment variables
|
||||
env.PATH = "/usr/local/go/bin:${env.PATH}"
|
||||
|
||||
// Run Go mod tidy and tests
|
||||
sh (script: "go mod tidy")
|
||||
int testExitCode = sh(script: "go test -short -coverprofile=./cov.out ./...", returnStatus: true)
|
||||
if (testExitCode != 0) {
|
||||
sh(script: "echo Go tests failed, but the pipeline will continue.")
|
||||
}
|
||||
|
||||
// Download and extract Sonar Scanner
|
||||
sh(script: "curl -LO https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip")
|
||||
sh(script: "unzip -o sonar-scanner-cli-5.0.1.3006-linux.zip -d /usr/local/sonar-scanner")
|
||||
sh(script: "rm -Rf sonar-scanner-cli-5.0.1.3006-linux.zip")
|
||||
env.PATH = "/usr/local/sonar-scanner/sonar-scanner-5.0.1.3006-linux/bin:${env.PATH}"
|
||||
|
||||
// Run Sonar Scanner
|
||||
sh(script:"sonar-scanner -Dsonar.pullrequest.provider=GitHub -Dsonar.pullrequest.github.repository=Homelab/${repo_name} -Dsonar.pullrequest.key=${env.CHANGE_ID} -Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} -Dsonar.pullrequest.base=${env.CHANGE_TARGET} -Dsonar.go.coverage.reportPaths=./cov.out ")
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script: "mvn sonar:sonar -Dsonar.branch.name=${env.BRANCH_NAME}")
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
|
||||
sh(script: "curl -LO https://go.dev/dl/go${go_version}.linux-amd64.tar.gz")
|
||||
sh(script: "tar -xvzf go${go_version}.linux-amd64.tar.gz -C /usr/local",returnStdout: true)
|
||||
sh(script:"rm -Rf go${go_version}.linux-amd64.tar.gz")
|
||||
// Set Go environment variables
|
||||
env.PATH = "/usr/local/go/bin:${env.PATH}"
|
||||
|
||||
// Run Go mod tidy and tests
|
||||
sh (script: "go mod tidy")
|
||||
int testExitCode = sh(script: "go test -short -coverprofile=./cov.out ./...", returnStatus: true)
|
||||
if (testExitCode != 0) {
|
||||
sh(script: "echo Go tests failed, but the pipeline will continue.")
|
||||
}
|
||||
|
||||
// Download and extract Sonar Scanner
|
||||
sh(script: "curl -LO https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip")
|
||||
sh(script: "unzip -o sonar-scanner-cli-5.0.1.3006-linux.zip -d /usr/local/sonar-scanner")
|
||||
sh(script: "rm -Rf sonar-scanner-cli-5.0.1.3006-linux.zip")
|
||||
env.PATH = "/usr/local/sonar-scanner/sonar-scanner-5.0.1.3006-linux/bin:${env.PATH}"
|
||||
|
||||
// Run Sonar Scanner
|
||||
sh(script:"sonar-scanner -Dsonar.go.coverage.reportPaths=./cov.out -Dproject.settings=`pwd`/sonar-project.properties -Dsonar.branch.name=${env.BRANCH_NAME}")
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
log.info('Skipping - Sonar Scan')
|
||||
}
|
||||
}
|
||||
stage("Quality Gate"){
|
||||
|
||||
if (skip_sonar){
|
||||
log.info("Sonar scan is skipped. Marking this stage as passed.")
|
||||
}
|
||||
else if (!env.CHANGE_ID){
|
||||
log.info("Skipping quality gate check on Branches. Marking this stage as passed.")
|
||||
}
|
||||
else{
|
||||
timeout(time: 600, unit: 'SECONDS') {
|
||||
def qg = waitForQualityGate()
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE'){
|
||||
if (qg.status != 'OK') {
|
||||
log.warn("Quality gate failed: ${qg.status}, but continuing pipeline execution.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
dir("$repo_name") {
|
||||
withSonarQubeEnv('sonarqube-test') {
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
sh(script: 'mvn sonar:sonar')
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
sh(script: "Quality Gate Failed !")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.homelab.stages
|
||||
|
||||
// Adapted from the original: same structure (dir-scoped checkout,
|
||||
// optional submodule init if .gitmodules exists), just the credential
|
||||
// default changed from Homelab's internal service account
|
||||
// (svc-devops-homelab) to this homelab's Gitea credential. Set GITHUB_CRED
|
||||
// in the pipeline env if you ever need to override it per-job.
|
||||
def run(Map param) {
|
||||
try {
|
||||
stage(stageName('Checkout from git')) {
|
||||
dir("${param.repo_name}") {
|
||||
checkout scm
|
||||
if (fileExists('.gitmodules')) {
|
||||
log.info('Repository has submodules - running git submodule init and update --recursive')
|
||||
def credId = env.GITHUB_CRED?.trim()
|
||||
if (!credId || credId == 'null') credId = 'gitea-ci-credentials'
|
||||
withCredentials([gitUsernamePassword(credentialsId: credId, gitToolName: 'git-tool')]) {
|
||||
sh 'git submodule init && git submodule update --recursive'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error cloning the job. Error: ' + e.toString()
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,906 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterDefinition
|
||||
|
||||
import com.homelab.utilities.getYamlParameter
|
||||
import com.homelab.utilities.buTeamMapping
|
||||
import com.homelab.utilities.gitActions
|
||||
import com.homelab.utilities.constructTemplate
|
||||
import com.homelab.utilities.constructParam
|
||||
import com.homelab.utilities.getDockerParams
|
||||
import com.homelab.utilities.nodePoolSelection
|
||||
|
||||
def run(String repo_name, def deployment_order, def tag, def build_team, def dockerBuildVersion, String notify_channel) {
|
||||
def userInput = ''
|
||||
// def helm_repo_name = 'devops-helm-charts'
|
||||
// def argo_repo_name = 'devops-argo-config'
|
||||
|
||||
def gitObj = new gitActions()
|
||||
def constructParam = new constructParam()
|
||||
def yamlObj = new getYamlParameter()
|
||||
def branch_name = env.BRANCH_NAME
|
||||
def branch_param_map = ['(master|main|gcp-main|gcp-master|farmiso-main)': ['branch':'main', 'envrn':'prd'],
|
||||
'(develop|gcp-dev)':['branch':'develop', 'envrn':'stg']]
|
||||
|
||||
if (env.CHANGE_ID) {
|
||||
branch_param_map = [
|
||||
'(develop|gcp-dev)':['branch':'feature', 'envrn':'ftr'],
|
||||
'(master|main|gcp-main|gcp-master|farmiso-main)':['branch':'pre-prod', 'envrn':'int']]
|
||||
branch_name = env.CHANGE_TARGET
|
||||
}
|
||||
def branch_param = branch_param_map.collectEntries { key, value -> branch_name.matches(key) ? value : [ : ] }
|
||||
if (branch_param == [:]) {
|
||||
branch_param = ['branch':'feature', 'envrn':'ftr']
|
||||
}
|
||||
def helm_branch_name = branch_param.branch
|
||||
def argo_branch_name = branch_param.branch
|
||||
def envrn = branch_param.envrn
|
||||
if (env.CHANGE_ID && env.CHANGE_BRANCH != "develop" && env.CHANGE_TARGET == "main" ) {
|
||||
def allowedNonDevelopPrDeploymentToInt = constructParam.allowedNonDevelopPrDeploymentToIntRepos(repo_name)
|
||||
if (!allowedNonDevelopPrDeploymentToInt) {
|
||||
log.error("******** ONLY DEVELOP BRANCH PR ALLOWED FOR PRE-PROD ENV DEPLOYMENT ********")
|
||||
return
|
||||
}
|
||||
}
|
||||
log.info('Please provide apps to deploy')
|
||||
timeout(unit: 'SECONDS', time: 300) {
|
||||
userInput = wait_for_user_input(deployment_order)
|
||||
}
|
||||
|
||||
// change deployment order according to user input
|
||||
|
||||
if (userInput == '') {
|
||||
log.info('No Deployments selected. Running remaining steps.')
|
||||
return
|
||||
}
|
||||
else if (!userInput.contains('All')) {
|
||||
deployment_order = userInput.split(',') as List
|
||||
}
|
||||
|
||||
commit_branch_name = env.CHANGE_ID ? env.CHANGE_BRANCH : env.BRANCH_NAME
|
||||
def commit_id = gitObj.fetchLatestCommitId(repo_name, commit_branch_name)
|
||||
env.COMMIT_ID = commit_id
|
||||
|
||||
env.SERVICES = deployment_order
|
||||
//Show selected applications to deploy
|
||||
log.info('Following services will be deployed:\n' + deployment_order.join('\n'))
|
||||
// Clone the repo just once for all the deployments
|
||||
gitObj.clone("${WORKSPACE}", "${env.helm_repo_name}", "${helm_branch_name}")
|
||||
|
||||
// Clone the repo just once for all the deployments
|
||||
gitObj.clone("${WORKSPACE}", "${env.argo_repo_name}", "${argo_branch_name}")
|
||||
|
||||
for (deployment in deployment_order) {
|
||||
try {
|
||||
|
||||
def isMultizoneEnabled = constructParam.isMultizoneEnabled(deployment)
|
||||
log.info(" Multi-zone enabled for ${deployment} - ${isMultizoneEnabled}")
|
||||
if (isMultizoneEnabled) {
|
||||
def multizone = "Multi-zone enabled for this deployable ${deployment}. Please use Ringmaster for deployment."
|
||||
currentBuild.result = env.FAILURE
|
||||
throw new Exception(multizone)
|
||||
}
|
||||
def value_binding = yamlObj.getParam("${repo_name}/deployments", "${deployment}.yaml")
|
||||
constructParam.perDeploymentVars(value_binding)
|
||||
// if(envrn == 'prd') {
|
||||
// stage("${deployment}: Downscale Pods in Preprod Env") {
|
||||
// preprod_downscale(repo_name, deployment,notify_channel)
|
||||
// }
|
||||
// }
|
||||
stage("${deployment}: Update Argo App") {
|
||||
update_argo_repo(repo_name, deployment, argo_branch_name, envrn)
|
||||
}
|
||||
stage('Update Argo App of apps') {
|
||||
refresh_app_of_apps(envrn)
|
||||
}
|
||||
stage("${deployment}: Update Helm Repo") {
|
||||
update_helm_repo(repo_name, deployment, tag, build_team, helm_branch_name, envrn, dockerBuildVersion, notify_channel)
|
||||
}
|
||||
stage("${deployment}: Refresh & Sync App in argoCD") {
|
||||
refresh_and_sync(repo_name, deployment, envrn)
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error(e)
|
||||
currentBuild.result = env.FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// def preprod_downscale(String repo_name, String deployment, String notify_channel){
|
||||
// def yamlobj = new getYamlParameter()
|
||||
// def deploy_values = yamlobj.getParam("${repo_name}/deployments","${deployment}.yaml")
|
||||
// def app_name = deploy_values.app_name
|
||||
// def bu = deploy_values.bu
|
||||
// def slack_channel = notify_channel
|
||||
// def as_enabled = deploy_values.environment.'int'.as_enabled
|
||||
// log.info("########################### Invoking Jenkins Job to Downscale Pods in Preprod Environment. ###########################")
|
||||
// build wait: false, job: 'downscale-preprod-eks', parameters:[string(name:'app_name', value:"${app_name}"),
|
||||
// string(name:'bu',value:"${bu}"),
|
||||
// string(name:'as_enabled',value:"${as_enabled}"),
|
||||
// string(name:'slack_channel',value:"${slack_channel}")]
|
||||
// }
|
||||
|
||||
def wait_for_user_input(def deployments) {
|
||||
def userInput = ''
|
||||
String choices = 'All,' + deployments.join(',')
|
||||
int visibleItemCount = 1 + deployments.size()
|
||||
def multiSelect = new ExtendedChoiceParameterDefinition('deployments', //name
|
||||
'PT_CHECKBOX', // parameter type
|
||||
choices, //values
|
||||
'', //projectName
|
||||
'', //propertyFile
|
||||
'', //groovyScript
|
||||
'', //groovyScriptFile
|
||||
'', //bindings
|
||||
'', //groovyClasspath
|
||||
'', //propertyKey
|
||||
'', //defaultValue
|
||||
'', //defaultPropertyFile
|
||||
'', //defaultGroovyScript
|
||||
'', //defaultGroovyScriptFile
|
||||
'', //defaultBindings
|
||||
'', //defaultGroovyClasspath
|
||||
'', //defaultPropertyKey
|
||||
'', //descriptionPropertyValue
|
||||
'', //descriptionPropertyFile
|
||||
'', //descriptionGroovyScript
|
||||
'', //descriptionGroovyScriptFile
|
||||
'', //descriptionBindings
|
||||
'', //descriptionGroovyClasspath
|
||||
'', //descriptionPropertyKey
|
||||
'', //javascriptFile
|
||||
'', //javascript
|
||||
false, //saveJSONParameterToFile
|
||||
false, //quoteValue
|
||||
visibleItemCount, //visibleItemCount
|
||||
'Choose Deployments', //description
|
||||
',') //multiSelectDelimiter
|
||||
|
||||
stage('wait for user input') {
|
||||
try {
|
||||
echo "Skipping User Input - ${env.skip_user_input}"
|
||||
if (env.skip_user_input.toBoolean()) {
|
||||
userInput = 'All'
|
||||
}
|
||||
else {
|
||||
userInput = input message: 'Choose applications to deploy', ok: 'Deploy', parameters: [multiSelect]
|
||||
}
|
||||
return userInput
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error in taking userInput' + e.toString()
|
||||
env.error_msg_to_db = 'Error Taking User Input for Deployment of Applications'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def update_helm_repo(String repo_name, String deployment, String tag, String build_team, String helm_branch_name, String envrn, def dockerBuildVersion, String notify_channel) {
|
||||
def yamlobj = new getYamlParameter()
|
||||
def btObj = new buTeamMapping()
|
||||
def gitObj = new gitActions()
|
||||
def templateObj = new constructTemplate()
|
||||
def nodePoolSelection = new nodePoolSelection()
|
||||
// def helm_repo_name = 'devops-helm-charts'
|
||||
|
||||
def value_yaml_file = get_value_yaml_file(dockerBuildVersion)
|
||||
def value_binding1 = yamlobj.getParam("${repo_name}/deployments", "${deployment}.yaml")
|
||||
if (value_binding1.containsKey('cron')) {
|
||||
if (value_binding1['cron']) {
|
||||
value_yaml_file = 'cron-values.yaml'
|
||||
}
|
||||
}
|
||||
|
||||
def env_norm = ['prd':'prod', 'int':'pre-prod', 'stg':'stg', 'ftr':'feature']
|
||||
def pr_num = ''
|
||||
def commit_status = 0
|
||||
|
||||
try {
|
||||
// Get values from deployments/deployment.yaml
|
||||
isfeatureDeployment = (envrn == 'ftr') ? true : false
|
||||
value_binding1 = yamlobj.getParam("${repo_name}/deployments", "${deployment}.yaml")
|
||||
def memory_request = value_binding1.environment[env.cicd_environment].memory_request
|
||||
def cpu_request = value_binding1.environment[env.cicd_environment].cpu_request
|
||||
def priority_v2 = value_binding1.priority_v2
|
||||
def commit_id = env.COMMIT_ID
|
||||
if ( env.CLOUD_PROVIDER == 'GCP' && !value_binding1.containsKey('cron') ) {
|
||||
nodeSelectorValue = nodePoolSelection.run(memory_request, cpu_request.toString(),priority_v2)
|
||||
echo "Node Selector Value is ${nodeSelectorValue}"
|
||||
value_binding1['nodeSelectorValue'] = nodeSelectorValue
|
||||
}
|
||||
def xms = ''
|
||||
def xmx = ''
|
||||
if (!value_binding1.containsKey('cron')) {
|
||||
if (dockerBuildVersion.contains('maven') || dockerBuildVersion.contains('gradle') || dockerBuildVersion.contains('go')) {
|
||||
value_binding1['activeProcessorCount'] = calculate_active_processors(cpu_request.toString())
|
||||
}
|
||||
if (dockerBuildVersion.contains('maven') || dockerBuildVersion.contains('gradle')) {
|
||||
def memory_limit = value_binding1.environment[env.cicd_environment].memory_limit
|
||||
def deployment_args = value_binding1.environment[env.cicd_environment].deployment_args
|
||||
echo "memory_limit is ${memory_limit}"
|
||||
def memory_string = memory_limit
|
||||
echo "performing operations on this memory string - ${memory_string}"
|
||||
|
||||
def memory_value = ''
|
||||
|
||||
if ( memory_string.contains('M') ) {
|
||||
memory_value = memory_string.replaceAll('Mi', '')
|
||||
memory_value = memory_value.replaceAll('M', '')
|
||||
try {
|
||||
memory_value = memory_value.toInteger()
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
memory_value = memory_value.toDouble()
|
||||
memory_value = memory_value.toInteger()
|
||||
}
|
||||
}
|
||||
else if ( memory_string.contains('G') ) {
|
||||
memory_value = memory_string.replaceAll('Gi', '')
|
||||
memory_value = memory_value.replaceAll('G', '')
|
||||
try {
|
||||
memory_value = memory_value.toInteger() * 1024
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
memory_value = memory_value.toDouble() * 1024
|
||||
memory_value = memory_value.toInteger()
|
||||
}
|
||||
}
|
||||
|
||||
memory_value = memory_value * 0.5
|
||||
memory_value = memory_value.toInteger()
|
||||
xms = "${memory_value}M"
|
||||
xmx = "${memory_value}M"
|
||||
|
||||
echo "xms and xmx from memory value - ${memory_value} are - ${xms} ${xmx}"
|
||||
for (arg in deployment_args) {
|
||||
if (arg.contains('Xms')) {
|
||||
xms = arg.replaceAll('.*Xms', '')
|
||||
echo "xms from deployment_args - ${xms}"
|
||||
}
|
||||
if (arg.contains('Xmx')) {
|
||||
xmx = arg.replaceAll('.*Xmx', '')
|
||||
echo "xmx from deployment_args - ${xmx}"
|
||||
}
|
||||
}
|
||||
echo "final xms and xmx - ${xms} ${xmx}"
|
||||
}
|
||||
}
|
||||
|
||||
value_binding1['dockerBuildVersion'] = dockerBuildVersion == 'python-3.10.12' ? 'python-3.7' : dockerBuildVersion
|
||||
deployEnv = !value_binding1['environment'].containsKey(envrn) && isfeatureDeployment ? 'stg' : envrn
|
||||
Map value_binding = value_binding1['environment'].collectEntries { key, value -> deployEnv.matches(key) ? value : [ : ] }
|
||||
value_binding1.remove('environment')
|
||||
value_binding1.putAll(value_binding)
|
||||
|
||||
value_binding1['repo_name'] = repo_name
|
||||
value_binding1['environment'] = envrn
|
||||
value_binding1['environment_norm'] = env_norm[envrn]
|
||||
value_binding1['tag'] = tag
|
||||
value_binding1['repo_name'] = repo_name
|
||||
value_binding1['build_team'] = build_team
|
||||
value_binding1['commit_id'] = commit_id
|
||||
|
||||
def buini = btObj.get_bu_initials(value_binding1.bu)
|
||||
def bu = btObj.get_bu_initials(value_binding1.bu)
|
||||
def teamini = btObj.get_team_initials(value_binding1.team)
|
||||
def team = btObj.get_team_initials(value_binding1.team)
|
||||
def app_name = value_binding1.app_name
|
||||
def app_branch = app_name + '-' + helm_branch_name
|
||||
|
||||
def app_helm_repo = "${env.helm_repo_name}/${env.helmChartsPath}/${buini}/${teamini}/${app_name}"
|
||||
def ingress_val = value_binding1.ingress_val ?: (env.CHANGE_ID) ? "pr-${CHANGE_ID}" : "${BRANCH_NAME}"
|
||||
|
||||
sh "chmod -R 777 ${app_helm_repo}"
|
||||
|
||||
sh "yq . ${app_helm_repo}/values_properties.yaml -y > a.yaml;mv a.yaml ${app_helm_repo}/values_properties.yaml"
|
||||
sh "cat ${app_helm_repo}/values_properties.yaml"
|
||||
if (isfeatureDeployment) {
|
||||
env.ingress_val = ingress_val
|
||||
value_binding1['env_ns'] = ingress_val
|
||||
value_binding1['vault_env'] = value_binding1['create_vault_path'] ? ingress_val : 'stg'
|
||||
sh(returnStdout: true, script: """
|
||||
mkdir -p ${app_helm_repo}/${ingress_val}
|
||||
sed "s/INGRESS_PR_NUMBER/${ingress_val}/g" ${app_helm_repo}/values_properties.yaml > ${app_helm_repo}/${ingress_val}/values_properties.yaml
|
||||
""")
|
||||
value_binding2 = yamlobj.getParam("${app_helm_repo}/${ingress_val}", 'values_properties.yaml')
|
||||
app_branch = ingress_val + '-' + app_branch
|
||||
}
|
||||
else {
|
||||
value_binding1['vault_env'] = envrn
|
||||
value_binding1['env_ns'] = envrn
|
||||
value_binding2 = yamlobj.getParam("${app_helm_repo}", 'values_properties.yaml')
|
||||
echo '722 Printing Value Binding 2'
|
||||
print value_binding2
|
||||
}
|
||||
|
||||
value_binding1.putAll(value_binding2)
|
||||
value_binding1['bu_norm'] = value_binding1['bu']
|
||||
value_binding1['team_norm'] = value_binding1['team']
|
||||
value_binding1['bu'] = buini
|
||||
value_binding1['team'] = teamini
|
||||
|
||||
|
||||
def serviceTypes = ["httpstateless", "consumer", "producer", "scheduler", "worker", "grpc", "web", "websocket", "cache", "database"]
|
||||
//validate service type params
|
||||
if (value_binding1['service_type']){
|
||||
// Validating the parameter type
|
||||
if(!(value_binding1['service_type'] instanceof List) || value_binding1['service_type'].isEmpty()){
|
||||
env.msg = 'You have not specified service_type correctly. Exiting the pipeline.'
|
||||
log.error(env.msg)
|
||||
sh 'exit 1'
|
||||
}
|
||||
// Check if value_binding1['service_type'] contains any service type not in servicesType
|
||||
def invalidServiceTypes = value_binding1['service_type'].findAll { !serviceTypes.contains(it) }
|
||||
if (!invalidServiceTypes.isEmpty()) {
|
||||
log.error("Invalid service type(s): ${invalidServiceTypes.join(',')}. Allowed service_types are: ${serviceTypes.join(',')}")
|
||||
sh 'exit 1'
|
||||
}
|
||||
value_binding1['service_type_norm'] = value_binding1['service_type'].join(',')
|
||||
}
|
||||
else{
|
||||
value_binding1['service_type_norm']=''
|
||||
}
|
||||
|
||||
// Enable backward compatibility for missing keys
|
||||
enable_backward_compatibility(value_binding1)
|
||||
value_binding1['prismsdk_environment'] = 'PRODUCTION' // THIS KEY WILL CHANGE ONCE 3RD CONFIRM TO STANDERIZE
|
||||
if (envrn == 'stg'){
|
||||
value_binding1['otel_enabled'] = true
|
||||
value_binding1['prismsdk_environment'] = 'SANDBOX' // THIS KEY WILL CHANGE ONCE 3RD CONFIRM TO STANDERIZE
|
||||
}
|
||||
// Override hot fix and notify_channel
|
||||
value_binding1['canary']['skipAnalysis'] = (env.hot_fix) ? true : value_binding1['canary']['skipAnalysis']
|
||||
value_binding1['canary']['slackChannel'] = notify_channel
|
||||
value_binding1['xms'] = xms
|
||||
value_binding1['xmx'] = xmx
|
||||
value_binding1['CLOUD_PROVIDER'] = env.CLOUD_PROVIDER
|
||||
def appConfig = value_binding1['appConfigEnabled']
|
||||
if (appConfig) {
|
||||
def configModule = (value_binding1["module"] == 'module_less') ? repo_name : value_binding1["module"]
|
||||
// read the static config from configs directory
|
||||
def value_binding3 = yamlobj.getParamAsString("${repo_name}/configs/${configModule}", "application-${envrn}.yml")
|
||||
// to update in values.yaml
|
||||
value_binding1['staticAppConfigData'] = value_binding3
|
||||
//read the dynamic config from config directory
|
||||
def value_binding4 = yamlobj.getParamAsString("${repo_name}/configs/${configModule}", "application-dyn-${envrn}.yml")
|
||||
// to update in values.yaml
|
||||
value_binding1['dynamicAppConfigData'] = value_binding4
|
||||
echo 'printing the values_binding3 and values_binding4'
|
||||
print value_binding3
|
||||
print value_binding4
|
||||
value_binding1['vault_env'] = value_binding1['vault_env'] + '-cac'
|
||||
|
||||
}
|
||||
|
||||
|
||||
gitObj.preDeleteBranch(env.helm_repo_name, helm_branch_name, app_branch)
|
||||
gitObj.branchCheckOut(env.helm_repo_name, app_branch)
|
||||
|
||||
echo "Helm Step - Value Binding 1 - ${value_binding1}"
|
||||
if (isfeatureDeployment) {
|
||||
echo 'It is feature deployment'
|
||||
templateObj.renderTemplate(value_binding1, value_yaml_file, "${app_helm_repo}/${ingress_val}/values.yaml")
|
||||
echo 'Before YAML Linting'
|
||||
sh "cat ${app_helm_repo}/${ingress_val}/values.yaml"
|
||||
sh "yq . ${app_helm_repo}/${ingress_val}/values.yaml -y > a.yaml;mv a.yaml ${app_helm_repo}/${ingress_val}/values.yaml"
|
||||
echo 'After YAML Linting'
|
||||
sh "cat ${app_helm_repo}/${ingress_val}/values.yaml"
|
||||
gitObj.add(env.helm_repo_name, "${env.helmChartsPath}/${buini}/${teamini}/${app_name}/${ingress_val}/values.yaml")
|
||||
}
|
||||
else {
|
||||
echo 'Not a feature deployment'
|
||||
templateObj.renderTemplate(value_binding1, value_yaml_file, "${app_helm_repo}/values.yaml")
|
||||
echo 'Before YAML Linting'
|
||||
sh "cat ${app_helm_repo}/values.yaml"
|
||||
|
||||
// Perform YAML linting
|
||||
sh "yq . ${app_helm_repo}/values.yaml -y > a.yaml; mv a.yaml ${app_helm_repo}/values.yaml"
|
||||
echo 'After YAML Linting'
|
||||
sh "cat ${app_helm_repo}/values.yaml"
|
||||
|
||||
// creating a map from final values.yaml to check canary enforcement conditions
|
||||
def valuesMap = yamlobj.getParam("${app_helm_repo}", "values.yaml")
|
||||
|
||||
// Canary enforcement for sp0 services
|
||||
// Also will have to check for cron, worker and scheduler services
|
||||
def enforceCanary = false
|
||||
if (!app_helm_repo.contains("cron") && !app_helm_repo.contains("worker") && !app_helm_repo.contains("scheduler") && !app_helm_repo.contains("consumer") && (valuesMap["labels"]["priority_v2"] == "sp0" || valuesMap["labels"]["priority_v2"] == "up0") && !value_binding1["addHeadless"] && !value_binding1['dockerBuildVersion'].contains("node") && envrn == "prd") {
|
||||
enforceCanary = true
|
||||
}
|
||||
|
||||
def proceed = false
|
||||
def deploymentFailureErrorMessage
|
||||
if (enforceCanary) {
|
||||
if (valuesMap["canary"]["enabled"] == true) {
|
||||
if (valuesMap["canary"]["skipAnalysis"] == false) {
|
||||
if (valuesMap["canary"]["enableManualPromotion"] == true) {
|
||||
proceed = true
|
||||
} else {
|
||||
error("Error: Update the enableManualPromotion parameter")
|
||||
}
|
||||
} else {
|
||||
error("Error: Update the skipAnalysis parameter")
|
||||
|
||||
}
|
||||
} else {
|
||||
error("Error: Enable canary and retry")
|
||||
}
|
||||
} else {
|
||||
proceed = true
|
||||
}
|
||||
|
||||
// Check if priority labels and environment conditions require critical dependency check
|
||||
def criticalPriorities = ["sp0", "up0", "cp0", "sp1", "up1", "cp1"]
|
||||
def priority = valuesMap["labels"]["priority_v2"]
|
||||
if (criticalPriorities.contains(priority) && (envrn == "prd")) {
|
||||
isDependabotCritcal = dependabotCriticalCheck(repo_name)
|
||||
if (isDependabotCritcal) {
|
||||
error("Error: Critical vulnerabilities found in repo: " + repo_name + " \nPlease resolve the alerts marked with CRITICAL here and retry: https://github.com/Homelab/" + repo_name + "/security/dependabot and Retry.")
|
||||
}
|
||||
}
|
||||
|
||||
def param = new constructParam()
|
||||
log.info("checking if appConfig is enabled")
|
||||
def isAppConfigDisabled = param.appConfigDisabledForbidden(appConfig, repo_name, envrn, dockerBuildVersion)
|
||||
if (isAppConfigDisabled){
|
||||
error("Error: appConfig is disabled , onboard your application with config-as-code changes")
|
||||
}
|
||||
|
||||
sh "cat ${app_helm_repo}/values.yaml"
|
||||
gitObj.add(env.helm_repo_name, "${env.helmChartsPath}/${buini}/${teamini}/${app_name}/values.yaml")
|
||||
|
||||
}
|
||||
|
||||
|
||||
commit_status = gitObj.codeCommit(env.helm_repo_name, app_branch, 'Generating values yaml file')
|
||||
if (commit_status == 0) {
|
||||
gitObj.codePush(env.helm_repo_name, app_branch)
|
||||
pr_num = gitObj.createPR(app_name, env.helm_repo_name, helm_branch_name, app_branch, 'Merge helm values file')
|
||||
gitObj.mergePR(env.helm_repo_name, pr_num, app_branch)
|
||||
gitObj.deleteBranch(env.helm_repo_name, helm_branch_name, app_branch)
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
env.msg = 'Error Updating Helm Repo ' + e.toString()
|
||||
env.error_msg_to_db = 'File Not Found'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error Updating Helm Repo ' + e.toString()
|
||||
env.error_msg_to_db += 'Error Updating in Helm repo. Git error message - ' + env.error_part_msg_to_db
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def refresh_and_sync(String repo_name, String deployment, String envrn) {
|
||||
def yamlobj = new getYamlParameter()
|
||||
def deploy_values = yamlobj.getParam("${repo_name}/deployments", "${deployment}.yaml")
|
||||
def app_name = deploy_values.app_name
|
||||
// def argoUrl = (env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'main' || env.CHANGE_TARGET == 'master' || env.CHANGE_TARGET == 'main') ? 'prod-ops-argocd.homelab.com' : 'stg-dev-argocd.homelabtest.in'
|
||||
def argoEnv = (envrn == 'ftr') ? env.ingress_val : envrn
|
||||
|
||||
log.info('########################### Pulling latest changes in ArgoCD. ###########################')
|
||||
withCredentials([usernamePassword(credentialsId: env.argoCreds, passwordVariable: 'ARGO_PASSWORD', usernameVariable: 'ARGO_USERNAME')]) {
|
||||
try {
|
||||
sh """
|
||||
set +x
|
||||
argocd login ${env.argoURL}:443 --username ${ARGO_USERNAME} --password ${ARGO_PASSWORD} --grpc-web
|
||||
argocd app get --hard-refresh ${argoEnv}-${app_name} --grpc-web
|
||||
"""
|
||||
}
|
||||
catch (Exception e) { //added try catch block here
|
||||
env.msg = "Error in hard refresh of app ${app_name} full error: ${e}"
|
||||
env.error_msg_to_db += "Error in hard refresh of app ${app_name};"
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
log.info('########################### Syncing latest changes in ArgoCD. ###########################')
|
||||
return_value = sh(returnStatus: true, script: "argocd app sync ${argoEnv}-${app_name} --grpc-web --http-retry-max 3 --retry-backoff-duration 1m") as Integer
|
||||
if (return_value == 0) {
|
||||
log.info('App synced succesfully.')
|
||||
} else {
|
||||
env.msg = 'App sync failed. Please check in ArgoCD UI.'
|
||||
env.error_msg_to_db += "Error Argo App sync failed ${app_name};"
|
||||
log.error(env.msg)
|
||||
error "${env.msg}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def refresh_app_of_apps(String envrn) {
|
||||
try {
|
||||
appofapps = env.argoIncubator
|
||||
log.info('########################### Pulling latest changes in ArgoCD for App of Apps. ###########################')
|
||||
withCredentials([usernamePassword(credentialsId: env.argoCreds, passwordVariable: 'ARGO_PASSWORD', usernameVariable: 'ARGO_USERNAME')]) {
|
||||
sh """
|
||||
set +x
|
||||
argocd login ${env.argoURL}:443 --username ${ARGO_USERNAME} --password ${ARGO_PASSWORD} --grpc-web
|
||||
argocd app sync ${appofapps} --grpc-web --http-retry-max 3 --retry-backoff-duration 1m || true
|
||||
"""
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = 'Error in App Sync' + e.toString()
|
||||
env.error_msg_to_db += 'Error syncing app of app;'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def update_argo_repo(String repo_name, String deployment, String argoBranch, String envrn) {
|
||||
def yamlobj = new getYamlParameter()
|
||||
def btObj = new buTeamMapping()
|
||||
def gitObj = new gitActions()
|
||||
def templateObj = new constructTemplate()
|
||||
// def argo_repo_name = 'devops-argo-config'
|
||||
def pr_num = ''
|
||||
def commit_status = 0
|
||||
def commit_id = env.COMMIT_ID
|
||||
|
||||
value_binding1 = yamlobj.getParam("${repo_name}/deployments", "${deployment}.yaml")
|
||||
value_binding1['CLOUD_PROVIDER'] = env.CLOUD_PROVIDER
|
||||
isfeatureDeployment = (envrn == 'ftr') ? true : false
|
||||
deployEnv = !value_binding1['environment'].containsKey(envrn) && isfeatureDeployment ? 'stg' : envrn
|
||||
Map value_binding = value_binding1['environment'].collectEntries { key, value -> deployEnv.matches(key) ? value : [ : ] }
|
||||
value_binding1.remove('environment')
|
||||
value_binding1.putAll(value_binding)
|
||||
value_binding1['environment'] = envrn
|
||||
|
||||
def buini = btObj.get_bu_initials(value_binding1.bu)
|
||||
def bu = value_binding1.bu
|
||||
|
||||
def teamini = btObj.get_team_initials(value_binding1.team)
|
||||
def team = value_binding1.team
|
||||
def app_name = value_binding1.app_name
|
||||
def filename = "${teamini}-${app_name}.yaml"
|
||||
def helm_values_path = "${env.helmChartsPath}/${buini}/${teamini}/${app_name}"
|
||||
def app_branch = app_name + '-' + argoBranch
|
||||
def ingress_val = value_binding1.ingress_val ?: (env.CHANGE_ID) ? "pr-${CHANGE_ID}" : "${BRANCH_NAME}"
|
||||
value_binding1['helm_version'] = value_binding1.helm_version ?: env.defaultHelmChartVersion
|
||||
|
||||
if (isfeatureDeployment) {
|
||||
value_binding1['env_ns'] = ingress_val
|
||||
value_binding1['helm_values_path'] = "${env.helmChartsPath}/${buini}/${teamini}/${app_name}/${ingress_val}"
|
||||
filename = ingress_val + '-' + "${app_name}.yaml"
|
||||
app_branch = ingress_val + '-' + app_branch
|
||||
}
|
||||
else {
|
||||
value_binding1['env_ns'] = envrn
|
||||
value_binding1['helm_values_path'] = "${env.helmChartsPath}/${buini}/${teamini}/${app_name}"
|
||||
filename = "${teamini}-${app_name}.yaml"
|
||||
}
|
||||
|
||||
value_binding1['server'] = env.clusterName
|
||||
value_binding1['clusterName'] = env.clusterName
|
||||
|
||||
value_binding1['branch_name'] = argoBranch
|
||||
value_binding1['buini'] = buini
|
||||
value_binding1['teamini'] = teamini
|
||||
|
||||
value_binding1['priority_v2'] = value_binding1.priority_v2 ?: 'cp3'
|
||||
value_binding1['primary_owner'] = value_binding1.primary_owner != null ? value_binding1.primary_owner.split('@')[0] : value_binding1.team
|
||||
value_binding1['secondary_owner'] = value_binding1.secondary_owner != null ? value_binding1.secondary_owner.split('@')[0] : value_binding1.team
|
||||
value_binding1['argoAppNS'] = env.argoAppNS
|
||||
value_binding1['commit_id'] = commit_id
|
||||
log.info(commit_id)
|
||||
gitObj.preDeleteBranch(env.argo_repo_name, argoBranch, app_branch)
|
||||
gitObj.branchCheckOut(env.argo_repo_name, app_branch)
|
||||
echo "Argo Step - Value Binding 1 - ${value_binding1}"
|
||||
|
||||
dir(env.argo_repo_name) {
|
||||
def dirExists = sh(script: "cat ${env.argoAppsPath}/${filename}", returnStatus: true)
|
||||
if ( dirExists != 0 ) {
|
||||
sh "mkdir -p ${env.argoAppsPath}"
|
||||
sh "touch ${env.argoAppsPath}/${filename}"
|
||||
}
|
||||
sh "chmod -R 777 ${env.argoAppsPath}/${filename}"
|
||||
templateObj.renderTemplate(value_binding1, 'argoApp.yaml', "${env.argoAppsPath}/${filename}")
|
||||
sh "yq . ${env.argoAppsPath}/${filename} -y > a.yaml;mv a.yaml ${env.argoAppsPath}/${filename}"
|
||||
sh "cat ${env.argoAppsPath}/${filename}"
|
||||
}
|
||||
|
||||
gitObj.add(env.argo_repo_name, "${env.argoAppsPath}/${filename}")
|
||||
commit_status = gitObj.codeCommit(env.argo_repo_name, app_branch, "onboarding ${deployment} app to ARGO")
|
||||
if (commit_status == 0) {
|
||||
gitObj.codePush(env.argo_repo_name, app_branch)
|
||||
pr_num = gitObj.createPR(app_name, env.argo_repo_name, argoBranch, app_branch, 'Merge argo application configuration')
|
||||
gitObj.mergePR(env.argo_repo_name, pr_num, app_branch)
|
||||
gitObj.deleteBranch(env.argo_repo_name, argoBranch, app_branch)
|
||||
}
|
||||
}
|
||||
|
||||
def calculate_active_processors(String cpu_request) {
|
||||
// If lowercase m is present
|
||||
if (cpu_request.contains('m')) {
|
||||
// Remove m
|
||||
cpu_request = cpu_request.replaceAll('m', '')
|
||||
|
||||
// If this fails means invalid input is given with m, so we can let the pipeline fail here
|
||||
// Convert to double, divide by thousand to get another double, round it off and then convert to integer.
|
||||
return Math.ceil(cpu_request.toDouble() / 1000).toInteger()
|
||||
}
|
||||
|
||||
return Math.ceil(cpu_request.toDouble()).toInteger()
|
||||
}
|
||||
|
||||
def enable_backward_compatibility(def bindings) {
|
||||
// def primaryEmailInitial = bindings.primary_owner.tokenize( '@' )[0]: null
|
||||
// def secondaryEmailInitial = bindings.secondary_owner.tokenize( '@' )[0]: null
|
||||
def dparam_obj = new getDockerParams()
|
||||
bindings['registry'] = env.registry
|
||||
def canary_default = [
|
||||
'progressDeadlineSeconds': 300,
|
||||
'analysisInterval': '120s',
|
||||
'analysisThreshold': 5,
|
||||
'analysisMaxWeight': 5,
|
||||
'analysisStepWeight': 5,
|
||||
'analysisMetrics':[
|
||||
'thresholdRangeMin': 0.99,
|
||||
'interval': '1m'],
|
||||
'skipAnalysis': false
|
||||
]
|
||||
def statefulset_default = [
|
||||
'updateStrategy': 'RollingUpdate',
|
||||
'volumeType': 'dynamic',
|
||||
'dynamicVolume':[
|
||||
'accessMode': 'ReadWriteMany',
|
||||
'mountPath': '/opt/data',
|
||||
'size': '5Gi',
|
||||
'storageClass': ''],
|
||||
'staticVolume':[
|
||||
'accessMode': 'ReadWriteMany',
|
||||
'size': '5Gi',
|
||||
'mountPath': '/opt/data',
|
||||
'storageClass': '',
|
||||
'volumeHandle': '',
|
||||
'csiDriver': '']
|
||||
]
|
||||
bindings['hostAliases'] = bindings.hostAliases ?: false
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
bindings['nodeSelector'] = bindings.nodeSelector ?: (env.cicd_environment == 'stg' || env.cicd_environment == 'ftr') ? bindings.bu : ( env.cicd_environment == 'int' ? bindings.bu + '-int' : bindings.team )
|
||||
bindings['nodeSelectorValue'] = 'dedicated'
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
if (env.cicd_environment == 'int') {
|
||||
bindings['nodeSelector'] = bindings.nodeSelector ?: 'cloud.google.com/compute-class'
|
||||
} else {
|
||||
bindings['nodeSelector'] = bindings.nodeSelector ?: 'dedicated'
|
||||
}
|
||||
bindings['nodeSelectorValue'] = bindings.nodeSelectorValue ?: 'megatetra'
|
||||
}
|
||||
bindings['triggers'] = bindings.triggers ?: false
|
||||
bindings['host'] = bindings.host ?: false
|
||||
bindings['hosts'] = bindings.hosts ?: false
|
||||
bindings['grpc_host'] = bindings.grpc_host ?: false
|
||||
bindings['grpc_hosts'] = bindings.grpc_hosts ?: false
|
||||
bindings['serviceAccount'] = bindings.serviceAccount ?: false
|
||||
bindings['ingress_annotations'] = bindings.ingress_annotations ?: ''
|
||||
bindings['canary'] = bindings.canary ?: canary_default
|
||||
bindings['minCanaryReplicas'] = bindings.canary?.minCanaryReplicas ?: bindings.minCanaryReplicas ?: bindings.as_min
|
||||
bindings['maxCanaryReplicas'] = bindings.canary?.maxCanaryReplicas ?: bindings.maxCanaryReplicas ?: bindings.as_max
|
||||
bindings['enableManualPromotion'] = bindings.canary?.enableManualPromotion ?: bindings.enableManualPromotion ?: false
|
||||
bindings['cpu_limit'] = bindings.cpu_limit ?: bindings.cpu_request
|
||||
bindings['slowStartWindow'] = bindings.slowStartWindow ?: false
|
||||
bindings['slowStartAggression'] = bindings.slowStartAggression ?: '1'
|
||||
bindings['slowStartMinPercent'] = bindings.slowStartMinPercent ?: '10'
|
||||
bindings['lifecycle'] = bindings.lifecycle ?: false
|
||||
bindings['maxSurge'] = bindings.maxSurge ?: '50'
|
||||
bindings['as_down_pod_count'] = bindings.as_down_pod_count ?: '2'
|
||||
bindings['as_up_pod_count'] = bindings.as_up_pod_count ?: '2'
|
||||
bindings['as_up_pod_percentage'] = bindings.as_up_pod_percentage ?: '10'
|
||||
bindings['createContourGateway'] = bindings.createContourGateway ?: false
|
||||
bindings['service_annotations'] = bindings.service_annotations ?: false
|
||||
bindings['pod_annotations'] = bindings.pod_annotations ?: false
|
||||
bindings['kind'] = bindings.kind ?: 'deployment'
|
||||
bindings['statefulset'] = bindings.statefulset ?: statefulset_default
|
||||
bindings['contourResponseTimeout'] = bindings.contourResponseTimeout ?: false
|
||||
bindings['pdbMinAvailable'] = bindings.pdbMinAvailable ?: ''
|
||||
bindings['pdbMaxUnavailable'] = bindings.pdbMaxUnavailable ?: '10%'
|
||||
|
||||
// Just to keep compatibility for services which still use grpc_port
|
||||
// If someone has supplied primary_port, then it is used
|
||||
// Else we check for grpc_port, and that is used
|
||||
// If none of the above is supplied, then app_port is used just like normal flow
|
||||
bindings['primary_port'] = bindings.primary_port ?: bindings.grpc_port ?: bindings.app_port
|
||||
bindings['grpc_port'] = bindings.grpc_port ?: false
|
||||
// bindings['xmx'] = bindings.xmx ?: '50.0'
|
||||
// bindings['xms'] = bindings.xms ?: '50.0'
|
||||
bindings['enableWebsocket'] = bindings.enableWebsocket ?: false
|
||||
bindings['external_secrets_annotations'] = bindings.external_secrets_annotations ?: ''
|
||||
bindings['liveness_failure_threshold'] = bindings.liveness_failure_threshold ?: '5'
|
||||
bindings['liveness_period_seconds'] = bindings.liveness_period_seconds ?: bindings.team_norm == 'ml-platform' ? '5' : '10'
|
||||
bindings['liveness_success_threshold'] = bindings.liveness_success_threshold ?: '1'
|
||||
bindings['liveness_timeout_seconds'] = bindings.liveness_timeout_seconds ?: '2'
|
||||
bindings['readiness_failure_threshold'] = bindings.readiness_failure_threshold ?: '5'
|
||||
bindings['readiness_period_seconds'] = bindings.liveness_period_seconds ?: bindings.team_norm == 'ml-platform' ? '5' : '10'
|
||||
bindings['readiness_success_threshold'] = bindings.liveness_success_threshold ?: '1'
|
||||
bindings['readiness_timeout_seconds'] = bindings.liveness_timeout_seconds ?: '2'
|
||||
bindings['addon_ports'] = bindings.addon_ports ?: false
|
||||
|
||||
validateRequiredMetadata(bindings)
|
||||
|
||||
bindings['priority_v2'] = bindings.priority_v2 ?: 'cp3'
|
||||
bindings['primary_owner'] = bindings.primary_owner != null ? bindings.primary_owner.split('@')[0] : bindings.team_norm
|
||||
bindings['secondary_owner'] = bindings.secondary_owner != null ? bindings.secondary_owner.split('@')[0] : bindings.team_norm
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
if (bindings.otel_enabled == null) {
|
||||
bindings['otel_enabled'] = (bindings.priority_v2 == 'cp1' || bindings.priority_v2 == 'up1') ? true : false
|
||||
}
|
||||
else {
|
||||
bindings['otel_enabled'] = bindings.otel_enabled
|
||||
}
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
echo "Checking for otel value - ${bindings.otel_enabled}"
|
||||
if (bindings.otel_enabled == null) {
|
||||
bindings['otel_enabled'] = true
|
||||
}
|
||||
else {
|
||||
bindings['otel_enabled'] = bindings.otel_enabled
|
||||
}
|
||||
|
||||
// Setting metrics mode -> avaliable options: telegraf | otel | dual
|
||||
echo "Setting metrics mode - ${bindings.metrics_mode}"
|
||||
// Setting default values
|
||||
bindings['telegraf_metrics'] = false
|
||||
bindings['otel_metrics'] = false
|
||||
bindings['metrics_mode'] = bindings.metrics_mode?.toLowerCase()
|
||||
def samplerArg = bindings.otel_traces_sampler_arg
|
||||
def isValidSamplerArg = false
|
||||
if (samplerArg != null && (samplerArg instanceof Float || samplerArg instanceof Double)) {
|
||||
if (samplerArg >= 0.0 && samplerArg <= 1.0) {
|
||||
isValidSamplerArg = true
|
||||
}
|
||||
}
|
||||
bindings['otel_traces_sampler_arg'] = isValidSamplerArg ? samplerArg : '0.1'
|
||||
log.info("1 - otel_traces_sampler_arg - ${bindings.otel_traces_sampler_arg}")
|
||||
log.info("2 - ${bindings['otel_traces_sampler_arg']}")
|
||||
// Enabling metrics based on mode
|
||||
if (bindings.metrics_mode == 'telegraf') {
|
||||
bindings['telegraf_metrics'] = true
|
||||
}
|
||||
else if (bindings.metrics_mode == 'otel') {
|
||||
bindings['otel_metrics'] = true
|
||||
}
|
||||
else if (bindings.metrics_mode == 'dual') {
|
||||
bindings['telegraf_metrics'] = true
|
||||
bindings['otel_metrics'] = true
|
||||
}
|
||||
else {
|
||||
bindings['metrics_mode'] = 'telegraf'
|
||||
bindings['telegraf_metrics'] = true
|
||||
}
|
||||
}
|
||||
bindings['command'] = bindings.command
|
||||
if (bindings.command == null) {
|
||||
bindings['command'] = get_default_command(bindings['dockerBuildVersion'])
|
||||
}
|
||||
bindings['as_down_stable_window'] = bindings.as_down_stable_window ?: '1800'
|
||||
bindings['podDistributionSkew'] = bindings.podDistributionSkew ?: false
|
||||
bindings["appConfigEnabled"] = bindings.appConfigEnabled ?: false
|
||||
bindings["addHeadless"] = bindings.addHeadless?: false
|
||||
}
|
||||
|
||||
def get_value_yaml_file(def dockerBuildVersion) {
|
||||
switch (dockerBuildVersion) {
|
||||
case ~/^maven-.*/: return 'values.yaml'
|
||||
case ~/^node-.*/: return 'node-values.yaml'
|
||||
case ~/^python-.*/: return 'python-values.yaml'
|
||||
case ~/^go.*/: return 'go-values.yaml'
|
||||
case 'php': return 'php-values.yaml'
|
||||
case 'gradle': return 'values.yaml'
|
||||
}
|
||||
}
|
||||
|
||||
def get_default_command(def dockerBuildVersion) {
|
||||
switch (dockerBuildVersion) {
|
||||
case ~/^maven-.*/: return 'java'
|
||||
case ~/^node-.*/: return 'pm2-runtime'
|
||||
case ~/^go.*/: return '/app/server'
|
||||
case 'php': return 'apache2-foreground'
|
||||
case 'gradle': return 'java'
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
|
||||
def validateRequiredMetadata(def bindings) {
|
||||
def requiredParams = ['primary_owner', 'secondary_owner', 'priority_v2', 'service_type']
|
||||
def serviceOwners = ['primary_owner', 'secondary_owner']
|
||||
// Validate if required parameters are present
|
||||
for (param in requiredParams) {
|
||||
if (bindings[param] == null || bindings[param] == '') {
|
||||
env.msg = 'You have not supplied ' + param + '. Exiting the pipeline.'
|
||||
log.error(env.msg)
|
||||
sh 'exit 1'
|
||||
}
|
||||
}
|
||||
|
||||
// Validate if the owners are valid or not
|
||||
if (env.cicd_environment == 'dev' || env.cicd_environment == 'ftr' || env.cicd_environment == 'stg') {
|
||||
return
|
||||
}
|
||||
|
||||
for (param in serviceOwners) {
|
||||
final String owner = bindings[param]
|
||||
final String url = "https://pulse.homelabgcp.in/api/anonymous-User/userexist?email=${owner}"
|
||||
final def(String response, String code) = sh(returnStdout: true, script: """
|
||||
set +x
|
||||
curl -s -X GET -w '\n%{response_code}' $url
|
||||
set -x
|
||||
""").trim().tokenize('\n')
|
||||
|
||||
if (code != "200") {
|
||||
// Let's not break the pipeline in case the API fails
|
||||
log.info("Received ${code} code from Pulse while checking for user. Skipping the checks further and letting the pipeline proceed.")
|
||||
} else {
|
||||
// Check if the user exists
|
||||
def jqCommand = "echo '${response}' | jq -r '.exists'"
|
||||
def userExists = sh(returnStdout: true, script: """
|
||||
set +x
|
||||
${jqCommand}
|
||||
set -x
|
||||
""").trim()
|
||||
if (userExists != "true") {
|
||||
env.msg = "Invalid value provided in ${param}. Check if the user ${owner} exists"
|
||||
log.error(env.msg)
|
||||
sh 'exit 1'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def dependabotCriticalCheck(def repoName) {
|
||||
def repo = repoName
|
||||
|
||||
def alerts
|
||||
withCredentials([usernamePassword(credentialsId: "${env.GITHUB_CRED}", usernameVariable:'user', passwordVariable: 'token')]) {
|
||||
validateDependabot = httpRequest httpMode: 'GET',
|
||||
customHeaders: [
|
||||
[name: 'Accept', value: 'application/vnd.github+json'],
|
||||
[maskValue: true, name: 'Authorization', value: 'Bearer ' + token]
|
||||
],
|
||||
url: "https://api.github.com/repos/homelab/${repo}/dependabot/alerts?state=open&per_page=100",
|
||||
validResponseCodes: '200',
|
||||
timeout: 10
|
||||
alerts = readJSON(text: validateDependabot.content)
|
||||
|
||||
}
|
||||
|
||||
// if (!alerts) {
|
||||
// println "Failed to fetch alerts for ${repo}."
|
||||
// return false
|
||||
// }
|
||||
|
||||
// Parse the JSON response
|
||||
if (!alerts) {
|
||||
println "No alerts found for ${repo}."
|
||||
return false
|
||||
}
|
||||
|
||||
def criticalCount = 0
|
||||
alerts.each { alert ->
|
||||
def severity = alert?.security_vulnerability?.severity
|
||||
|
||||
if (severity == "critical") {
|
||||
criticalCount++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (criticalCount > 0) {
|
||||
println "Critical vulnerabilities found in ${repo}: ${criticalCount}"
|
||||
return true // Critical vulnerabilities found
|
||||
} else {
|
||||
println "No critical vulnerabilities in ${repo}."
|
||||
return false // No critical vulnerabilities found
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.homelab.stages
|
||||
|
||||
def run(Map param){
|
||||
//get the jar for deployment, get it from the params module
|
||||
//run the ansible playbook
|
||||
//create the inventory file with given ip in parameters
|
||||
def server_ip = param.run_automation.server_ip
|
||||
def app_name = param.run_automation.app_name
|
||||
def healthcheck_api = param.run_automation.healthcheck_api
|
||||
def app_port = param.run_automation.app_port
|
||||
def repo_name = param.repo_name
|
||||
|
||||
deploy(server_ip,repo_name,app_name,healthcheck_api,app_port)
|
||||
}
|
||||
|
||||
def deploy(String server_ip, String repo_name, String app_name, String healthcheck_api, String app_port){
|
||||
try{
|
||||
stage('Deploying JAR'){
|
||||
sh "echo '$server_ip' > host_file.txt"
|
||||
echo "inventory created"
|
||||
def playbook_content = libraryResource 'com/homelab/deployJar.yaml'
|
||||
writeFile file:"deployJar.yaml", text: playbook_content
|
||||
sh(returnStatus: true, script: "ansible-playbook -i host_file.txt -u 'ubuntu' -e 'env=stage' -e 'app_name=${app_name}' -e 'repo_name=${repo_name}' -e 'healthcheck_api=${healthcheck_api}' -e 'app_port=${app_port}' deployJar.yaml -v")
|
||||
}
|
||||
}
|
||||
catch( Exception e) {
|
||||
env.msg = "Error while deploying JAR. Please check console output for more details."
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.getYamlParameter
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatterBuilder
|
||||
|
||||
def run(String repo_name, def deployment_order, def tag, def build_team, def dockerBuildVersion, String notify_channel){
|
||||
def yamlobj = new getYamlParameter()
|
||||
def applicationNames = []
|
||||
def jobStatusMap=["SUCCESS": "BUILD_STATUS_COMPLETED","FAILURE": "BUILD_STATUS_FAILED","UNSTABLE":"BUILD_STATUS_FAILED"]
|
||||
def build_user = currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId()
|
||||
|
||||
for (deployment in deployment_order){
|
||||
def value_binding = yamlobj.getParam("${repo_name}/deployments","${deployment}.yaml")
|
||||
applicationNames.add(value_binding['app_name'])
|
||||
}
|
||||
def end_time = getDateTime()
|
||||
def working_env = (env.CLOUD_PROVIDER == "GCP")? "gcp_${env.cicd_environment}" : env.cicd_environment
|
||||
|
||||
def jsonMap = [:]
|
||||
jsonMap["hot_fix"] = env.hot_fix ? true : false
|
||||
jsonMap["job_name"] = env.JOB_NAME ? env.JOB_NAME.split('/')[0] : "unknown"
|
||||
jsonMap["build_no"] = env.BUILD_NUMBER
|
||||
jsonMap["image"] = tag
|
||||
jsonMap["applications"] = applicationNames
|
||||
jsonMap["job_status"] = jobStatusMap[currentBuild?.currentResult]
|
||||
jsonMap["err_msg"] = env.error_msg_to_db
|
||||
jsonMap["end_time"] = end_time
|
||||
jsonMap["email"] = currentBuild?.rawBuild?.getCause(Cause.UserIdCause)?.getUserId()
|
||||
jsonMap["start_time"] = env.STARTTIME
|
||||
jsonMap["build_team"] = build_team
|
||||
jsonMap["docker_build_version"] = dockerBuildVersion
|
||||
jsonMap["notify_channel"] = notify_channel
|
||||
jsonMap["branch_name"] = env.CHANGE_ID ? env.CHANGE_BRANCH : env.BRANCH_NAME
|
||||
jsonMap["commit_id"] = env.commit_id
|
||||
jsonMap["deploy_argo"] = env.deployArgo != null ? env.deployArgo.toBoolean() : false
|
||||
jsonMap["pr_number"] = env.CHANGE_ID ? env.CHANGE_ID.toInteger() : 0
|
||||
|
||||
final String baseUrl
|
||||
|
||||
switch (env.cicd_environment) {
|
||||
case 'prd':
|
||||
case 'int':
|
||||
baseUrl = 'https://ringmaster-api.homelabgcp.in/api/v1/key/cicd/cd/update'
|
||||
break
|
||||
default:
|
||||
baseUrl = 'https://ringmaster-api.admin.homelabgcp.in/api/v1/key/cicd/cd/update'
|
||||
}
|
||||
final String url = "${baseUrl}?workingEnv=${working_env}"
|
||||
final String header = "Content-Type: application/json"
|
||||
final String jsonData = writeJSON returnText: true, json: jsonMap
|
||||
|
||||
|
||||
if (build_user == "ringmaster-bot"){
|
||||
callApi(url, header, jsonData)
|
||||
} else{
|
||||
def newCICD_Payload = [:]
|
||||
newCICD_Payload["repo_name"] = repo_name
|
||||
newCICD_Payload["source_branch"] = env.CHANGE_ID ? env.CHANGE_BRANCH : env.BRANCH_NAME
|
||||
newCICD_Payload["pull_request_number"] = env.CHANGE_ID ? env.CHANGE_ID.toInteger() : 0
|
||||
newCICD_Payload["env"] = env.cicd_environment
|
||||
newCICD_Payload["job_name"] = env.JOB_NAME ? env.JOB_NAME.split('/')[0] : "UNKNOWN"
|
||||
newCICD_Payload["sub_job_name"] = env.JOB_NAME ? env.JOB_NAME.split('/')[1] : "UNKNOWN"
|
||||
newCICD_Payload["build_number"] = env.BUILD_NUMBER.toInteger()
|
||||
newCICD_Payload["image_tag"] = tag
|
||||
newCICD_Payload["build_detailed_error"] = env.error_msg_to_db
|
||||
switch (env.cicd_environment) {
|
||||
case 'prd':
|
||||
case 'int':
|
||||
cicdBaseUrl = 'http://turbo-turtle.homelabgcp.in'
|
||||
break
|
||||
default:
|
||||
cicdBaseUrl = 'http://turbo-turtle.admin.homelabgcp.in'
|
||||
}
|
||||
final String newCICD_JSON = writeJSON returnText: true, json: newCICD_Payload
|
||||
//log.info("New CICD JSON - ${newCICD_JSON}")
|
||||
final String newCICD_URL = cicdBaseUrl + "/api/v1/ci/jenkins/callback"
|
||||
final String newCICD_Header = "Content-Type: application/json"
|
||||
// Use a temporary file to store the JSON payload
|
||||
// This avoids shell quoting issues completely
|
||||
final String jsonFilePath = "cicd_payload_${env.BUILD_NUMBER}_${System.currentTimeMillis()}.json"
|
||||
|
||||
try {
|
||||
// 1. Write the JSON payload to a temporary file
|
||||
// The writeJSON step ensures the content is valid JSON, escaping internal characters
|
||||
writeFile(file: jsonFilePath, text: writeJSON(returnText: true, json: newCICD_Payload))
|
||||
final String newCICD_JSON_log = readFile(file: jsonFilePath)
|
||||
log.info("New CICD JSON (from file) - ${newCICD_JSON_log.take(500)}...") // Log a snippet
|
||||
final def(String response, String code) = sh(
|
||||
returnStdout: true,
|
||||
script: """
|
||||
curl -s -X POST \\
|
||||
-H '$newCICD_Header' \\
|
||||
-w '\\n%{response_code}' \\
|
||||
$newCICD_URL \\
|
||||
-d @$jsonFilePath
|
||||
"""
|
||||
).trim().tokenize("\n")
|
||||
|
||||
if (code != "200") {
|
||||
log.error("CICD Application API call failed with error code - ${code}, response - ${response}")
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("CICD Application API call failed - " + "Error: " + e.toString())
|
||||
throw e
|
||||
} finally {
|
||||
sh(script: "rm -f ${jsonFilePath}", returnStatus: true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def callApi(String url, String header, String jsonData){
|
||||
try{
|
||||
withCredentials([usernamePassword(credentialsId: "ringmaster-token", usernameVariable:'user', passwordVariable: 'token')]){
|
||||
final def(String response, String code) = sh(returnStdout: true, script: "curl -s -X POST -H '$header' -H 'Authorization: $token' -w '\\n%{response_code}' $url -d '$jsonData'").trim().tokenize("\n")
|
||||
log.info("HTTP response status code : ${code}")
|
||||
if(code != "200"){
|
||||
log.error("API call failed with error code - ${code}, response - ${response}")
|
||||
currentBuild.result = 'FAILURE'
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( Exception e) {
|
||||
log.error("API call failed")
|
||||
currentBuild.result = 'FAILURE'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@NonCPS
|
||||
def getDateTime() {
|
||||
// Get the current date and time in IST
|
||||
def currentDateTime = ZonedDateTime.now()
|
||||
// Create a formatter for the desired pattern
|
||||
def formatter = new DateTimeFormatterBuilder()
|
||||
.appendPattern("yyyy-MM-dd'T'HH:mm:ss")
|
||||
.appendOffset("+HH:mm", "+00:00")
|
||||
.toFormatter()
|
||||
// Format the current date and time using the formatter
|
||||
def formattedDateTime = currentDateTime.format(formatter)
|
||||
return formattedDateTime;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.constructTemplate
|
||||
import com.homelab.utilities.gitActions
|
||||
import com.homelab.utilities.buTeamMapping
|
||||
import com.homelab.utilities.getDockerParams
|
||||
import com.homelab.stages.multiBranchPipeline
|
||||
|
||||
def run(Map params) {
|
||||
def btObj = new buTeamMapping()
|
||||
def gitObj = new gitActions()
|
||||
|
||||
def environments = ['stg':'develop', 'int':'pre-prod', 'prd':'main', 'ftr':'feature']
|
||||
final Map<?, ?> modifiedParams = new HashMap<>(params)
|
||||
|
||||
// def helm_repo_name = 'devops-helm-charts'
|
||||
// def argoRepo = "devops-argo-config"
|
||||
|
||||
def deployment_order = modifiedParams.app_names.replaceAll('\\s', '').split(', ') as List
|
||||
|
||||
modifiedParams['deployment_order'] = deployment_order
|
||||
|
||||
stage('Create Jenkinsfile') {
|
||||
jenkinsfileCreate(modifiedParams)
|
||||
}
|
||||
|
||||
modifiedParams['bu'] = btObj.get_bu_initials(modifiedParams['bu'])
|
||||
modifiedParams['team'] = btObj.get_team_initials(modifiedParams['team'])
|
||||
log.info(modifiedParams)
|
||||
|
||||
stage('Update Helm charts') {
|
||||
gitObj.clone("${WORKSPACE}", "${env.helm_repo_name}", null)
|
||||
environments.each {
|
||||
modifiedParams['environment'] = it.key
|
||||
def helm_branch_name = it.value
|
||||
for (deployment in deployment_order) {
|
||||
modifiedParams['app_name'] = deployment
|
||||
modifiedParams['host'] = helm_branch_name == 'feature' ? "INGRESS_PR_NUMBER-${deployment}.dev.internal.homelabtest.in" : "${deployment}.${it.key}.internal.homelabtest.in"
|
||||
modifiedParams['host'] = (helm_branch_name == 'pre-prod') ? "${deployment}.${modifiedParams.bu}.internal.homelab.co" : modifiedParams.host
|
||||
//If branch is main, then assuming the env as prod
|
||||
//Making nodeSelector changes to hypercore or hypermem & based on arch, so that it moves to common node pool
|
||||
if (helm_branch_name == 'main') {
|
||||
def cpu = modifiedParams['cpu_request']
|
||||
def memory = modifiedParams['memory_request']
|
||||
def nodeSelector = 'hypercore'
|
||||
|
||||
if (cpu.contains('m')) {
|
||||
def numericValue = cpu.replaceAll('\\D+', '').toDouble()
|
||||
def gb = numericValue / 1000
|
||||
cpu = gb.toString()
|
||||
}
|
||||
def mem_dgt = memory.replaceAll('\\D+', '').toDouble()
|
||||
memory = memory.contains('Mi') ? mem_dgt / 1024 : mem_dgt
|
||||
def ratio = cpu.toDouble() / memory.toDouble()
|
||||
def arch = modifiedParams['arch']
|
||||
def graviton_required = false
|
||||
if (arch == 'arm64') {
|
||||
graviton_required = true
|
||||
}
|
||||
if (ratio <= 1 / 3) {
|
||||
if (graviton_required) {
|
||||
nodeSelector = 'hypermem-arm64'
|
||||
} else {
|
||||
nodeSelector = 'hypermem'
|
||||
}
|
||||
} else {
|
||||
if (graviton_required) {
|
||||
nodeSelector = 'hypercore-arm64'
|
||||
} else {
|
||||
nodeSelector = 'hypercore'
|
||||
}
|
||||
}
|
||||
modifiedParams['nodeSelector'] = nodeSelector
|
||||
}
|
||||
updateHelmRepo(modifiedParams, env.helm_repo_name, helm_branch_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stage("Update Argo Application"){
|
||||
// gitObj.clone("${WORKSPACE}", "${argoRepo}", null)
|
||||
// environments.each{
|
||||
// modifiedParams["environment"] = it.key
|
||||
// def argo_branch_name = it.value
|
||||
// for (deployment in deployment_order){
|
||||
// modifiedParams["app_name"] = deployment
|
||||
// updateArgoRepo(modifiedParams,argoRepo,argo_branch_name)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
stage('Create ECR repo') {
|
||||
def buildDockerObj = new getDockerParams()
|
||||
def modules = buildDockerObj.getModules(modifiedParams['repo_name'])
|
||||
if (modules == null) {
|
||||
modules = ['module_less']
|
||||
}
|
||||
for (module in modules) {
|
||||
def ecr_repo_name = (module == 'module_less') ? "${modifiedParams.team}/${modifiedParams.repo_name.toLowerCase()}" : "${modifiedParams.team}/${modifiedParams.repo_name.toLowerCase()}/${module}"
|
||||
try {
|
||||
sh "aws ecr create-repository --repository-name ${ecr_repo_name}"
|
||||
}
|
||||
catch (Exceptione) {
|
||||
log.info('repository already present')
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Create Multibranch pipeline') {
|
||||
def multiBranchPipelineObj = new multiBranchPipeline()
|
||||
multiBranchPipelineObj.applicationOnboard(params['bu'], params['repo_name'])
|
||||
}
|
||||
}
|
||||
|
||||
def jenkinsfileCreate(Map config) {
|
||||
def constructObj = new constructTemplate()
|
||||
def gitObj = new gitActions()
|
||||
def repo_name = config.repo_name
|
||||
def deployment_order = config.deployment_order
|
||||
def repo_branch_name = 'eks_onboarding'
|
||||
config['branch_params'] = ''
|
||||
config['excludedMoudles'] = config.excludedMoudles.replaceAll('\\s', '').split(', ') as List
|
||||
gitObj.clone("${WORKSPACE}", "${config.repo_name}", null)
|
||||
gitObj.branchCheckOut(config.repo_name, repo_branch_name)
|
||||
deployment_yaml_file = get_deployment_yaml_file(config['dockerBuildVersion'])
|
||||
dir(repo_name) {
|
||||
sh(script:'mkdir -p deployments/')
|
||||
sh 'chmod -R 777 .'
|
||||
constructObj.renderTemplate(config, 'Jenkinsfile', 'Jenkinsfile')
|
||||
gitObj.add('.', 'Jenkinsfile')
|
||||
|
||||
constructObj.renderTemplate(config, 'config.yaml', 'config.yaml')
|
||||
gitObj.add('.', 'config.yaml')
|
||||
|
||||
dir('deployments') {
|
||||
for (deployment in deployment_order) {
|
||||
config['app_name'] = deployment
|
||||
constructObj.renderTemplate(config, deployment_yaml_file, deployment + '.yaml')
|
||||
gitObj.add('.', deployment + '.yaml')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gitObj.codeCommit(repo_name, repo_branch_name, 'Generating build and deployments files')
|
||||
gitObj.codePush(repo_name, repo_branch_name)
|
||||
}
|
||||
|
||||
def updateHelmRepo(Map config, String helm_repo_name, String helm_branch_name) {
|
||||
def constructObj = new constructTemplate()
|
||||
def gitObj = new gitActions()
|
||||
def filepath = "${env.helmChartsPath}/${config.bu}/${config.team}/${config.app_name}"
|
||||
def file_name = 'values_properties.yaml'
|
||||
def app_branch = config.app_name + '-' + helm_branch_name + '-helm'
|
||||
def pr_num = ''
|
||||
def commit_status = 0
|
||||
|
||||
gitObj.branchCheckOut(env.helm_repo_name, helm_branch_name)
|
||||
gitObj.branchCheckOut("${env.helm_repo_name}", "${app_branch}")
|
||||
// create filepath
|
||||
dir(env.helm_repo_name) {
|
||||
sh(script:"mkdir -p ${filepath}" ,returnStdout:true)
|
||||
sh 'chmod -R 777 .'
|
||||
constructObj.renderTemplate(config, file_name, filepath + '/' + file_name)
|
||||
}
|
||||
|
||||
// git commit and push
|
||||
gitObj.add(env.helm_repo_name, filepath + '/' + file_name)
|
||||
commit_status = gitObj.codeCommit(env.helm_repo_name, app_branch, 'Generating properties values yaml file')
|
||||
if (commit_status == 0) {
|
||||
gitObj.codePush(env.helm_repo_name, app_branch)
|
||||
pr_num = gitObj.createPR(config.app_name, env.helm_repo_name, helm_branch_name, app_branch, 'Merge helm values properties')
|
||||
gitObj.mergePR(env.helm_repo_name, pr_num, app_branch)
|
||||
gitObj.deleteBranch(env.helm_repo_name, helm_branch_name, app_branch)
|
||||
}
|
||||
}
|
||||
|
||||
def get_deployment_yaml_file(def dockerBuildVersion) {
|
||||
switch(dockerBuildVersion) {
|
||||
case ~/^maven-.*/: return 'deployment.yaml'
|
||||
case ~/^node-.*/: return 'node-deployment.yaml'
|
||||
case ~/^python-.*/: return 'python-deployment.yaml'
|
||||
case ~/^go.*/: return 'go-deployment.yaml'
|
||||
case 'gradle': return 'gradle-deployment.yaml'
|
||||
case 'php': return 'php-deployment.yaml'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.homelab.stages
|
||||
|
||||
def run(String repo_name){
|
||||
stage(stageName("Check for Hot Fix")){
|
||||
dir(repo_name){
|
||||
if (env.CHANGE_ID) {
|
||||
def source_branch_name = env.CHANGE_BRANCH
|
||||
log.info("Source-Branch : ${source_branch_name}")
|
||||
source_branch_name = source_branch_name.toLowerCase()
|
||||
if (source_branch_name.contains("hotfix_")){
|
||||
env.hot_fix = true
|
||||
log.info("***************** Enabling Hot-fix workflow *****************")
|
||||
}
|
||||
else if (source_branch_name.matches('^revert-\\d+-.+$')){
|
||||
env.hot_fix = true
|
||||
log.info("***************** Enabling Hot-fix workflow for revert branch *****************")
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.info("**** Branch: ${env.BRANCH_NAME}, So Skipping Hot-fix check ****")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.homelab.stages
|
||||
|
||||
// New stage — the homelab equivalent of the real system's config.yaml
|
||||
// convention. Verified by reading constructParam.groovy directly first:
|
||||
// it does NOT read config.yaml itself — helmGenerator.groovy renders
|
||||
// devops-lib's own resources/com/homelab/config.yaml TEMPLATE into each
|
||||
// SERVICE repo's own root as a real, committed config.yaml, and various
|
||||
// stages then read that file directly (runHooks.groovy's
|
||||
// hooks.<phase> block, buildNode.groovy's s3_path/pbac_scope_name, etc.).
|
||||
//
|
||||
// This is the simplified equivalent: if the checked-out repo has a
|
||||
// config.yaml at its root, read it and merge its keys into the
|
||||
// pipeline's config Map — repo-committed values override whatever the
|
||||
// Jenkinsfile call passed in. Mutates config in place (Groovy Maps are
|
||||
// passed by reference), so every later stage that already has a
|
||||
// reference to the same object sees the merge — nothing needs to be
|
||||
// reassigned or returned.
|
||||
//
|
||||
// Entirely optional — a repo with no config.yaml just keeps running on
|
||||
// the Jenkinsfile call's defaults, same as before this existed.
|
||||
def run(Map config) {
|
||||
stage(stageName('Load config.yaml')) {
|
||||
dir(config.repo_name) {
|
||||
if (fileExists('config.yaml')) {
|
||||
def repoConfig = readYaml file: 'config.yaml'
|
||||
if (repoConfig instanceof Map) {
|
||||
config.putAll(repoConfig)
|
||||
log.info("loadConfig: merged config.yaml — keys: ${repoConfig.keySet()}")
|
||||
}
|
||||
} else {
|
||||
log.info('loadConfig: no config.yaml in repo root, using Jenkinsfile call defaults only.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import jxl.*
|
||||
import hudson.util.PersistedList
|
||||
import jenkins.model.Jenkins
|
||||
import jenkins.branch.*
|
||||
import jenkins.plugins.git.*
|
||||
import org.jenkinsci.plugins.workflow.multibranch.*
|
||||
|
||||
import com.cloudbees.hudson.plugins.folder.*
|
||||
import org.jenkinsci.plugins.github_branch_source.*
|
||||
import org.jenkinsci.plugins.workflow.libs.*
|
||||
import hudson.scm.SCM
|
||||
import hudson.plugins.git.*
|
||||
import net.gleske.scmfilter.impl.trait.*
|
||||
|
||||
def applicationOnboard(String foldername, String repo_name) {
|
||||
// Bring some values in from ansible using the jenkins_script modules wierd "args" approach (these are not gstrings)
|
||||
String folderName = "${foldername}"
|
||||
String repoName = "${repo_name}"
|
||||
String scriptPath = "Jenkinsfile"
|
||||
String gitRepo = "https://github.com/Homelab/${repo_name}.git"
|
||||
String mBPName = "${repo_name}-cicd"
|
||||
String credentialsId = env.GITHUB_CRED
|
||||
|
||||
Jenkins jenkins = Jenkins.instance // saves some typing
|
||||
|
||||
// Get the folder where this job should be
|
||||
// def folder = jenkins.getItem(folderName)
|
||||
// //Create the folder if it doesn't exist
|
||||
// if (folder == null) {
|
||||
// folder = jenkins.createProject(Folder.class, folderName)
|
||||
// }
|
||||
|
||||
// Multibranch creation/update
|
||||
WorkflowMultiBranchProject mbp
|
||||
def view = jenkins.getView(foldername)
|
||||
Item item = jenkins.getItem(mBPName)
|
||||
if ( item != null ) {
|
||||
// Update case
|
||||
mbp = (WorkflowMultiBranchProject) item
|
||||
} else {
|
||||
// Create case
|
||||
mbp = jenkins.createProject(WorkflowMultiBranchProject.class, mBPName)
|
||||
}
|
||||
|
||||
// Configure the script this MBP uses
|
||||
mbp.getProjectFactory().setScriptPath(scriptPath)
|
||||
|
||||
def implicit = false
|
||||
def defaultVersion = "master"
|
||||
def traits = []
|
||||
|
||||
GitHubSCMSource gitHubSCMSource = new GitHubSCMSource("Homelab", repoName, gitRepo, implicit)
|
||||
gitHubSCMSource.credentialsId = credentialsId
|
||||
|
||||
BranchDiscoveryTrait branchDiscoveryTrait = new BranchDiscoveryTrait(3)
|
||||
OriginPullRequestDiscoveryTrait pullRequestTrait = new OriginPullRequestDiscoveryTrait(1)
|
||||
WildcardSCMHeadFilterTrait wildcardSCMHeadFilterTrait = new WildcardSCMHeadFilterTrait('gcp-main*','','','*')
|
||||
traits.add(branchDiscoveryTrait)
|
||||
traits.add(pullRequestTrait)
|
||||
traits.add(wildcardSCMHeadFilterTrait)
|
||||
gitHubSCMSource.setTraits(traits)
|
||||
BranchSource branchSource = new BranchSource(gitHubSCMSource)
|
||||
NoTriggerBranchProperty noTriggerBranchProperty = new NoTriggerBranchProperty()
|
||||
BranchProperty[] ntbp = [noTriggerBranchProperty]
|
||||
branchSource.setStrategy(new DefaultBranchPropertyStrategy(ntbp))
|
||||
|
||||
PersistedList sources = mbp.getSourcesList()
|
||||
sources.clear()
|
||||
sources.add(branchSource)
|
||||
view.add(mbp)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.homelab.stages
|
||||
|
||||
// Rewritten for this homelab. The original posted to Homelab's real Slack
|
||||
// workspace, an internal deployment-tracker API, and Ringmaster (their
|
||||
// deployment-approval system) — none of which exist here. This just logs
|
||||
// to the build console, which is all a solo homelab pipeline needs;
|
||||
// swap in a real notification channel later if you ever want one (e.g.
|
||||
// a Gitea webhook back to a commit status, or your own Slack if you set
|
||||
// one up).
|
||||
def run(Map config) {
|
||||
stage(stageName('Notify')) {
|
||||
log.info("Build ${currentBuild.currentResult} for ${config.repo_name} — tag ${env.TAG} — ${env.BUILD_URL}")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.homelab.stages
|
||||
|
||||
// Simplified from the real devops-lib's vars/runHooks.groovy — same core
|
||||
// mechanism: repo-declared scripts under hooks.<phase> in config.yaml,
|
||||
// blocking by default with an explicit `blocking: false` opt-out to
|
||||
// advisory (log + continue), repo-relative path validation (no absolute
|
||||
// paths, no `..` traversal), interpreter resolution (explicit >
|
||||
// extension > shebang), python venv + requirements provisioning, a
|
||||
// timeout per hook. Dropped: the deploy-phase cdHookRunner reuse (we
|
||||
// don't have a separate deploy-phase repo checkout — everything happens
|
||||
// in one pipeline) and the Turbo-Turtle callback system (TT_* env vars
|
||||
// renamed to HOOK_* — same shape, no Homelab-specific meaning attached).
|
||||
//
|
||||
// Called as runHooksObj.run(config, 'pre_build') / 'post_build' from
|
||||
// vars/homelabPipeline.groovy, around the buildDocker stage.
|
||||
def run(Map config, String phase) {
|
||||
def hooksBlock = config?.hooks
|
||||
if (!(hooksBlock instanceof Map)) {
|
||||
return
|
||||
}
|
||||
def hooks = hooksBlock[phase]
|
||||
if (!(hooks instanceof List) || hooks.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
String repoName = config.repo_name
|
||||
hooks.eachWithIndex { hook, idx ->
|
||||
runOneHook(repoName, phase, idx, hook)
|
||||
}
|
||||
}
|
||||
|
||||
def runOneHook(String repoName, String phase, int idx, def hook) {
|
||||
String name = (hook?.name ?: "${phase}-${idx}").toString()
|
||||
String script = hook?.script?.toString()
|
||||
String interpreter = hook?.interpreter?.toString()
|
||||
String requirements = hook?.requirements?.toString()
|
||||
// Blocking by default; only an explicit `blocking: false` demotes to advisory.
|
||||
boolean blocking = !(hook?.blocking?.toString() == 'false')
|
||||
int timeoutSeconds = 600
|
||||
if (hook?.timeout_seconds) {
|
||||
try { timeoutSeconds = hook.timeout_seconds.toString().toInteger() } catch (ignored) { timeoutSeconds = 600 }
|
||||
}
|
||||
|
||||
if (!script) {
|
||||
error("runHooks: ${phase}[${idx}] '${name}' has no 'script' path in config.yaml.")
|
||||
}
|
||||
assertRepoRelative(script, "${phase}[${idx}] '${name}' script")
|
||||
if (requirements) {
|
||||
assertRepoRelative(requirements, "${phase}[${idx}] '${name}' requirements")
|
||||
}
|
||||
|
||||
stage(stageName("${phase}: ${name}")) {
|
||||
container('docker-cli') {
|
||||
dir(repoName) {
|
||||
if (!fileExists(script)) {
|
||||
error("runHooks: ${phase} hook '${name}' script not found in repo: ${script}")
|
||||
}
|
||||
List<String> hookEnv = [
|
||||
"HOOK_PHASE=${phase}",
|
||||
"HOOK_NAME=${name}",
|
||||
"HOOK_REPO=${repoName}",
|
||||
"HOOK_IMAGE_TAG=${env.TAG ?: ''}",
|
||||
]
|
||||
String runCmd = buildRunCommand(script, interpreter, requirements)
|
||||
log.info("runHooks: executing ${phase} hook '${name}' (${script}), blocking=${blocking}, timeout=${timeoutSeconds}s")
|
||||
withEnv(hookEnv) {
|
||||
try {
|
||||
timeout(time: timeoutSeconds, unit: 'SECONDS') {
|
||||
sh(script: runCmd)
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (blocking) {
|
||||
log.error("runHooks: blocking ${phase} hook '${name}' failed: ${e}")
|
||||
throw e
|
||||
}
|
||||
log.warning("runHooks: advisory ${phase} hook '${name}' failed (non-blocking): ${e}")
|
||||
currentBuild.description = (currentBuild.description ? currentBuild.description + " | " : "") + "hook(${name}) advisory-failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def buildRunCommand(String script, String interpreter, String requirements) {
|
||||
String interp = interpreter
|
||||
if (!interp) {
|
||||
if (script.endsWith('.py')) {
|
||||
interp = 'python3'
|
||||
} else if (script.endsWith('.sh')) {
|
||||
interp = 'bash'
|
||||
}
|
||||
}
|
||||
|
||||
boolean isPython = (interp == 'python3' || interp == 'python' || script.endsWith('.py'))
|
||||
// docker:27-cli is minimal Alpine — only `sh` is guaranteed present.
|
||||
// bash/python3 need installing on demand, unlike the real system's
|
||||
// pod images which already bundle a full toolchain.
|
||||
String installLine = ''
|
||||
if (isPython) {
|
||||
installLine = 'apk add --no-cache python3 py3-pip py3-virtualenv >/dev/null'
|
||||
} else if (interp == 'bash') {
|
||||
installLine = 'apk add --no-cache bash >/dev/null'
|
||||
}
|
||||
|
||||
if (requirements && isPython) {
|
||||
String py = interp ?: 'python3'
|
||||
return """
|
||||
set -e
|
||||
${installLine}
|
||||
${py} -m venv .hook_venv
|
||||
. .hook_venv/bin/activate
|
||||
pip install --quiet --disable-pip-version-check -r ${requirements}
|
||||
${py} ${script}
|
||||
""".stripIndent().trim()
|
||||
}
|
||||
|
||||
if (interp) {
|
||||
return "set -e\n${installLine}\n${interp} ${script}"
|
||||
}
|
||||
// No interpreter resolved — rely on the script's shebang.
|
||||
return "set -e\nchmod +x ${script}\n./${script}"
|
||||
}
|
||||
|
||||
// Rejects absolute paths and parent-directory traversal so a hook can
|
||||
// only execute code that lives inside the checked-out repo.
|
||||
def assertRepoRelative(String p, String what) {
|
||||
if (p.startsWith('/')) {
|
||||
error("runHooks: ${what} path '${p}' must be repo-relative, not absolute.")
|
||||
}
|
||||
if (p == '..' || p.startsWith('../') || p.contains('/../')) {
|
||||
error("runHooks: ${what} path '${p}' must not traverse outside the repo ('..').")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.homelab.stages
|
||||
|
||||
def run(Map config) {
|
||||
try {
|
||||
def repo_name = config.repo_name
|
||||
stage('Security scan') {
|
||||
if (config.skip_security_scan) {
|
||||
log.info('Skipping - Security Scan')
|
||||
}
|
||||
else {
|
||||
final String url = '172.31.5.29:63232/scans'
|
||||
final def(String response, String code) = sh(returnStdout: true, script: "curl -s -X POST -H 'Content-Type: application/json' -w '\\n%{response_code}' $url -d '{\"reponame\":\"$repo_name\",\"branch\":\"$BRANCH_NAME\"}'").trim().tokenize('\n')
|
||||
log.info("HTTP response status code : ${code}")
|
||||
log.info("Response: ${response}")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( Exception e) {
|
||||
env.msg = 'failed in security scan . Please check console output for more details.'
|
||||
log.error(env.msg)
|
||||
currentBuild.result = env.FAILURE
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.homelab.stages
|
||||
|
||||
// New stage — the homelab equivalent of devops-lib's real
|
||||
// refresh_and_sync (deployArgoCD.groovy step 4: `argocd app sync
|
||||
// --hard-refresh`). Without this, the pipeline stops at the tag-bump
|
||||
// commit and a human has to remember to click Sync in the ArgoCD UI —
|
||||
// their real system never leaves that step manual. Uses the ArgoCD REST
|
||||
// API directly via curl (scoped jenkins-ci token, not the argocd CLI
|
||||
// binary — avoids adding another container to dind-pod.yaml just for
|
||||
// this one call) against the server's internal cluster-DNS Service, not
|
||||
// the ingress — same pod-to-pod reasoning as pushing to Harbor.
|
||||
//
|
||||
// ARGOCD_TOKEN comes from the pod env (dind-pod.yaml, ESO-managed secret)
|
||||
// — not Jenkins' own credential store, staying consistent with the rest
|
||||
// of this pipeline. It's marked optional there, so if it's genuinely
|
||||
// empty (bootstrap token not generated yet), fail loudly here with a
|
||||
// clear message rather than a confusing curl auth error.
|
||||
//
|
||||
// Expects in config:
|
||||
// argo_app_name the Application's metadata.name, e.g. demo-go-app
|
||||
// argo_server_url e.g. http://argocd-admin-prd-server.argocd.svc.cluster.local
|
||||
def run(Map config) {
|
||||
stage(stageName('Sync ArgoCD Application')) {
|
||||
container('docker-cli') {
|
||||
if (!env.ARGOCD_TOKEN?.trim()) {
|
||||
log.error('ARGOCD_TOKEN is empty — the jenkins-ci account token has not been generated yet. See secretstores/argocd-jenkins-ci-token.yaml for the one-time bootstrap steps.')
|
||||
error('Skipping ArgoCD sync: no token available.')
|
||||
}
|
||||
sh """
|
||||
apk add --no-cache curl >/dev/null
|
||||
curl -sf -X POST \\
|
||||
-H "Authorization: Bearer \$ARGOCD_TOKEN" \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-d '{}' \\
|
||||
"${config.argo_server_url ?: 'http://argocd-admin-prd-server.argocd.svc.cluster.local'}/api/v1/applications/${config.argo_app_name}/sync"
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.homelab.stages
|
||||
|
||||
// New stage — no equivalent this simple existed before. The original's
|
||||
// version (deployArgoCD.groovy's update_helm_repo, ~900 lines) ran a full
|
||||
// PR workflow — branch, commit, push, create PR, merge PR, delete branch
|
||||
// — against Homelab's real GitHub org, driven by a BU/team templating
|
||||
// engine and canary-enforcement rules. Solo homelab, no reviewer to wait
|
||||
// on: this just clones, bumps the tag with yq, commits, and pushes
|
||||
// straight to main. ArgoCD picks up the change on its own next sync
|
||||
// (manual, same as everything else in this repo — see the sister repo's
|
||||
// README on sync policy).
|
||||
//
|
||||
// Expects in config:
|
||||
// repo_name the app's repo name — also the first path segment
|
||||
// under devops-helm-charts/values/
|
||||
// service_name second path segment; same as repo_name for a
|
||||
// single-service repo, different for a monorepo with
|
||||
// several services sharing one repo
|
||||
// helm_repo_url e.g. http://gitea.192.168.1.7.nip.io/mukul/devops-helm-charts.git
|
||||
// image_tag_yq_path yq path to the tag field, e.g. .deployment.image.tag
|
||||
// gitea_cred Jenkins credential ID for a Gitea push-capable token (default: gitea-ci-credentials)
|
||||
//
|
||||
// Values file path is computed, not passed directly:
|
||||
// devops-helm-charts/values/<repo_name>/<service_name>/values.yaml
|
||||
def run(Map config) {
|
||||
def valuesFile = "values/${config.repo_name}/${config.service_name}/values.yaml"
|
||||
stage(stageName('Update Helm chart image tag')) {
|
||||
withCredentials([usernamePassword(credentialsId: config.gitea_cred ?: 'gitea-ci-credentials', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_PASS')]) {
|
||||
dir('helm-chart-repo') {
|
||||
deleteDir()
|
||||
def authedUrl = config.helm_repo_url.replaceFirst('http://', "http://\${GIT_USER}:\${GIT_PASS}@")
|
||||
sh """
|
||||
git clone ${authedUrl} .
|
||||
yq -i '${config.image_tag_yq_path} = "${env.TAG}"' ${valuesFile}
|
||||
git config user.email 'jenkins-ci@homelab.local'
|
||||
git config user.name 'jenkins-ci'
|
||||
git commit -am 'ci: bump ${config.repo_name}/${config.service_name} image tag to ${env.TAG}'
|
||||
git push origin main
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.homelab.stages
|
||||
|
||||
import com.homelab.utilities.buTeamMapping
|
||||
|
||||
def run(String bu, String team,String module){
|
||||
stage("Validate BU and Team"){
|
||||
def vObj = new buTeamMapping()
|
||||
if (vObj.validate(bu,team)){
|
||||
log.info("Correct team and BU values")
|
||||
}
|
||||
else{
|
||||
error "Incorrect BU and team values provided"
|
||||
}
|
||||
if(module == ""){
|
||||
error "Module can't be empty. if there is no module, Please provide the parameter value as module_less"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.homelab.utilities
|
||||
|
||||
def create() {
|
||||
withCredentials([file(credentialsId: 'ssh-private-key', variable: 'FILE')]) {
|
||||
sh """
|
||||
cat ${FILE} > ./id_github_jenkins
|
||||
chmod 600 ./id_github_jenkins
|
||||
|
||||
# Ensure .ssh directory has correct permissions
|
||||
chmod 700 /root/.ssh
|
||||
|
||||
# Fix SSH config file permissions if it exists
|
||||
if [ -f /root/.ssh/config ]; then
|
||||
chmod 600 /root/.ssh/config
|
||||
chown root:root /root/.ssh/config
|
||||
fi
|
||||
|
||||
# Fix existing SSH private key permissions if it exists
|
||||
if [ -f /root/.ssh/id_rsa ]; then
|
||||
chmod 600 /root/.ssh/id_rsa
|
||||
chown root:root /root/.ssh/id_rsa
|
||||
fi
|
||||
|
||||
# Fix any other SSH key files that might exist
|
||||
find /root/.ssh -type f -name "id_*" -exec chmod 600 {} \\; 2>/dev/null || true
|
||||
"""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Purpose: Utility function to return BU and their respective teams
|
||||
Author: Avinash kumar Lodhi
|
||||
*/
|
||||
package com.homelab.utilities
|
||||
|
||||
def validate(String bu, String team) {
|
||||
def bu_team_map = ['supply':['supplier-ads', 'supplier-ads-frontend', 'experience', 'fulfilment', 'fulfilment-frontend','financial-services', 'cataloging', 'cataloging-frontend', 'payout', 'payout-frontend', 'supplier-acquisition-activation', 'supplier-service', 'returns', 'supply-shared', 'display-ads', 'offers','transact', 'supplier-live-commerce'],
|
||||
'demand':['comms-platform', 'live-commerce', 'shopping-platform', 'product-feed', 'search', 'product-meta', 'user-growth', 'web', 'transact', 'communications', 'discovery-platform', 'offers', 'android-platform', 'ios', 'demand-shared', 'discovery-ranking'],
|
||||
'farmiso':['farmiso'],
|
||||
'admin':['devops'],
|
||||
'central':['shared', 'devops', 'psec', 'dbe'],
|
||||
'dataengg':['data-platform', 'dataengg-shared', 'data-intelligence', 'data-platform-consumption', 'data-platform-ingestion', 'data-platform-nrt', 'data-platform-prism-frmw', 'data-platform-experimentation'],
|
||||
'datascience':['data-science', 'ml-platform', 'for-you', 'recommendation', 'catalog-listing-page', 'search', 'advertisement', 'explore', 'pricing', 'product-match', 'catalog-taxonomy', 'brand-infringment', 'fds', 'return-reimbursements', 'fullfilment', 'ugc-moderation-analysis', 'home-page', 'core', 'usergrowth', 'demand-forecast', 'catalog-qc'],
|
||||
'mcache':['mcache', 'mcache-shared', 'supplier-service'],
|
||||
'infra':['devops', 'dbe']
|
||||
]
|
||||
if (bu == null || team == null) {
|
||||
return null
|
||||
}
|
||||
return bu_team_map[bu].contains(team)
|
||||
}
|
||||
|
||||
def get_initials(String targetMap, String targetString) {
|
||||
def bu_initials = ['supply':'supl',
|
||||
'demand':'dmnd',
|
||||
'farmiso':'farm',
|
||||
'admin':'admn',
|
||||
'central':'cntr',
|
||||
'dataengg':'deng',
|
||||
'datascience':'dsci',
|
||||
'mcache':'mche',
|
||||
'infra':'infr'
|
||||
]
|
||||
def team_initials = ['supplier-ads':'ads',
|
||||
'comms-platform': 'cplat',
|
||||
'supplier-ads-frontend':'fads',
|
||||
'experience':'xp',
|
||||
'fulfilment':'fnf',
|
||||
'fulfilment-frontend':'ffnf',
|
||||
'financial-services':'fsvc',
|
||||
'cataloging':'ctlng',
|
||||
'cataloging-frontend':'fctlg',
|
||||
'payout':'pay',
|
||||
'payout-frontend':'fpay',
|
||||
'supplier-acquisition-activation':'saa',
|
||||
'supplier-service':'ssvc',
|
||||
'seller-services': 'sis',
|
||||
'live-commerce':'lcom',
|
||||
'shopping-platform':'splat',
|
||||
'product-feed':'pfeed',
|
||||
'search':'srch',
|
||||
'product-meta':'pmeta',
|
||||
'user-growth':'grwth',
|
||||
'web':'web',
|
||||
'transact':'trnst',
|
||||
'communications':'comms',
|
||||
'discovery-platform':'dplat',
|
||||
'farmiso':'farm',
|
||||
'devops':'devop',
|
||||
'offers':'offer',
|
||||
'returns':'retrn',
|
||||
'android-platform':'andrd',
|
||||
'ios':'ios',
|
||||
'shared':'xcntr',
|
||||
'supply-shared':'xsupl',
|
||||
'demand-shared':'xdmnd',
|
||||
'data-platform':'dp',
|
||||
'data-science':'ds',
|
||||
'ml-platform':'ml',
|
||||
'dataengg-shared':'xdeng',
|
||||
'datascience-shared':'xdsci',
|
||||
'data-intelligence':'di',
|
||||
'recommendation':'rcmnd',
|
||||
'catalog-listing-page':'ctllp',
|
||||
'advertisement':'adv',
|
||||
'explore':'explr',
|
||||
'pricing':'price',
|
||||
'product-match':'patch',
|
||||
'catalog-taxonomy':'ctltx',
|
||||
'brand-infringment':'brndi',
|
||||
'fds':'fds',
|
||||
'return-reimbursements':'retrr',
|
||||
'fullfilment':'flfmt',
|
||||
'ugc-moderation-analysis':'umdra',
|
||||
'home-page':'hpage',
|
||||
'usergrowth':'ugrwt',
|
||||
'demand-forecast':'dmndf',
|
||||
'catalog-qc':'ctlqc',
|
||||
'data-platform-consumption':'dpcon',
|
||||
'data-platform-ingestion':'dping',
|
||||
'data-platform-nrt':'dpnrt',
|
||||
'data-platform-prism-frmw':'dpprf',
|
||||
'data-platform-experimentation':'dpexp',
|
||||
'display-ads':'dplay',
|
||||
'discovery-ranking':'drank',
|
||||
'mcache':'mche',
|
||||
'mcache-shared':'xmche',
|
||||
'supplier-live-commerce':'slcom',
|
||||
'trust-and-safety': 'tns',
|
||||
'valmo': 'vlm',
|
||||
'psec':'psec',
|
||||
'dbe':'dbe',
|
||||
'dev-productivity':'devprd']
|
||||
|
||||
if (targetString == null) {
|
||||
return null
|
||||
}
|
||||
switch (targetMap) {
|
||||
case 'bu_initials':
|
||||
return bu_initials[targetString]
|
||||
case 'team_initials':
|
||||
return team_initials[targetString]
|
||||
default:
|
||||
return 'Undefined option'
|
||||
}
|
||||
}
|
||||
|
||||
def get_team_initials(String team) {
|
||||
return get_initials('team_initials', team)
|
||||
}
|
||||
|
||||
def get_bu_initials(String bu) {
|
||||
return get_initials('bu_initials', bu)
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
|
||||
package com.homelab.utilities
|
||||
|
||||
def getWhitelistedRepos(fileName){
|
||||
dir('whitelist'){
|
||||
git(
|
||||
url: "https://github.com/Homelab/whitelists.git",
|
||||
branch: "main",
|
||||
credentialsId: 'cicd-github-app',
|
||||
)}
|
||||
def yaml = readYaml file: "whitelist/${fileName}.yaml"
|
||||
return yaml.get("repos", []) as Set
|
||||
}
|
||||
|
||||
def getWhitelistedDeployable(fileName, keyName){
|
||||
dir('whitelist'){
|
||||
git(
|
||||
url: "https://github.com/Homelab/whitelists.git",
|
||||
branch: "main",
|
||||
credentialsId: 'cicd-github-app',
|
||||
)}
|
||||
def yaml = readYaml file: "whitelist/${fileName}.yaml"
|
||||
return yaml.get(keyName, []) as Set
|
||||
}
|
||||
|
||||
/*
|
||||
* return `true` if we should not proceed
|
||||
*/
|
||||
def isMultizoneEnabled( String deployable){
|
||||
def WHITELIST = getWhitelistedDeployable("multizone-enabled-repos" , "multizone_enabled_deployables")
|
||||
if (WHITELIST.contains(deployable)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* return `true` if we should not proceed
|
||||
*/
|
||||
def skipSonarCheckForbidden(Map config, Map environment_map) {
|
||||
def branch = env.BRANCH_NAME
|
||||
def environment = environment_map.getOrDefault(branch, "int")
|
||||
|
||||
def WHITELIST = getWhitelistedRepos("skip-sonar-whitelist")
|
||||
|
||||
// returns 'true' if we aren't skipping sonar
|
||||
// or if we're allowed to skip sonar
|
||||
def build_version = config['dockerBuildVersion']
|
||||
def repo_name = config['repo_name']
|
||||
|
||||
// if not a maven build OR if it a hotfix we exit early and don’t care what skip_sonar is
|
||||
if (WHITELIST.contains(repo_name)|| !(build_version.contains("maven")) || environment != "prd" || branch.contains("hotfix")){
|
||||
return false;
|
||||
}
|
||||
|
||||
return config['skip_sonar'];
|
||||
}
|
||||
|
||||
def skipSonarCheckForGo(Map config) {
|
||||
def branch = env.BRANCH_NAME
|
||||
def WHITELIST = getWhitelistedRepos("skip-sonar-whitelist")
|
||||
def build_version = config['dockerBuildVersion']
|
||||
def repo_name = config['repo_name']
|
||||
echo "env.INFRA_ENV: ${env.INFRA_ENV}"
|
||||
if (WHITELIST.contains(repo_name)|| branch.contains("hotfix")|| env.INFRA_ENV == "toolchain"){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* return `true` if we should not proceed
|
||||
*/
|
||||
def appConfigDisabledForbidden(boolean appConfigEnabled, String repo_name, String environment, String build_version){
|
||||
def branch = env.CHANGE_TARGET
|
||||
def WHITELIST = getWhitelistedRepos("app-config-disabled")
|
||||
if (WHITELIST.contains(repo_name) || environment!="stg"){
|
||||
return false
|
||||
}
|
||||
if (!(build_version.contains("maven") || build_version.contains("gradle"))){
|
||||
return false
|
||||
}
|
||||
return !appConfigEnabled
|
||||
}
|
||||
|
||||
/*
|
||||
* return `true` if we should not proceed
|
||||
*/
|
||||
def allowedNonDevelopPrDeploymentToIntRepos( String repo_name){
|
||||
def WHITELIST = getWhitelistedRepos("allowedNonDevelopPrDeploymentToInt")
|
||||
if (WHITELIST.contains(repo_name)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* return `true` if we should not proceed
|
||||
*/
|
||||
def ValidateCacConfigForRepo(boolean ValidateConfig, String repo_name ){
|
||||
def branch = env.CHANGE_TARGET
|
||||
def WHITELIST = getWhitelistedRepos("ValidateCacConfig")
|
||||
if (WHITELIST.contains(repo_name)) {
|
||||
return true
|
||||
}
|
||||
return ValidateConfig
|
||||
}
|
||||
|
||||
def getToolchainEnv() {
|
||||
def paramsAction = currentBuild.rawBuild.getAction(hudson.model.ParametersAction.class)
|
||||
if (paramsAction) {
|
||||
echo "paramsAction: ${paramsAction}"
|
||||
def p = paramsAction.getParameter("TOOLCHAIN_ENV")
|
||||
echo "p: ${p}"
|
||||
if (p) {
|
||||
return p.getValue()?.toString()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
def run(Map config) {
|
||||
def branch_name = env.BRANCH_NAME
|
||||
if (env.INFRA_ENV == 'toolchain' && (config.build_tool?.startsWith('node-') || config.dockerBuildVersion?.startsWith('node-'))) {
|
||||
branch_name = 'develop' // develop maps to stg in the environment_map
|
||||
def tcEnv = getToolchainEnv()
|
||||
env.TOOLCHAIN_ENV = tcEnv
|
||||
log.info("Successfully extracted TOOLCHAIN_ENV from trigger cause: ${env.TOOLCHAIN_ENV}")
|
||||
}
|
||||
def environment_map = ['master':'prd', 'main':'prd', 'develop':'stg', 'gcp-main':'prd', 'farmiso-main':'prd', 'gcp-master':'prd', 'gcp-dev':'stg']
|
||||
env.skip_user_input = config.skip_user_input ?: false
|
||||
|
||||
// don't allow skip_sonar
|
||||
if (skipSonarCheckForbidden(config, environment_map)){
|
||||
throw new Exception("Not allowed to skip sonar (skip_sonar in config.yaml)")
|
||||
}
|
||||
|
||||
if (env.CHANGE_ID) {
|
||||
branch_name = env.CHANGE_TARGET
|
||||
environment_map = ['master':'int', 'main':'int', 'gcp-main':'int', 'farmiso-main':'int', 'gcp-master':'int', 'develop':'ftr', 'gcp-dev':'ftr']
|
||||
}
|
||||
environment_map[branch_name] = environment_map[branch_name] ?: 'ftr'
|
||||
|
||||
if (config.containsKey('branch_params')) {
|
||||
Map branch_config = config['branch_params'].collectEntries { key, value -> branch_name.matches(key) ? value : [ : ] }
|
||||
config.remove('branch_params')
|
||||
config.putAll(branch_config)
|
||||
}
|
||||
if (config.containsKey('environment')) {
|
||||
Map envrionment_config = config['environment'].collectEntries { key, value -> environment_map[branch_name].matches(key) ? value : [ : ] }
|
||||
config.remove('environment')
|
||||
config.putAll(envrionment_config)
|
||||
}
|
||||
|
||||
env.GITHUB_CRED = 'svc-devops-homelab'
|
||||
env.cicd_environment = environment_map[branch_name]
|
||||
env.helm_repo_name = 'devops-helm-charts'
|
||||
env.argo_repo_name = 'devops-argo-config'
|
||||
|
||||
echo "Branch Name - ${branch_name} and Environment - ${env.cicd_environment}"
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
def prodAccountID = '847438129436'
|
||||
def prodRegion = 'ap-southeast-1'
|
||||
def prodObjBucket = 'homelab-prod-artifacts'
|
||||
def devAccountID = '766380763301'
|
||||
def devObjBucket = 'homelab-stg-artifacts'
|
||||
def devRegion = 'ap-south-1'
|
||||
def accountDetails = [
|
||||
'prd': [
|
||||
'accountID': prodAccountID,
|
||||
'region': prodRegion,
|
||||
'objBucket': prodObjBucket
|
||||
],
|
||||
'int': [
|
||||
'accountID': prodAccountID,
|
||||
'region': prodRegion,
|
||||
'objBucket': prodObjBucket
|
||||
],
|
||||
'stg': [
|
||||
'accountID': devAccountID,
|
||||
'region': devRegion,
|
||||
'objBucket': devObjBucket
|
||||
],
|
||||
'ftr': [
|
||||
'accountID': devAccountID,
|
||||
'region': devRegion,
|
||||
'objBucket': devObjBucket
|
||||
]
|
||||
]
|
||||
env.accountID = accountDetails[env.cicd_environment]['accountID']
|
||||
env.region = accountDetails[env.cicd_environment]['region']
|
||||
env.registry = "${env.accountID}.dkr.ecr.${env.region}.amazonaws.com"
|
||||
env.buildRegistry = env.registry
|
||||
env.helmChartsPath = 'charts'
|
||||
env.defaultHelmChartVersion = '1.0.10'
|
||||
env.objBucket = accountDetails[env.cicd_environment]['objBucket']
|
||||
env.skip_notify = false
|
||||
echo "${env.accountID}.dkr.ecr.${env.region}.amazonaws.com"
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
def prodVaultURL = 'https://vault-prd.homelabgcp.in'
|
||||
def prodVaultToken = 'vault-prd-token'
|
||||
def prodSonarURL = 'https://sonarqube-prd.homelabgcp.in'
|
||||
def prodSonarToken = 'sonar-token-prod'
|
||||
def prodSonarEnv = 'sonarqube-test'
|
||||
def prodGoProxyUrl = 'https://athens-prd.homelabgcp.in'
|
||||
def prdDockerHost = 'dind-prd-svc'
|
||||
def preProdDockerHost = 'dind-int-svc'
|
||||
def prodGCPProject = "homelab-${config.bu}-prd-0622"
|
||||
def preprodGCPProject = "homelab-shared-int-0525"
|
||||
def devVaultURL = 'https://vault-dev.homelabgcp.in'
|
||||
def devVaultToken = 'vault-dev-token'
|
||||
def devSonarURL = "https://sonarqube-${config.bu}-dev.homelabgcp.in"
|
||||
def devSonarToken = "sonar-token-${config.bu}-dev"
|
||||
def devSonarEnv = "sonar-${config.bu}-dev"
|
||||
def devGoProxyUrl = 'https://athens-dev.homelabgcp.in'
|
||||
def devGCPProject = "homelab-${config.bu}-dev-0622"
|
||||
def devDockerHost = 'dind-dev-new-svc.jenkins-new.svc.cluster.local'
|
||||
def toolchainDockerHost = 'toolchain-dind-dev-svc.jenkins-toolchain.svc.cluster.local'
|
||||
def accountDetails = [
|
||||
'prd': [
|
||||
'vaultURL': prodVaultURL,
|
||||
'vaultToken': prodVaultToken,
|
||||
'sonarURL': prodSonarURL,
|
||||
'sonarToken': prodSonarToken,
|
||||
'GCPProject': prodGCPProject,
|
||||
'sonarEnv': prodSonarEnv,
|
||||
'GCPLBProject': prodGCPProject,
|
||||
'goProxyUrl': prodGoProxyUrl,
|
||||
'dockerHost': prdDockerHost
|
||||
],
|
||||
'int': [
|
||||
'vaultURL': prodVaultURL,
|
||||
'vaultToken': prodVaultToken,
|
||||
'sonarURL': prodSonarURL,
|
||||
'sonarToken': prodSonarToken,
|
||||
'GCPProject': preprodGCPProject,
|
||||
'sonarEnv': prodSonarEnv,
|
||||
'GCPLBProject': prodGCPProject,
|
||||
'goProxyUrl': prodGoProxyUrl,
|
||||
'dockerHost': preProdDockerHost
|
||||
],
|
||||
'stg': [
|
||||
'vaultURL': devVaultURL,
|
||||
'vaultToken': devVaultToken,
|
||||
'sonarURL': devSonarURL,
|
||||
'sonarToken': devSonarToken,
|
||||
'GCPProject': devGCPProject,
|
||||
'sonarEnv': devSonarEnv,
|
||||
'GCPLBProject': devGCPProject,
|
||||
'goProxyUrl': devGoProxyUrl,
|
||||
'dockerHost': devDockerHost
|
||||
],
|
||||
'ftr': [
|
||||
'vaultURL': devVaultURL,
|
||||
'vaultToken': devVaultToken,
|
||||
'sonarURL': devSonarURL,
|
||||
'sonarToken': devSonarToken,
|
||||
'GCPProject': devGCPProject,
|
||||
'sonarEnv': devSonarEnv,
|
||||
'GCPLBProject': devGCPProject,
|
||||
'goProxyUrl': devGoProxyUrl,
|
||||
'dockerHost': devDockerHost ]
|
||||
]
|
||||
env.GCPProject = accountDetails[env.cicd_environment]['GCPProject']
|
||||
env.GCPLBProject = accountDetails[env.cicd_environment]['GCPLBProject']
|
||||
env.registry = 'asia-southeast1-docker.pkg.dev/homelab-devops-admin-0622'
|
||||
if (env.INFRA_ENV == 'toolchain') {
|
||||
env.registry = 'asia-southeast1-docker.pkg.dev/homelab-central-dev-0622/toolchain'
|
||||
}
|
||||
env.buildRegistry = 'asia-southeast1-docker.pkg.dev/homelab-devops-admin-0622/admin'
|
||||
env.helmChartsPath = env.cicd_environment == 'prd' ? 'values_v3' : 'values_v2'
|
||||
env.defaultHelmChartVersion = '2.0.0'
|
||||
env.objBucket = "gcs-infr-dvps-homelab-artifacts-${env.cicd_environment}"
|
||||
env.vaultURL = accountDetails[env.cicd_environment]['vaultURL']
|
||||
env.vaultToken = accountDetails[env.cicd_environment]['vaultToken']
|
||||
env.sonarURL = accountDetails[env.cicd_environment]['sonarURL']
|
||||
env.sonarToken = accountDetails[env.cicd_environment]['sonarToken']
|
||||
env.sonarEnv = accountDetails[env.cicd_environment]['sonarEnv']
|
||||
env.goProxyUrl = accountDetails[env.cicd_environment]['goProxyUrl']
|
||||
env.skip_notify = true
|
||||
env.DOCKER_HOST = accountDetails[env.cicd_environment]['dockerHost']
|
||||
if (env.INFRA_ENV == 'toolchain') {
|
||||
env.DOCKER_HOST = toolchainDockerHost
|
||||
}
|
||||
}
|
||||
echo "Bucket and Image Repo Details - ${env.registry} ${env.buildRegistry} ${env.objBucket}"
|
||||
echo "Docker Host - ${env.DOCKER_HOST}"
|
||||
}
|
||||
|
||||
def perDeploymentVars(Map value_binding) {
|
||||
env.BU = value_binding.bu
|
||||
echo "${env.BU}"
|
||||
|
||||
if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
def inClusterName = 'https://kubernetes.default.svc'
|
||||
def prodArgoURL = 'prod-ops-argocd.homelab.com'
|
||||
def devArgoURL = 'stg-dev-argocd.homelabtest.in'
|
||||
def prodK8sCluster = [
|
||||
'supply': 'https://211689C65F4496AAA76FE19B29E24B6E.yl4.ap-southeast-1.eks.amazonaws.com',
|
||||
'demand': 'https://9059D138B6277A0EA592BA7F4B680CEC.gr7.ap-southeast-1.eks.amazonaws.com',
|
||||
'dataengg': 'https://806ADE97231CA65D2A0FFB780352630D.yl4.ap-southeast-1.eks.amazonaws.com',
|
||||
'datascience': 'https://E34D119516F751AFD1A61043B0514726.yl4.ap-southeast-1.eks.amazonaws.com',
|
||||
'central': 'https://C95FCEDF7CEE890F531E1D0488BEC6C3.gr7.ap-southeast-1.eks.amazonaws.com',
|
||||
'mcache': 'https://2FFADF214AD8BE58769C5F2797987E50.gr7.ap-southeast-1.eks.amazonaws.com'
|
||||
]
|
||||
def devK8sCluster = [
|
||||
'supply': inClusterName,
|
||||
'demand': inClusterName,
|
||||
'dataengg': inClusterName,
|
||||
'datascience': inClusterName,
|
||||
'central': inClusterName,
|
||||
'mcache': inClusterName
|
||||
]
|
||||
def accountDetails = [
|
||||
'prd': [
|
||||
'argoURL': prodArgoURL,
|
||||
'argoIncubator': 'prod-app-of-apps',
|
||||
'serverMap': prodK8sCluster
|
||||
],
|
||||
'int': [
|
||||
'argoURL': prodArgoURL,
|
||||
'argoIncubator': 'int-app-of-app',
|
||||
'serverMap': prodK8sCluster
|
||||
],
|
||||
'stg': [
|
||||
'argoURL': devArgoURL,
|
||||
'argoIncubator': 'app-of-apps',
|
||||
'serverMap': devK8sCluster
|
||||
],
|
||||
'ftr': [
|
||||
'argoURL': devArgoURL,
|
||||
'argoIncubator': 'ftr-app-of-apps',
|
||||
'serverMap': devK8sCluster
|
||||
]
|
||||
]
|
||||
env.clusterName = accountDetails[env.cicd_environment]['serverMap'][env.BU]
|
||||
env.argoAppsPath = 'applications'
|
||||
env.argoURL = accountDetails[env.cicd_environment]['argoURL']
|
||||
env.argoCreds = 'argocd-jenkins'
|
||||
env.argoIncubator = accountDetails[env.cicd_environment]['argoIncubator']
|
||||
env.argoAppNS = 'argocd'
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
def prodArgoURL = "argocd-${env.BU}-prd.homelabgcp.in"
|
||||
def prodArgoCreds = "argocd-${env.BU}-prd-creds"
|
||||
def preprodArgoURL = "argocd-shared-int.homelabgcp.in"
|
||||
def preprodArgoCreds = "argocd-shared-int-creds"
|
||||
def devArgoURL = 'argocd-dev.homelabgcp.in'
|
||||
def devArgoCreds = 'argocd-dev-creds'
|
||||
def accountDetails = [
|
||||
'prd': [
|
||||
'argoURL': prodArgoURL,
|
||||
'argoCreds': prodArgoCreds,
|
||||
'argoAppNS': "argocd-${env.BU}-prd",
|
||||
'clusterName': "k8s-${env.BU}-prd-ase1"
|
||||
],
|
||||
'int': [
|
||||
'argoURL': preprodArgoURL,
|
||||
'argoCreds': preprodArgoCreds,
|
||||
'argoAppNS': "argocd-shared-int",
|
||||
'clusterName': "k8s-shared-int-ase1"
|
||||
],
|
||||
'stg': [
|
||||
'argoURL': devArgoURL,
|
||||
'argoCreds': devArgoCreds,
|
||||
'argoAppNS': "argocd-dev",
|
||||
'clusterName': "k8s-${env.BU}-stg-ase1"
|
||||
],
|
||||
'ftr': [
|
||||
'argoURL': devArgoURL,
|
||||
'argoCreds': devArgoCreds,
|
||||
'argoAppNS': "argocd-dev",
|
||||
'clusterName': "k8s-${env.BU}-stg-ase1"
|
||||
]
|
||||
]
|
||||
|
||||
env.clusterName = accountDetails[env.cicd_environment]['clusterName']
|
||||
|
||||
if (env.cicd_environment == 'int') {
|
||||
env.argoAppsPath = "applications_v2/k8s-${env.BU}-int-ase1"
|
||||
} else {
|
||||
env.argoAppsPath = "applications_v2/${env.clusterName}"
|
||||
}
|
||||
|
||||
env.argoURL = accountDetails[env.cicd_environment]['argoURL']
|
||||
env.argoCreds = accountDetails[env.cicd_environment]['argoCreds']
|
||||
env.argoAppNS = accountDetails[env.cicd_environment]['argoAppNS']
|
||||
env.argoIncubator = "incubator-apps-k8s-${env.BU}-${env.cicd_environment}-ase1"
|
||||
}
|
||||
echo "${env.clusterName} ${env.argoURL} ${env.argoIncubator}"
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.homelab.utilities
|
||||
|
||||
// Ported from the real devops-lib's constructTemplate.groovy — same
|
||||
// SimpleTemplateEngine mechanism (${...} interpolation, <% %> control
|
||||
// flow), same @NonCPS requirement (the engine is a non-serialisable Java
|
||||
// object; calling this across a `parallel` boundary would break). Dropped
|
||||
// the Homelab-specific `add_file`/PBAC resolution — this homelab has no
|
||||
// equivalent convention yet; add it back here if that's ever needed.
|
||||
def run(Map binding, String text) {
|
||||
return _construct(binding, text)
|
||||
}
|
||||
|
||||
def renderTemplate(Map binding, String templateFile, String fileName) {
|
||||
def template = libraryResource 'com/homelab/' + templateFile
|
||||
def renderedTemplate = run(binding, template.toString())
|
||||
writeFile file: fileName, text: renderedTemplate
|
||||
}
|
||||
|
||||
@NonCPS
|
||||
def _construct(Map binding, String text) {
|
||||
binding = new HashMap(binding)
|
||||
def engine = new groovy.text.SimpleTemplateEngine()
|
||||
def template = engine.createTemplate(text).make(binding)
|
||||
engine = null
|
||||
return template.toString()
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.homelab.utilities
|
||||
|
||||
def retryDockerPush(String cmd) {
|
||||
int maxAttempts = 5
|
||||
int attempt = 1
|
||||
while (attempt <= maxAttempts) {
|
||||
try {
|
||||
sh cmd
|
||||
break
|
||||
} catch (err) {
|
||||
if (attempt == maxAttempts) {
|
||||
error("Command failed after ${maxAttempts} attempts: ${err}")
|
||||
}
|
||||
echo "Command failed, retrying... (${attempt}/${maxAttempts})"
|
||||
sleep 3
|
||||
attempt++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def imageExists(String registry, String repoName, String tag) {
|
||||
int maxAttempts = 5
|
||||
int attempt = 1
|
||||
|
||||
while (attempt <= maxAttempts) {
|
||||
try {
|
||||
if (env.CLOUD_PROVIDER == 'GCP') {
|
||||
def result = sh(
|
||||
script: "gcloud container images list-tags ${registry}/${repoName} --filter='tags:${tag}' --format='get(tags)'",
|
||||
returnStdout: true
|
||||
).trim()
|
||||
return result != ""
|
||||
}
|
||||
else if (env.CLOUD_PROVIDER == 'AWS') {
|
||||
echo "AWS not supported."
|
||||
return false
|
||||
}
|
||||
} catch (Exception e) {
|
||||
echo "Attempt ${attempt}/${maxAttempts} failed: Error checking image existence: ${e.toString()}"
|
||||
|
||||
if (attempt == maxAttempts) {
|
||||
echo "Max attempts reached. Assuming image does not exist or service is down."
|
||||
return false
|
||||
}
|
||||
|
||||
echo "Retrying in 5 seconds..."
|
||||
sleep 5
|
||||
attempt++
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.homelab.utilities
|
||||
|
||||
def getCommitid(String repo_name) {
|
||||
dir(repo_name) {
|
||||
def gitCmd = env.INFRA_ENV == 'toolchain' ? 'git -c safe.directory="$(pwd)"' : 'git'
|
||||
def commitID = sh(returnStdout: true, script: "${gitCmd} log -1 --format=%h").trim()
|
||||
env.commit_id = sh(returnStdout: true, script: "${gitCmd} log -1 --format=%H").trim()
|
||||
return commitID
|
||||
}
|
||||
}
|
||||
|
||||
def getVersion(String repo_name) {
|
||||
dir(repo_name) {
|
||||
if (fileExists('pom.xml')) {
|
||||
return sh(returnStdout: true, script: 'xq -r .project.version pom.xml').trim()
|
||||
}
|
||||
else if (fileExists('package.json')) {
|
||||
return sh(returnStdout: true, script: 'jq -r .version package.json').trim()
|
||||
}
|
||||
else {
|
||||
return '1.0'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getModules(String repo_name) {
|
||||
dir(repo_name) {
|
||||
if (fileExists('pom.xml')) {
|
||||
modules = sh(returnStdout: true, script: 'xq -r .project.modules.module[] pom.xml 2>/dev/null || xq -r .project.modules.module pom.xml 2>/dev/null || echo empty').trim()
|
||||
if (modules == 'empty' || modules == 'null') {
|
||||
return null
|
||||
}
|
||||
modules = modules.split('\n') as List
|
||||
return modules
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getTag(String repo_name) {
|
||||
def version = getVersion(repo_name)
|
||||
def commitID = getCommitid(repo_name)
|
||||
def date = new Date()
|
||||
def timesha = date.getTime()
|
||||
def tag = "v${version}-${commitID}-${timesha}"
|
||||
if (env.INFRA_ENV == 'toolchain') {
|
||||
tag = "v${version}-${commitID}"
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
def getTagShort(String repo_name) {
|
||||
def version = getVersion(repo_name)
|
||||
def commitID = getCommitid(repo_name)
|
||||
def tagShort = "v${version}-${commitID}"
|
||||
|
||||
return tagShort
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.homelab.utilities
|
||||
|
||||
def getParam(String wd, String fileName = 'config.yaml') {
|
||||
dir(wd) {
|
||||
def config = readYaml file: fileName
|
||||
return config
|
||||
}
|
||||
}
|
||||
//read as string
|
||||
def getParamAsString(String wd, String fileName = 'config.yaml') {
|
||||
dir(wd) {
|
||||
// Read the entire file content as a string
|
||||
def yamlContent = readFile(file: fileName)
|
||||
return yamlContent
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
package com.homelab.utilities
|
||||
|
||||
def clone(String path, String repo_name, String branch_name) {
|
||||
log.info("Cloning repo - ${repo_name}, branch - ${branch_name} in path - ${path}")
|
||||
dir(path) {
|
||||
try {
|
||||
sh "rm -rf ${repo_name}"
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.info('Repo not present, proceeding to clone the repo.')
|
||||
}
|
||||
try {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
if (branch_name) {
|
||||
sh "git clone -b ${branch_name} https://github.com/Homelab/${repo_name}.git"
|
||||
}
|
||||
else {
|
||||
sh "git clone https://github.com/Homelab/${repo_name}.git"
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg += "\n\nFAILED -\n ```Unable to clone repo - ${repo_name}, branch - ${branch_name} in path - ${path}.\n Full Erroror Details - ${e}```"
|
||||
env.error_msg_to_db += "Unable to Clone Repo ${repo_name};"
|
||||
log.error("${env.msg}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def branchCheckOut(String path, String branch_name) {
|
||||
log.info("Checking out to branch - ${branch_name} in path - ${path}")
|
||||
dir(path) {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
sh 'git fetch'
|
||||
try {
|
||||
sh "git checkout ${branch_name}"
|
||||
sh 'git pull --ff-only'
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.info('Creating new branch')
|
||||
sh "git checkout -b ${branch_name}"
|
||||
}
|
||||
sh 'git branch'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def add(String path, String git_add_file) {
|
||||
log.info("Adding files - ${git_add_file} to git, in path - ${path}")
|
||||
try {
|
||||
dir(path) {
|
||||
sh "git add ${git_add_file}"
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg += "\n\nFAILED -\n ```Unable to add files - ${git_add_file} to git, in path - ${path}.\n Full Erroror Details - ${e}```"
|
||||
env.error_msg_to_db += 'Unable to Add Files;'
|
||||
log.error("${env.msg}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def createTag(path, git_tag, tag_message) {
|
||||
log.info("Creating tag - ${git_tag} in git, in path - ${path}")
|
||||
try {
|
||||
dir(path) {
|
||||
sh "git tag ${git_tag} -m '${tag_message}'"
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg += "\n\nFAILED -\n ```Unable to create tag - ${git_tag} in git, in path - ${path}.\n Full Erroror Details - ${e}```"
|
||||
env.error_msg_to_db += "Unable to Create Tag ${git_tag};"
|
||||
log.error("${env.msg}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def codeCommit(String path, String branch_name, String commit_message) {
|
||||
log.info("Commiting code with message - ${commit_message} in git, in path - ${path}")
|
||||
def global_arg = '--global'
|
||||
def gitName = 'svc-devops-homelab'
|
||||
def gitEmail = 'devops@homelab.com'
|
||||
def gitEnv = [
|
||||
"GIT_AUTHOR_NAME=${gitName}",
|
||||
"GIT_AUTHOR_EMAIL=${gitEmail}",
|
||||
"GIT_COMMITTER_NAME=${gitName}",
|
||||
"GIT_COMMITTER_EMAIL=${gitEmail}",
|
||||
]
|
||||
dir(path) {
|
||||
withEnv(gitEnv) {
|
||||
if (sh(returnStatus: true, script: "git diff-index --quiet ${branch_name} 2>/dev/null")) {
|
||||
try {
|
||||
sh "git commit -m '${commit_message}'"
|
||||
return 0
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg += "\n\nFAILED -\n ```Unable to commit code with message - ${commit_message} to git in path - ${path}.\n Full Erroror Details - ${e}```"
|
||||
env.error_msg_to_db += 'Unable to Commit;'
|
||||
log.error("${env.msg}")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.info("No changes were made in branch - ${branch_name}")
|
||||
return 1
|
||||
// log.error("${env.msg}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def codePush(String path, String branch_name) {
|
||||
log.info("Pushing code in branch - ${branch_name} in path - ${path}")
|
||||
try {
|
||||
dir(path) {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
try {
|
||||
sh "git push -f --set-upstream origin ${branch_name}"
|
||||
}
|
||||
catch (Exception e) {
|
||||
sh 'git pull --ff-only'
|
||||
sh 'git push -f'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg += "\n\nFAILED -\n ```Unable to push code in branch - ${branch_name} in path - ${path}.\n Full Erroror Details - ${e}```"
|
||||
env.error_msg_to_db += "Unable to push code in branch - ${branch_name};"
|
||||
log.error("${env.msg}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def tagPush(String path) {
|
||||
log.info("Pushing tag in path - ${path}")
|
||||
try {
|
||||
dir(path) {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
sh 'git push --tags'
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg += "\n\nFAILED -\n ```Unable to push tag from path - ${path}.\n Full Erroror Details - ${e}```"
|
||||
env.error_msg_to_db += 'Unable to Push tag;'
|
||||
log.error("${env.msg}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def createPR(String app_name, String repo_name, String base_branch, String target_branch, String pr_message) {
|
||||
log.info("Creating PR with message - ${pr_message} in Github for repo ${repo_name}")
|
||||
def body = "{\"title\":\"${app_name} ${base_branch} Onboarding\",\"body\":\"${pr_message}\",\"head\":\"${target_branch}\",\"base\":\"${base_branch}\"}"
|
||||
def pr_num = 'empty'
|
||||
|
||||
try {
|
||||
withCredentials([usernamePassword(credentialsId: "${env.GITHUB_CRED}", usernameVariable:'user', passwordVariable: 'token')]) {
|
||||
create_pr = httpRequest httpMode: 'POST',
|
||||
customHeaders: [
|
||||
[name: 'Accept', value: 'application/vnd.github+json'],
|
||||
[maskValue: true, name: 'Authorization', value: 'Bearer ' + token]
|
||||
],
|
||||
requestBody: body,
|
||||
url: "https://api.github.com/repos/Homelab/${repo_name}/pulls",
|
||||
validResponseCodes: '201',
|
||||
timeout: 10
|
||||
def create_pr_json = readJSON(text: create_pr.content)
|
||||
try {
|
||||
error_filter = create_pr_json.errors.message[0]
|
||||
}
|
||||
catch (Exception e) {
|
||||
error_filter = 'empty'
|
||||
}
|
||||
if ( error_filter.contains('No commits between') ) {
|
||||
log.info("No changes were made in branch - ${target_branch}. Skipping - PR Creation...\n${create_pr_json}")
|
||||
}
|
||||
else if ( error_filter.contains('A pull request already exists') ) {
|
||||
log.info(error_filter)
|
||||
}
|
||||
else {
|
||||
pr_num = create_pr_json.number
|
||||
pr_url = create_pr_json.url
|
||||
log.info("PR Number - ${pr_num} and URL - ${pr_url}")
|
||||
}
|
||||
return pr_num.toString()
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = "\n\nFAILED -\n Unable to create PR with message - ${pr_message}.\n Full Error Details - ${e}"
|
||||
def error_code = "${e}".split('Status code')[1]
|
||||
error_code = error_code.toString()
|
||||
error_code = error_code.split(' ')
|
||||
error_code = error_code[1]
|
||||
env.error_part_msg_to_db = "Unable to Create PR for base branch - ${base_branch}, target branch - ${target_branch}, Repo Name - ${repo_name}. Failed with status code ${error_code};" //Akshay has asked to remove it
|
||||
log.error("${env.msg}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
def mergePR(String repo_name, String pr_num, String target_branch) {
|
||||
log.info("Merging PR ${pr_num} in repo - ${repo_name}")
|
||||
if ( "${pr_num}" == 'empty' || pr_num == null) {
|
||||
log.info("No changes were made in branch - ${target_branch}. Skipping - PR Merge...")
|
||||
}
|
||||
else {
|
||||
try {
|
||||
withCredentials([usernamePassword(credentialsId: "${env.GITHUB_CRED}", usernameVariable:'user', passwordVariable: 'token')]) {
|
||||
merge_pr = httpRequest httpMode: 'PUT',
|
||||
customHeaders: [
|
||||
[name: 'Accept', value: 'application/vnd.github+json'],
|
||||
[maskValue: true, name: 'Authorization', value: 'Bearer ' + token]
|
||||
],
|
||||
url: "https://api.github.com/repos/Homelab/${repo_name}/pulls/${pr_num}/merge",
|
||||
validResponseCodes: '200',
|
||||
timeout: 10
|
||||
|
||||
def merge_pr_json = readJSON(text: merge_pr.content)
|
||||
if ( merge_pr_json.message.contains('not mergeable') ) {
|
||||
env.msg += "\n\nFAILED -\n Unable to merge PR - ${pr_num}. PR URL - https://github.com/Homelab/${repo_name}/pulls/${pr_num}. ${merge_pr_json.message}"
|
||||
log.error("${env.msg}")
|
||||
error("${env.msg}")
|
||||
}
|
||||
else {
|
||||
log.info("PR Merge was SUCCESSFUL. Message - ${merge_pr_json.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = "\n\nFAILED -\n Unable to merge PR - ${pr_num} \n Full Error Details - ${e}"
|
||||
def error_code = "${e}".split('Status code')[1]
|
||||
error_code = error_code.toString()
|
||||
error_code = error_code.split(' ')
|
||||
error_code = error_code[1]
|
||||
env.error_part_msg_to_db = "Unable to Merge PR - ${pr_num}, Repo Name - ${repo_name} status code ${error_code};" //Akshay has asked to remove it
|
||||
log.error("${env.msg}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def deleteBranch(String path, String base_branch, String target_branch) {
|
||||
log.info("Deleting branch - ${target_branch}")
|
||||
dir(path) {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
sh 'git fetch'
|
||||
try {
|
||||
sh "git checkout ${base_branch}"
|
||||
sh "git branch -d ${target_branch}"
|
||||
sh "git push -d origin ${target_branch}"
|
||||
}
|
||||
catch (Exception e) {
|
||||
env.msg = "\n\nFAILED -\n Unable to delete branch - ${target_branch}.\n Full Error Details - ${e}"
|
||||
env.error_msg_to_db = "Unable to Delete Branch ${target_branch};"
|
||||
log.error("${env.msg}")
|
||||
throw e
|
||||
}
|
||||
sh 'git branch'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def preDeleteBranch(String path, String base_branch, String target_branch) {
|
||||
log.info("Pre Deleting branch - ${target_branch}")
|
||||
dir(path) {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
try {
|
||||
sh 'git fetch'
|
||||
sh "git checkout ${base_branch}"
|
||||
sh "git push -d origin ${target_branch}"
|
||||
}
|
||||
catch (Exception e) {
|
||||
echo "Pre Delete Branch - Unable to delete branch ${target_branch}"
|
||||
}
|
||||
sh 'git branch'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def fetchDiffFilesForPullRequest(String path , String target_branch){
|
||||
log.info("check diff from target branch - ${target_branch}")
|
||||
dir(path) {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
try {
|
||||
sh "git fetch origin ${target_branch}:${target_branch}"
|
||||
sh "git branch"
|
||||
def changedFiles = sh(script: "git diff --name-only HEAD ${target_branch}", returnStdout: true).trim()
|
||||
echo "${changedFiles}"
|
||||
return changedFiles
|
||||
}
|
||||
catch (Exception e) {
|
||||
echo "failed to get the diff files from ${target_branch}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def fetchDiffFilesForPushRequest(String path ){
|
||||
log.info("check diff of current and previous commit for ${path}")
|
||||
dir(path) {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
try {
|
||||
sh "git fetch origin"
|
||||
|
||||
// Get the latest commit hash and the previous commit hash
|
||||
def currentCommit = sh(script: "git rev-parse HEAD", returnStdout: true).trim()
|
||||
def previousCommit = sh(script: "git rev-parse HEAD~1", returnStdout: true).trim()
|
||||
|
||||
// Capture the list of changed files in a Groovy variable
|
||||
def changedFiles = sh(script: "git diff --name-only ${previousCommit} ${currentCommit}", returnStdout: true).trim()
|
||||
|
||||
return changedFiles
|
||||
}
|
||||
catch (Exception e) {
|
||||
echo "failed to get the diff files for ${path}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def fetchLatestCommitId(String path, String branch) {
|
||||
log.info("Checking the latest commit for branch '${branch}' in path '${path}'")
|
||||
dir(path) {
|
||||
withCredentials([gitUsernamePassword(credentialsId: "${env.GITHUB_CRED}", gitToolName: 'git-tool')]) {
|
||||
try {
|
||||
// Fetch the latest changes for the specific branch
|
||||
sh "git fetch origin ${branch}:${branch}"
|
||||
|
||||
// Check out the specified branch
|
||||
sh "git checkout ${branch}"
|
||||
|
||||
// Get the latest commit hash for the branch
|
||||
def currentCommit = sh(script: "git rev-parse ${branch}", returnStdout: true).trim()
|
||||
|
||||
return currentCommit
|
||||
} catch (Exception e) {
|
||||
echo "Failed to get the latest commit for branch '${branch}' in path '${path}': ${e.message}"
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.homelab.utilities
|
||||
|
||||
def run(String memory_request, String cpu_request, String priority_v2) {
|
||||
echo 'Code to select node pool based on environment'
|
||||
switch (env.cicd_environment) {
|
||||
case 'prd':
|
||||
echo 'Code to select node pool based on memory and cpu request in prd'
|
||||
def mem_req_part = memory_request
|
||||
def cpu_req_part = cpu_request
|
||||
def priority = priority_v2
|
||||
echo "mem_req_part is ${mem_req_part}"
|
||||
echo "cpu_req_part is ${cpu_req_part}"
|
||||
if ( mem_req_part.contains('M') ) {
|
||||
mem_req = mem_req_part.replaceAll('Mi', '')
|
||||
mem_req = mem_req.replaceAll('M', '')
|
||||
try {
|
||||
mem_req = mem_req.toDouble()
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
mem_req = mem_req.toDouble()
|
||||
//mem_req = mem_req.toInteger()
|
||||
}
|
||||
}
|
||||
else if ( mem_req_part.contains('G') ) {
|
||||
mem_req = mem_req_part.replaceAll('Gi', '')
|
||||
mem_req = mem_req.replaceAll('G', '')
|
||||
try {
|
||||
mem_req = mem_req.toDouble() * 1024
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
mem_req = mem_req.toDouble() * 1024
|
||||
//mem_req = mem_req.toInteger()
|
||||
}
|
||||
}
|
||||
echo "memory_request is ${memory_request} ${mem_req}"
|
||||
|
||||
if ( cpu_req_part.contains('m') ) {
|
||||
cpu_req = cpu_req_part.replaceAll('m', '')
|
||||
try {
|
||||
cpu_req = cpu_req.toDouble()
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
cpu_req = cpu_req.toDouble()
|
||||
//cpu_req = cpu_req.toInteger()
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
cpu_req = cpu_req_part.toDouble() * 1000
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
cpu_req = cpu_req_part.toDouble() * 1000
|
||||
//cpu_req = cpu_req.toInteger()
|
||||
}
|
||||
}
|
||||
echo "cpu_request is ${cpu_request} ${cpu_req}"
|
||||
|
||||
def ratio = (mem_req / cpu_req).toDouble()
|
||||
if ( cpu_req > mem_req ) {
|
||||
ratio = 2
|
||||
}
|
||||
//ratio = ratio.toInteger()
|
||||
echo "Ratio - ${ratio}"
|
||||
if(priority.equalsIgnoreCase('cp1')||priority.equalsIgnoreCase('cp2')||priority.equalsIgnoreCase('cp3')||priority.equalsIgnoreCase('up1')||priority.equalsIgnoreCase('up2')||priority.equalsIgnoreCase('up3')||priority.equalsIgnoreCase('sp1')||priority.equalsIgnoreCase('sp2')||priority.equalsIgnoreCase('sp3')){
|
||||
low_priority="lite"
|
||||
if ( ratio >= 2.5 ) {
|
||||
ratiovalue = "tetra"
|
||||
}
|
||||
else {
|
||||
ratiovalue = "duo"
|
||||
}
|
||||
if ( cpu_req >=2200 ) {
|
||||
nodename = "sumo"
|
||||
}
|
||||
else{
|
||||
nodename= "mega"
|
||||
}
|
||||
nodeSelectorvalue = "${nodename}${ratiovalue}${low_priority}"
|
||||
println nodeSelectorvalue
|
||||
break
|
||||
}
|
||||
else{
|
||||
if ( ratio > 5.5 ) {
|
||||
ratiovalue = 'octa'
|
||||
}
|
||||
else if ( ratio >= 2.5 ) {
|
||||
ratiovalue = 'tetra'
|
||||
}
|
||||
else {
|
||||
ratiovalue = 'duo'
|
||||
}
|
||||
|
||||
if ( cpu_req >= 2200) {
|
||||
nodevalue = 'sumo'
|
||||
}
|
||||
else if ( cpu_req < 2200 && cpu_req >= 1000) {
|
||||
nodevalue = 'mega'
|
||||
}
|
||||
else {
|
||||
nodevalue = 'compact'
|
||||
}
|
||||
|
||||
switch ( env.BU?.toLowerCase() ) {
|
||||
case 'supply':
|
||||
if (nodevalue == 'sumo' && (ratiovalue == 'hexa' || ratiovalue == 'octa')){
|
||||
ratiovalue = 'tetra'
|
||||
}
|
||||
else if (nodevalue == 'mega' && (ratiovalue == 'quad' || ratiovalue == 'octa')){
|
||||
ratiovalue = 'tetra'
|
||||
}
|
||||
else if (nodevalue == 'compact' && ratiovalue == 'trio'){
|
||||
ratiovalue = 'tetra'
|
||||
}
|
||||
break
|
||||
case 'demand':
|
||||
if (nodevalue == 'mega' && ratiovalue == 'quad'){
|
||||
ratiovalue = 'tetra'
|
||||
}
|
||||
else if (nodevalue == 'compact' && (ratiovalue == 'octa' || ratiovalue == 'trio')){
|
||||
ratiovalue = 'tetra'
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
nodeSelectorvalue = "${nodevalue}${ratiovalue}"
|
||||
break
|
||||
|
||||
}
|
||||
case 'int':
|
||||
echo 'Shared node pool for int/pre-prod'
|
||||
nodeSelectorvalue = "preprod-cost-optimized"
|
||||
break
|
||||
case ['dev', 'ftr', 'stg']:
|
||||
echo 'Shared node pool for dev and ftr'
|
||||
nodeSelectorvalue = "${env.BU}-shared"
|
||||
break
|
||||
default:
|
||||
log.error('Unable to fetch environment')
|
||||
}
|
||||
return nodeSelectorvalue
|
||||
}
|
||||
Reference in New Issue
Block a user