|
|
@@ -6,8 +6,9 @@
|
|
|
*
|
|
|
* Harbor (153.68): when PUSH_TO_HARBOR=true, push e.g.
|
|
|
* 39.105.153.68/alien_cloud/gateway:uat-latest
|
|
|
- * Before push: existing uat-latest is archived as uat-build-<BUILD_NUMBER> (same digest).
|
|
|
- * New image is pushed only as uat-latest. Prod promote: SOURCE_TAG=uat-latest.
|
|
|
+ * Before push: existing uat-latest is archived as uat-build-<BUILD_NUMBER> via Harbor API
|
|
|
+ * (same digest, no docker pull/tag/push). New image is pushed only as uat-latest.
|
|
|
+ * Prod promote: SOURCE_TAG=uat-latest.
|
|
|
*/
|
|
|
|
|
|
/** Normalize GIT_BRANCH: uat-20260202 (not origin/uat-20260202 or refs/heads/...) */
|
|
|
@@ -42,13 +43,52 @@ def filterHarborPushScope(List allServices, String scope) {
|
|
|
error("Unknown HARBOR_PUSH_SCOPE: ${scope}")
|
|
|
}
|
|
|
|
|
|
+/** Archive current uat-latest as uat-build-N via Harbor API (same digest, no layer re-upload). */
|
|
|
+def archiveHarborLatestViaApi(def script, String reg, String proj, String repo, String latestTag, String buildTag) {
|
|
|
+ script.sh """
|
|
|
+ set -e
|
|
|
+ REG='${reg}'
|
|
|
+ PROJ='${proj}'
|
|
|
+ REPO='${repo}'
|
|
|
+ LATEST='${latestTag}'
|
|
|
+ BUILD_TAG='${buildTag}'
|
|
|
+ if ! command -v jq >/dev/null 2>&1; then
|
|
|
+ echo '>>> WARN: jq missing, skip Harbor API archive for '\${REPO}
|
|
|
+ exit 0
|
|
|
+ fi
|
|
|
+ enc_repo=\$(printf '%s' "\${REPO}" | jq -sRr @uri)
|
|
|
+ if ! curl -fsS -u "\${HARBOR_USER}:\${HARBOR_PASS}" \\
|
|
|
+ "http://\${REG}/api/v2.0/projects/\${PROJ}/repositories/\${enc_repo}/artifacts/\${LATEST}" >/dev/null 2>&1; then
|
|
|
+ echo ">>> no prior \${LATEST} for \${REPO}, skip archive"
|
|
|
+ exit 0
|
|
|
+ fi
|
|
|
+ echo ">>> archive previous \${LATEST} -> \${BUILD_TAG} (\${REPO}, Harbor API)"
|
|
|
+ archive_code=\$(curl -sS -o /tmp/harbor_archive_\${REPO}.json -w '%{http_code}' \\
|
|
|
+ -X POST -u "\${HARBOR_USER}:\${HARBOR_PASS}" \\
|
|
|
+ "http://\${REG}/api/v2.0/projects/\${PROJ}/repositories/\${enc_repo}/artifacts/\${LATEST}/tags" \\
|
|
|
+ -H 'Content-Type: application/json' \\
|
|
|
+ -d "{\\"name\\":\\"\${BUILD_TAG}\\"}")
|
|
|
+ case "\${archive_code}" in
|
|
|
+ 201|200)
|
|
|
+ echo ">>> archived \${REPO}:\${BUILD_TAG} (same digest as prior \${LATEST})"
|
|
|
+ ;;
|
|
|
+ 409)
|
|
|
+ echo ">>> WARN: \${REPO}:\${BUILD_TAG} already exists, continue"
|
|
|
+ ;;
|
|
|
+ *)
|
|
|
+ echo ">>> ERROR: Harbor archive \${REPO} HTTP \${archive_code}"
|
|
|
+ cat /tmp/harbor_archive_\${REPO}.json 2>/dev/null || true
|
|
|
+ exit 1
|
|
|
+ ;;
|
|
|
+ esac
|
|
|
+ """
|
|
|
+}
|
|
|
+
|
|
|
def pushOneHarborImage(def script, Map svc, String reg, String proj, String latestTag, String buildTag,
|
|
|
String baseImage, String dockerfile, String workspace) {
|
|
|
def jarName = "${svc.module}-1.0.0.jar"
|
|
|
def imageLatest = "${reg}/${proj}/${svc.repo}:${latestTag}"
|
|
|
- def imageBuild = "${reg}/${proj}/${svc.repo}:${buildTag}"
|
|
|
def withLibFlag = svc.withLib ? 'true' : 'false'
|
|
|
- // Parallel branches share one Docker daemon; serialize docker ops to avoid containerd CreateDiff/lease races.
|
|
|
script.sh """
|
|
|
set -e
|
|
|
test -f ${workspace}/${svc.module}/target/${jarName}
|
|
|
@@ -60,16 +100,16 @@ def pushOneHarborImage(def script, Map svc, String reg, String proj, String late
|
|
|
else
|
|
|
touch .jenkins_docker_ctx/lib/.keep
|
|
|
fi
|
|
|
+ """
|
|
|
+ // Harbor API archive runs outside flock (HTTP only, safe to parallelize).
|
|
|
+ archiveHarborLatestViaApi(script, reg, proj, svc.repo, latestTag, buildTag)
|
|
|
+ // Parallel branches share one Docker daemon; serialize docker build/push to avoid containerd races.
|
|
|
+ script.sh """
|
|
|
+ set -e
|
|
|
DOCKER_LOCK=/tmp/jenkins-alien-cloud-docker.lock
|
|
|
flock "\${DOCKER_LOCK}" sh -c '
|
|
|
set -e
|
|
|
cd ${workspace}/${svc.module}/.jenkins_docker_ctx
|
|
|
- if docker pull ${imageLatest} 2>/dev/null; then
|
|
|
- echo ">>> archive previous ${latestTag} -> ${buildTag}"
|
|
|
- docker tag ${imageLatest} ${imageBuild}
|
|
|
- docker push ${imageBuild}
|
|
|
- docker rmi ${imageBuild} ${imageLatest} 2>/dev/null || true
|
|
|
- fi
|
|
|
build_ok=0
|
|
|
for attempt in 1 2 3; do
|
|
|
if docker build -f ${workspace}/${dockerfile} \\
|
|
|
@@ -228,7 +268,7 @@ pipeline {
|
|
|
booleanParam(
|
|
|
name: 'HARBOR_PUSH_PARALLEL',
|
|
|
defaultValue: true,
|
|
|
- description: 'Parallel per service (context prep); docker build/push serialized via flock on agent'
|
|
|
+ description: 'Parallel per service (context prep + Harbor API archive); docker build/push serialized via flock'
|
|
|
)
|
|
|
}
|
|
|
|