You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
be-optimumoh/Jenkinsfile

230 lines
9.4 KiB
Groovy

pipeline {
agent any
environment {
DOCKER_HUB_USERNAME = 'aimodocker'
// This creates DOCKER_AUTH_USR and DOCKER_AUTH_PSW
DOCKER_AUTH = credentials('aimodocker')
IMAGE_NAME = 'oh-service'
SERVICE_NAME = 'be-optimumoh'
SECURITY_PREFIX = 'security'
VAULT_ADDR = 'http://127.0.0.1:8200'
VAULT_KV_PATH = 'secret/data/Ep=X1qdWqzh+H&x&dRmV'
VAULT_ENV_FILE = 'vault.env'
TEMP_DOCKERFILE = 'Dockerfile.ci'
// Initialize variables to be updated in script blocks
GIT_COMMIT_HASH = ""
IMAGE_TAG = ""
SECONDARY_TAG = ""
}
stages {
stage('Checkout & Setup') {
steps {
script {
checkout scm
GIT_COMMIT_HASH = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
// Use env.BRANCH_NAME or logic to handle detached HEAD if necessary
def branch = env.BRANCH_NAME ?: 'unknown'
echo "Current Branch: ${branch}"
if (branch == 'main') {
IMAGE_TAG = GIT_COMMIT_HASH
SECONDARY_TAG = 'latest'
} else if (branch == 'oh_security') {
IMAGE_TAG = "${SECURITY_PREFIX}-${GIT_COMMIT_HASH}"
SECONDARY_TAG = "${SECURITY_PREFIX}-latest"
} else {
IMAGE_TAG = "temp-${GIT_COMMIT_HASH}"
SECONDARY_TAG = "" // Ensure it's empty for other branches
}
echo "Primary Tag: ${IMAGE_TAG}"
}
}
}
stage('Docker Login') {
steps {
// Fixed variable names based on the 'DOCKER_AUTH' environment key
sh "echo ${DOCKER_AUTH_PSW} | docker login -u ${DOCKER_AUTH_USR} --password-stdin"
}
}
stage('Setup Vault SSH Tunnel') {
steps {
sshagent(credentials: ['vault-server-ssh-key']) {
sh """#!/bin/bash
pkill -f 'ssh.*8200:127.0.0.1:8200' || true
sleep 1
ssh -fN \
-L 8200:127.0.0.1:8200 \
-o StrictHostKeyChecking=no \
-o ServerAliveInterval=30 \
-o ExitOnForwardFailure=yes \
aimo@192.168.1.105
for i in \$(seq 1 10); do
if curl -sf --connect-timeout 2 'http://127.0.0.1:8200/v1/sys/health' > /dev/null 2>&1; then
echo "✓ Vault tunnel is ready"
exit 0
fi
echo "Waiting... (\$i/10)"
sleep 2
done
exit 1
"""
}
}
}
stage('Fetch Runtime Env from Vault') {
steps {
script {
withCredentials([
string(credentialsId: 'vault-approle-role-id', variable: 'CI_VAULT_ROLE_ID'),
string(credentialsId: 'vault-approle-secret-id', variable: 'CI_VAULT_SECRET_ID')
]) {
def roleId = env.CI_VAULT_ROLE_ID
def secretId = env.CI_VAULT_SECRET_ID
def loginPayload = """{"role_id":"${roleId}","secret_id":"${secretId}"}"""
def loginResponse = sh(
script: '''curl -sS --fail --connect-timeout 10 \
--request POST \
--header 'Content-Type: application/json' \
--data "{\\"role_id\\":\\"${CI_VAULT_ROLE_ID}\\",\\"secret_id\\":\\"${CI_VAULT_SECRET_ID}\\"}" \
'http://127.0.0.1:8200/v1/auth/approle/login' ''',
returnStdout: true
).trim()
def vaultToken = sh(
script: "echo '${loginResponse}' | python3 -c \"import json,sys; print(json.load(sys.stdin)['auth']['client_token'])\"",
returnStdout: true
).trim()
def secretResponse = sh(
script: """curl -sS --fail --connect-timeout 10 \
--header "X-Vault-Token: ${vaultToken}" \
'${VAULT_ADDR}/v1/${VAULT_KV_PATH}'""",
returnStdout: true
).trim()
def pythonScript = '''import json, os
with open('/tmp/vault_response.json') as f:
payload = json.load(f)
data = payload.get("data", {}).get("data", {})
if not data:
raise SystemExit("ERROR: Vault secret is empty")
def render_env_line(key, value):
if value is None:
return key + "="
text = str(value)
has_special = text == "" or " " in text or "#" in text or "\\t" in text
if has_special:
escaped = text.replace('"', '\\\\"')
return key + "=" + '"' + escaped + '"'
return key + "=" + text
workspace = os.environ.get("WORKSPACE", ".")
with open(workspace + "/vault.env", "w") as f:
for key in sorted(data.keys()):
f.write(render_env_line(key, data[key]) + "\\n")
print("Generated vault.env with " + str(len(data)) + " keys")
'''
writeFile(file: '/tmp/vault_response.json', text: secretResponse)
writeFile(file: '/tmp/process_vault.py', text: pythonScript)
sh "python3 /tmp/process_vault.py"
sh "chmod 600 vault.env"
sh "echo '=== Vault.env content ===' && cat vault.env | head -20"
echo "✓ vault.env ready"
}
}
}
}
stage('Build & Tag') {
steps {
script {
def fullImageName = "${DOCKER_HUB_USERNAME}/${IMAGE_NAME}"
// Build image TANPA vault.env - image tetap bersih
sh "docker build -t ${fullImageName}:${IMAGE_TAG} ."
if (SECONDARY_TAG) {
sh "docker tag ${fullImageName}:${IMAGE_TAG} ${fullImageName}:${SECONDARY_TAG}"
}
}
}
}
stage('Push to Docker Hub') {
steps {
script {
def fullImageName = "${DOCKER_HUB_USERNAME}/${IMAGE_NAME}"
sh "docker push ${fullImageName}:${IMAGE_TAG}"
if (SECONDARY_TAG) {
sh "docker push ${fullImageName}:${SECONDARY_TAG}"
}
}
}
}
stage('Deploy to Server') {
steps {
sshagent(credentials: ['vault-server-ssh-key']) {
sh """#!/bin/bash
# Transfer vault.env ke server deployment via SSH
# File disimpan di /tmp — tidak persistent
echo "=== SCP transferring env file ==="
scp -o StrictHostKeyChecking=no \
vault.env \
aimo@192.168.1.105:/tmp/${SERVICE_NAME}.env
# Set permission ketat
ssh -o StrictHostKeyChecking=no aimo@192.168.1.105 \
'chmod 600 /tmp/${SERVICE_NAME}.env && \
echo "=== Env file transferred ===" && \
ls -lh /tmp/${SERVICE_NAME}.env'
# Jalankan deploy dengan env-file dari /tmp
ssh -o StrictHostKeyChecking=no aimo@192.168.1.105 \
'cd ~ && \
echo "=== Checking env file ===" && \
ls -lh /tmp/${SERVICE_NAME}.env && \
echo "=== lines of env file ===" && \
cat /tmp/${SERVICE_NAME}.env && \
cp /tmp/${SERVICE_NAME}.env .env && \
echo "=== Copied to .env for docker compose ===" && \
docker compose pull ${SERVICE_NAME} && \
docker rm -f ${SERVICE_NAME} || true && \
docker compose -p digitaltwin up -d --no-deps ${SERVICE_NAME} && \
rm -f /tmp/${SERVICE_NAME}.env && \
echo "✓ ${SERVICE_NAME} deployed, env file removed"'
"""
}
}
}
}
post {
always {
script {
// Hapus vault.env dari Jenkins workspace
sh 'rm -f vault.env /tmp/vault_response.json /tmp/process_vault.py || true'
sh 'pkill -f "ssh.*8200:127.0.0.1:8200" || true'
sh 'docker logout || true'
def fullImageName = "${DOCKER_HUB_USERNAME}/${IMAGE_NAME}"
sh "docker rmi ${fullImageName}:${IMAGE_TAG} || true"
if (SECONDARY_TAG) {
sh "docker rmi ${fullImageName}:${SECONDARY_TAG} || true"
}
}
}
}
}