From ba1903882d39067c726d0674d5c426e8c6b10701 Mon Sep 17 00:00:00 2001 From: JEON MIN JU <96719969+mingmingmon@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:11:10 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20Jenkins=20Config=20YAML=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EC=95=8C=EB=A6=BC=20=ED=94=8C=EB=9E=AB=ED=8F=BC=20?= =?UTF-8?q?=EC=9C=A0=EB=8F=99=EC=A0=81=20=EC=A7=80=EC=9B=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=99=84=EB=A3=8C=20(#637)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 한관희 --- jenkins/prod/Jenkinsfile | 68 ++++++++++++++++++++++------- jenkins/prod/config.yml | 92 ++++++++++++++++++++++----------------- jenkins/stage/Jenkinsfile | 68 ++++++++++++++++++++++------- jenkins/stage/config.yml | 80 ++++++++++++++++++++-------------- 4 files changed, 207 insertions(+), 101 deletions(-) diff --git a/jenkins/prod/Jenkinsfile b/jenkins/prod/Jenkinsfile index 9f0513946..8d273d9e1 100644 --- a/jenkins/prod/Jenkinsfile +++ b/jenkins/prod/Jenkinsfile @@ -140,31 +140,52 @@ pipeline { } post { - failure { + success { script { - sendSlackBuildNotification(":scream_cat: Stage *${FAILED_STAGE}* failed.", env.SLACK_COLOR_FAILURE) + sendBuildNotification(":rocket: Deployment completed successfully", env.NOTIFICATION_COLOR_SUCCESS) } } - - success { + failure { script { - sendSlackBuildNotification(":rocket: Deployment completed successfully", env.SLACK_COLOR_SUCCESS) + sendBuildNotification(":scream_cat: Deployment failed in stage *${FAILED_STAGE}*", env.NOTIFICATION_COLOR_FAILURE) } } } } -def sendSlackBuildNotification(String message, String color) { +def sendBuildNotification(String message, String color) { def jobUrl = "${env.JENKINS_DOMAIN}/job/${env.JOB_NAME}" def consoleOutputUrl = "${jobUrl}/${env.BUILD_NUMBER}/console" + def changelog = env.GIT_CHANGELOG + + def notificationPlatforms = readJSON text: env.NOTIFICATION_PLATFORMS_JSON.trim() + + notificationPlatforms.each { notification -> + if (notification.enabled.toBoolean()) { + def platform = notification.platform ? notification.platform.trim().toLowerCase() : "" + echo "Processing notification for platform: '${platform}'" + + def payload = null + switch (platform) { + case 'slack': + payload = createSlackPayload(message, color, jobUrl, consoleOutputUrl, changelog) + break + case 'discord': + payload = createDiscordPayload(message, color, jobUrl, consoleOutputUrl, changelog) + break + default: + echo "Unsupported or undefined notification platform: '${platform}'" + } - def payload = createSlackPayload(message, color, jobUrl, consoleOutputUrl) - def payloadJson = groovy.json.JsonOutput.toJson(payload) - - sendHttpPostRequest(env.SLACK_WEBHOOK_URL, payloadJson) + if (payload != null) { + def payloadJson = groovy.json.JsonOutput.toJson(payload) + sendHttpPostRequest(notification.'webhook-url', payloadJson) + } + } + } } -def createSlackPayload(String message, String color, String jobUrl, String consoleOutputUrl) { +def createSlackPayload(String message, String color, String jobUrl, String consoleOutputUrl, String changelog) { return [ blocks: [ [ @@ -183,7 +204,7 @@ def createSlackPayload(String message, String color, String jobUrl, String conso type: "section", text: [ type: "mrkdwn", - text: "*Change Log:*\n${env.GIT_CHANGELOG}" + text: "*Change Log:*\n${changelog}" ] ], [ @@ -217,6 +238,23 @@ def createSlackPayload(String message, String color, String jobUrl, String conso ] } +def createDiscordPayload(String message, String color, String jobUrl, String consoleOutputUrl, String changelog) { + return [ + embeds: [ + [ + title: message, + color: parseColor(color), + description: "*Change Log:*\n${changelog}\n\n[Job](${jobUrl}) | [Console Output](${consoleOutputUrl})", + ] + ] + ] +} + +def parseColor(String hexColor) { + // Discord requires the color in decimal format + return Integer.parseInt(hexColor.replace("#", ""), 16) +} + def sendHttpPostRequest(String url, String payload) { def CONTENT_TYPE_JSON = 'application/json' def HTTP_POST = 'POST' @@ -233,9 +271,9 @@ def loadEnvironmentVariables(String configFile) { def config = readYaml(file: configFile) env.JENKINS_DOMAIN = config.'jenkins-domain' - env.SLACK_WEBHOOK_URL = config.slack.'webhook-url' - env.SLACK_COLOR_SUCCESS = config.slack.'color-success' - env.SLACK_COLOR_FAILURE = config.slack.'color-failure' + env.NOTIFICATION_COLOR_SUCCESS = config.notifications.common.'color-success' + env.NOTIFICATION_COLOR_FAILURE = config.notifications.common.'color-failure' + env.NOTIFICATION_PLATFORMS_JSON = groovy.json.JsonOutput.toJson(config.notifications.platforms) env.PG_USER = config.postgresql.user env.PG_PASSWORD = config.postgresql.password diff --git a/jenkins/prod/config.yml b/jenkins/prod/config.yml index c476127f5..ca0530808 100644 --- a/jenkins/prod/config.yml +++ b/jenkins/prod/config.yml @@ -1,60 +1,74 @@ -jenkins-domain: "https://jenkins.example.com" # Jenkins domain +jenkins-domain: "https://jenkins.example.com" # Base URL of the Jenkins instance -slack: - webhook-url: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX" # Slack webhook URL - color-success: "#F2C744" # Slack message color for success - color-failure: "#8D1E0E" # Slack message color for failure +notifications: + common: + color-success: "#F2C744" # Common hex color code for success messages across all notification platforms + color-failure: "#8D1E0E" # Common hex color code for failure messages across all notification platforms + + platforms: + - platform: "slack" # Name of the notification platform (e.g., Slack) + enabled: true # Enable (true) or disable (false) notifications for this platform + webhook-url: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX" # Webhook URL for sending messages to Slack + + - platform: "discord" # Name of the notification platform (e.g., Discord) + enabled: true # Enable (true) or disable (false) notifications for this platform + webhook-url: "https://discord.com/api/webhooks/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # Webhook URL for sending messages to Discord + + # Add additional notification platforms below as needed + # Example: + # - platform: "microsoft-teams" + # enabled: true + # webhook-url: "https://outlook.office.com/webhook/..." postgresql: - user: "postgres_user" # PostgreSQL username - password: "postgres_password" # PostgreSQL password - backup-dir: "/var/backups/postgresql" # Directory for PostgreSQL backups + user: "postgres_user" # Username for PostgreSQL + password: "postgres_password" # Password for PostgreSQL + backup-dir: "/var/backups/postgresql" # Directory where PostgreSQL backups are stored dockerhub: - repo: "yourdockerhub/repository" # Docker Hub repository name - user: "dockerhub_user" # Docker Hub username - password: "dockerhub_password" # Docker Hub password + repo: "yourdockerhub/repository" # Docker Hub repository name + user: "dockerhub_user" # Docker Hub username + password: "dockerhub_password" # Docker Hub password external-server: - config-path: "/path/to/external/config" # Path for external server configuration - cloud-path: "/path/to/external/cloud" # Path for external server cloud storage - logs-path: "/path/to/external/logs" # Path for external server logs + config-path: "/path/to/external/config" # Path to external server configuration files + cloud-path: "/path/to/external/cloud" # Path to external server cloud storage + logs-path: "/path/to/external/logs" # Path to external server log files internal-server: - config-path: "/path/to/internal/config" # Path for internal server configuration - cloud-path: "/path/to/internal/cloud" # Path for internal server cloud storage - logs-path: "/path/to/internal/logs" # Path for internal server logs + config-path: "/path/to/internal/config" # Path to internal server configuration files + cloud-path: "/path/to/internal/cloud" # Path to internal server cloud storage + logs-path: "/path/to/internal/logs" # Path to internal server log files containers: - blue: "blue-container" # Blue-Green deployment: Blue container name - green: "green-container" # Blue-Green deployment: Green container name - blue-url: "http://blue-container:8080" # URL for the Blue container environment - green-url: "http://green-container:8080" # URL for the Green container environment - image-name: "application-image" # Docker image name for the application + blue: "blue-container" # Name of the Blue container in Blue-Green deployment + green: "green-container" # Name of the Green container in Blue-Green deployment + blue-url: "http://blue-container:8080" # URL for accessing the Blue container environment + green-url: "http://green-container:8080" # URL for accessing the Green container environment + image-name: "application-image" # Docker image name for the application networks: - application: "application-network" # Docker network for the application - monitoring: "monitoring-network" # Docker network for monitoring + application: "application-network" # Docker network used by the application + monitoring: "monitoring-network" # Docker network used for monitoring services spring: - profile: "default" # Spring profile setting - port-a: 8080 # Application port A - port-b: 8081 # Application port B + profile: "default" # Spring profile setting + port-a: 8080 # Application port A + port-b: 8081 # Application port B admin: - username: "admin" # Backend admin username - password: "admin_password" # Backend admin password + username: "admin" # Username for the backend admin + password: "admin_password" # Password for the backend admin docker: - dockerfile-path: "/jenkins/prod/Dockerfile" # Path to the Dockerfile - nginx-container-name: "nginx" # Nginx container name - postgresql-container-name: "postgresql" # PostgreSQL container name + dockerfile-path: "/jenkins/prod/Dockerfile" # Path to the Dockerfile used for building the application image + nginx-container-name: "nginx" # Name of the Nginx container + postgresql-container-name: "postgresql" # Name of the PostgreSQL container staging: - user: "user" # Staging user name - host: "host" # Staging host name or IP address - backup-dir-path: "/var/backups/postgresql" # Staging Directory Path for PostgreSQL backups - restore-backup-script-path: "/var/restore.sh" # Staging Restore Backup Script Path - ssh-port: 22 # Staging SSH Port - postgresql-user: "postgres_user" # Staging Postgresql User - + user: "user" # Username for accessing the staging environment + host: "host" # Hostname or IP address of the staging server + backup-dir-path: "/var/backups/postgresql" # Directory path on the staging server for PostgreSQL backups + restore-backup-script-path: "/var/restore.sh" # Path to the script on the staging server for restoring backups + ssh-port: 22 # SSH port for accessing the staging server + postgresql-user: "postgres_user" # PostgreSQL username on the staging server diff --git a/jenkins/stage/Jenkinsfile b/jenkins/stage/Jenkinsfile index d22b26d02..77ee2d304 100644 --- a/jenkins/stage/Jenkinsfile +++ b/jenkins/stage/Jenkinsfile @@ -118,31 +118,52 @@ pipeline { } post { - failure { + success { script { - sendSlackBuildNotification(":scream_cat: Stage *${FAILED_STAGE}* failed.", env.SLACK_COLOR_FAILURE) + sendBuildNotification(":rocket: Deployment completed successfully", env.NOTIFICATION_COLOR_SUCCESS) } } - - success { + failure { script { - sendSlackBuildNotification(":rocket: Deployment completed successfully", env.SLACK_COLOR_SUCCESS) + sendBuildNotification(":scream_cat: Deployment failed in stage *${FAILED_STAGE}*", env.NOTIFICATION_COLOR_FAILURE) } } } } -def sendSlackBuildNotification(String message, String color) { +def sendBuildNotification(String message, String color) { def jobUrl = "${env.JENKINS_DOMAIN}/job/${env.JOB_NAME}" def consoleOutputUrl = "${jobUrl}/${env.BUILD_NUMBER}/console" + def changelog = env.GIT_CHANGELOG + + def notificationPlatforms = readJSON text: env.NOTIFICATION_PLATFORMS_JSON.trim() + + notificationPlatforms.each { notification -> + if (notification.enabled.toBoolean()) { + def platform = notification.platform ? notification.platform.trim().toLowerCase() : "" + echo "Processing notification for platform: '${platform}'" + + def payload = null + switch (platform) { + case 'slack': + payload = createSlackPayload(message, color, jobUrl, consoleOutputUrl, changelog) + break + case 'discord': + payload = createDiscordPayload(message, color, jobUrl, consoleOutputUrl, changelog) + break + default: + echo "Unsupported or undefined notification platform: '${platform}'" + } - def payload = createSlackPayload(message, color, jobUrl, consoleOutputUrl) - def payloadJson = groovy.json.JsonOutput.toJson(payload) - - sendHttpPostRequest(env.SLACK_WEBHOOK_URL, payloadJson) + if (payload != null) { + def payloadJson = groovy.json.JsonOutput.toJson(payload) + sendHttpPostRequest(notification.'webhook-url', payloadJson) + } + } + } } -def createSlackPayload(String message, String color, String jobUrl, String consoleOutputUrl) { +def createSlackPayload(String message, String color, String jobUrl, String consoleOutputUrl, String changelog) { return [ blocks: [ [ @@ -161,7 +182,7 @@ def createSlackPayload(String message, String color, String jobUrl, String conso type: "section", text: [ type: "mrkdwn", - text: "*Change Log:*\n${env.GIT_CHANGELOG}" + text: "*Change Log:*\n${changelog}" ] ], [ @@ -195,6 +216,23 @@ def createSlackPayload(String message, String color, String jobUrl, String conso ] } +def createDiscordPayload(String message, String color, String jobUrl, String consoleOutputUrl, String changelog) { + return [ + embeds: [ + [ + title: message, + color: parseColor(color), + description: "*Change Log:*\n${changelog}\n\n[Job](${jobUrl}) | [Console Output](${consoleOutputUrl})", + ] + ] + ] +} + +def parseColor(String hexColor) { + // Discord requires the color in decimal format + return Integer.parseInt(hexColor.replace("#", ""), 16) +} + def sendHttpPostRequest(String url, String payload) { def CONTENT_TYPE_JSON = 'application/json' def HTTP_POST = 'POST' @@ -211,9 +249,9 @@ def loadEnvironmentVariables(String configFile) { def config = readYaml(file: configFile) env.JENKINS_DOMAIN = config.'jenkins-domain' - env.SLACK_WEBHOOK_URL = config.slack.'webhook-url' - env.SLACK_COLOR_SUCCESS = config.slack.'color-success' - env.SLACK_COLOR_FAILURE = config.slack.'color-failure' + env.NOTIFICATION_COLOR_SUCCESS = config.notifications.common.'color-success' + env.NOTIFICATION_COLOR_FAILURE = config.notifications.common.'color-failure' + env.NOTIFICATION_PLATFORMS_JSON = groovy.json.JsonOutput.toJson(config.notifications.platforms) env.PG_USER = config.postgresql.user env.PG_PASSWORD = config.postgresql.password diff --git a/jenkins/stage/config.yml b/jenkins/stage/config.yml index 4ba799d8c..7adf0faf3 100644 --- a/jenkins/stage/config.yml +++ b/jenkins/stage/config.yml @@ -1,50 +1,66 @@ -jenkins-domain: "https://jenkins.example.com" # Jenkins domain +jenkins-domain: "https://jenkins.example.com" # Base URL of the Jenkins instance -slack: - webhook-url: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX" # Slack webhook URL - color-success: "#F2C744" # Slack message color for success - color-failure: "#8D1E0E" # Slack message color for failure +notifications: + common: + color-success: "#F2C744" # Common hex color code for success messages across all notification platforms + color-failure: "#8D1E0E" # Common hex color code for failure messages across all notification platforms + + platforms: + - platform: "slack" # Name of the notification platform (e.g., Slack) + enabled: true # Enable (true) or disable (false) notifications for this platform + webhook-url: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX" # Webhook URL for sending messages to Slack + + - platform: "discord" # Name of the notification platform (e.g., Discord) + enabled: true # Enable (true) or disable (false) notifications for this platform + webhook-url: "https://discord.com/api/webhooks/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # Webhook URL for sending messages to Discord + + # Add additional notification platforms below as needed + # Example: + # - platform: "microsoft-teams" + # enabled: true + # webhook-url: "https://outlook.office.com/webhook/..." postgresql: - user: "postgres_user" # PostgreSQL username - password: "postgres_password" # PostgreSQL password - backup-dir: "/var/backups/postgresql" # Directory for PostgreSQL backups + user: "postgres_user" # Username for PostgreSQL + password: "postgres_password" # Password for PostgreSQL + backup-dir: "/var/backups/postgresql" # Directory where PostgreSQL backups are stored dockerhub: - repo: "yourdockerhub/repository" # Docker Hub repository name - user: "dockerhub_user" # Docker Hub username - password: "dockerhub_password" # Docker Hub password + repo: "yourdockerhub/repository" # Docker Hub repository name + user: "dockerhub_user" # Docker Hub username + password: "dockerhub_password" # Docker Hub password external-server: - config-path: "/path/to/external/config" # Path for external server configuration - cloud-path: "/path/to/external/cloud" # Path for external server cloud storage - logs-path: "/path/to/external/logs" # Path for external server logs + config-path: "/path/to/external/config" # Path to external server configuration files + cloud-path: "/path/to/external/cloud" # Path to external server cloud storage + logs-path: "/path/to/external/logs" # Path to external server log files internal-server: - config-path: "/path/to/internal/config" # Path for internal server configuration - cloud-path: "/path/to/internal/cloud" # Path for internal server cloud storage - logs-path: "/path/to/internal/logs" # Path for internal server logs + config-path: "/path/to/internal/config" # Path to internal server configuration files + cloud-path: "/path/to/internal/cloud" # Path to internal server cloud storage + logs-path: "/path/to/internal/logs" # Path to internal server log files containers: - blue: "blue-container" # Blue-Green deployment: Blue container name - green: "green-container" # Blue-Green deployment: Green container name - blue-url: "http://blue-container:8080" # URL for the Blue container environment - green-url: "http://green-container:8080" # URL for the Green container environment - image-name: "application-image" # Docker image name for the application + blue: "blue-container" # Name of the Blue container in Blue-Green deployment + green: "green-container" # Name of the Green container in Blue-Green deployment + blue-url: "http://blue-container:8080" # URL for accessing the Blue container environment + green-url: "http://green-container:8080" # URL for accessing the Green container environment + image-name: "application-image" # Docker image name for the application networks: - application: "application-network" # Docker network for the application - monitoring: "monitoring-network" # Docker network for monitoring + application: "application-network" # Docker network used by the application + monitoring: "monitoring-network" # Docker network used for monitoring services spring: - profile: "default" # Spring profile setting - port-a: 8080 # Application port A - port-b: 8081 # Application port B + profile: "default" # Spring profile setting + port-a: 8080 # Application port A + port-b: 8081 # Application port B admin: - username: "admin" # Backend admin username - password: "admin_password" # Backend admin password + username: "admin" # Username for the backend admin + password: "admin_password" # Password for the backend admin + docker: - dockerfile-path: "/jenkins/stage/Dockerfile" # Path to the Dockerfile - nginx-container-name: "nginx" # Nginx container name - postgresql-container-name: "postgresql" # PostgreSQL container name + dockerfile-path: "/jenkins/stage/Dockerfile" # Path to the Dockerfile used for building the application image + nginx-container-name: "nginx" # Name of the Nginx container + postgresql-container-name: "postgresql" # Name of the PostgreSQL container