From 0c27facf9c8fd929533721ab34d2acb3bb984a51 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Tue, 4 Dec 2018 11:58:27 +0100 Subject: [PATCH 01/15] Added comments to node cicd Jenkinsfile --- jenkins/node/cicd/Jenkinsfile | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/jenkins/node/cicd/Jenkinsfile b/jenkins/node/cicd/Jenkinsfile index 3931107f5..fef72cf2e 100644 --- a/jenkins/node/cicd/Jenkinsfile +++ b/jenkins/node/cicd/Jenkinsfile @@ -12,22 +12,33 @@ pipeline{ } environment { + // Script for build the application. Defined at package.json buildScript = "compile" + // Script for lint the application. Defined at package.json lintScript = "lint" + // Script for test the application. Defined at package.json testScript = "test" + // Node project directory nodeDir = "node" // sonarQube + // Name of the sonarQube tool sonarTool = 'SonarQube-scanner' + // Name of the sonarQube environment sonarEnv = "SonarQube" // Nexus - artifactId = 'my-thai-star' + // Artifact groupId groupId = 'com.devonfw.mythaistar' + // Nexus repository ID repositoryId = 'devon.snapshots' + // Nexus internal URL repositoryUrl = 'http://nexus3-core:8081/nexus3/repository/snapshots' + // Maven global settings ID mavenGlobalSettings = '9d437f6e-46e7-4a11-a8d1-2f0055f14033' + // JavaJDK tool id javaJdk = 'Java8' + // Maven tool id mavenInstallation = 'Maven3' } From c5a02666f3de7611b8a627c611969c5cbd006fb8 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 09:36:20 +0100 Subject: [PATCH 02/15] Updated deployment Jenkinsfile + Added docker-compose for deployment --- angular/Dockerfile | 8 +-- java/Dockerfile | 2 +- jenkins/angular/cicd/Jenkinsfile | 2 +- jenkins/angular/deployment/Jenkinsfile | 87 ++++++++++++++++++++++++++ jenkins/java/deployment/Jenkinsfile | 82 ++++++++++++++++++++++++ reverse-proxy/Dockerfile | 8 +-- reverse-proxy/backend/Dockerfile | 5 ++ reverse-proxy/docker-compose.ci.yml | 29 +++++++++ reverse-proxy/docker-compose.yml | 29 +++++++++ reverse-proxy/frontend/Dockerfile | 4 ++ reverse-proxy/frontend/nginx.conf | 75 ++++++++++++++++++++++ reverse-proxy/nginx.conf | 38 ++++++++--- 12 files changed, 349 insertions(+), 20 deletions(-) create mode 100644 jenkins/angular/deployment/Jenkinsfile create mode 100644 jenkins/java/deployment/Jenkinsfile create mode 100644 reverse-proxy/backend/Dockerfile create mode 100644 reverse-proxy/docker-compose.ci.yml create mode 100644 reverse-proxy/docker-compose.yml create mode 100644 reverse-proxy/frontend/Dockerfile create mode 100644 reverse-proxy/frontend/nginx.conf diff --git a/angular/Dockerfile b/angular/Dockerfile index 1bc7efabf..c2bdf59c5 100644 --- a/angular/Dockerfile +++ b/angular/Dockerfile @@ -5,14 +5,14 @@ COPY . /app RUN npm i -g @angular/cli RUN apk update && apk add yarn RUN yarn -RUN ng build --configuration=docker --prod --build-optimizer +RUN yarn build --configuration=docker # 2. Deploy FROM nginx:latest RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apache2-utils \ - && rm -rf /var/lib/apt/lists/* + && apt-get install -y --no-install-recommends \ + apache2-utils \ + && rm -rf /var/lib/apt/lists/* COPY nginx.conf /etc/nginx/conf.d/default.conf COPY --from=build /app/dist/. /usr/share/nginx/html EXPOSE 80 443 diff --git a/java/Dockerfile b/java/Dockerfile index 939c06db7..6f10f9b1c 100644 --- a/java/Dockerfile +++ b/java/Dockerfile @@ -1,7 +1,7 @@ # 1. Build FROM maven:3.5-jdk-8-alpine AS build WORKDIR /app -COPY mtsj/. /app +COPY mtsj/ /app RUN ls -l RUN mvn install diff --git a/jenkins/angular/cicd/Jenkinsfile b/jenkins/angular/cicd/Jenkinsfile index d2ccc3694..3b4d536c1 100644 --- a/jenkins/angular/cicd/Jenkinsfile +++ b/jenkins/angular/cicd/Jenkinsfile @@ -13,7 +13,7 @@ pipeline{ environment { // Script for build the application. Defined at package.json - buildScript = 'build:prodcompose' + buildScript = 'build --configuration=docker' // Script for lint the application. Defined at package.json lintScript = 'lint' // Script for test the application. Defined at package.json diff --git a/jenkins/angular/deployment/Jenkinsfile b/jenkins/angular/deployment/Jenkinsfile new file mode 100644 index 000000000..af8a9ad9d --- /dev/null +++ b/jenkins/angular/deployment/Jenkinsfile @@ -0,0 +1,87 @@ +pipeline{ + agent any + + options { + buildDiscarder(logRotator(artifactDaysToKeepStr: '1', artifactNumToKeepStr: '1', daysToKeepStr: '5', numToKeepStr: '50')) + // Disable concurrent builds. It will wait until the pipeline finish before start a new one + disableConcurrentBuilds() + } + + environment { + containerPort = 8090 + sshAgentCredentials = '3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11' + artifactId = 'mythaistar-restaurant' + + nexusApiUrl = 'nexus3-core:8081/nexus3/' + groupId = 'com/devonfw/mythaistar/mythaistar-restaurant/' + repositoryId = 'devon.snapshots' + repositoryUrl = 'nexus3-core:8081/nexus3/repository/releases/' + + repository = 'snapshots' + format = 'maven2' + group = 'com.devonfw.mythaistar' + name = 'mythaistar-restaurant' + extension = 'zip' + } + + parameters { + string(name: 'VERSION', defaultValue: '1.12.0-SNAPSHOT', description: 'Version number') + string(name: 'EXTERNAL_SERVER_IP', defaultValue: '10.40.235.244', description: 'Server IP') + } + + stages { + stage ('Download artifact from Nexus') { + steps { + script { + withCredentials([usernamePassword(credentialsId: 'pl-technical-user', passwordVariable: 'pass', usernameVariable: 'user')]) { + def response = httpRequest """https://${user}:${pass}@devon.s2-eu.capgemini.com/nexus3/service/rest/beta/search/assets?repository=${repository}&format=${format}&group=${group}&name=${name}&maven.groupId=${group}&maven.artifactId=${name}&maven.baseVersion=${params.VERSION}&maven.extension=${extension}""" + def props = readJSON text: response.content + + def num = -1 + def url = '' + props.items.each { + def n = (it.downloadUrl =~ /.*-(\d*)\.zip/)[0][1] + if (n > num) { + num = n + url = it.downloadUrl + } + } + + sh """wget -O ${artifactId}-${params.VERSION}.${extension} ${url.replace('https://','https://'+user+':'+pass+'@')}""" + + sh "mkdir -p dist" + unzip dir: 'dist', zipFile: """${artifactId}-${params.VERSION}.${extension}""" + } + } + } + } + + stage ('Deployment') { + steps { + script { + dir('dist'){ + sshagent (credentials: [sshAgentCredentials]) { + sh """ + # Copy resulting "dist" folder from workspace to deployment server + scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/angular/ + + # Launch application in Docker container + # ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker rm -f mts-angular + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker run -itd --name=mts-angular -p ${containerPort}:80 cbelda/nginx4angular:1.0 + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker exec mts-angular bash -c \\"rm /usr/share/nginx/html/*\\" + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker cp mythaistar/angular/dist/. mts-angular:/usr/share/nginx/html/ + """ + echo "Your app is available in http://${params.EXTERNAL_SERVER_IP}:${containerPort}" + } + } + } + } + } + } + + post { + always { + cleanWs() + } + } +} \ No newline at end of file diff --git a/jenkins/java/deployment/Jenkinsfile b/jenkins/java/deployment/Jenkinsfile new file mode 100644 index 000000000..3678c0208 --- /dev/null +++ b/jenkins/java/deployment/Jenkinsfile @@ -0,0 +1,82 @@ +pipeline{ + agent any + + options { + buildDiscarder(logRotator(artifactDaysToKeepStr: '1', artifactNumToKeepStr: '1', daysToKeepStr: '5', numToKeepStr: '50')) + // Disable concurrent builds. It will wait until the pipeline finish before start a new one + disableConcurrentBuilds() + } + + environment { + containerPort = 9091 + sshAgentCredentials = '3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11' + artifactId = 'mythaistar-restaurant' + + nexusApiUrl = 'nexus3-core:8081/nexus3/' + groupId = 'com/devonfw/mythaistar/mythaistar-restaurant/' + repositoryId = 'devon.snapshots' + repositoryUrl = 'nexus3-core:8081/nexus3/repository/releases/' + nexusCredentialsId = 'pl-technical-user' + classifier = 'bootified' + + repository = 'snapshots' + format = 'maven2' + group = 'com.devonfw.java.mtsj' + name = 'mtsj-server' + extension = 'war' + } + + parameters { + string(name: 'VERSION', defaultValue: '0.1-SNAPSHOT', description: 'Version number') + string(name: 'EXTERNAL_SERVER_IP', defaultValue: '10.40.235.244', description: 'Server IP') + } + + stages { + stage ('Download artifact from Nexus') { + steps { + script { + withCredentials([usernamePassword(credentialsId: nexusCredentialsId, passwordVariable: 'pass', usernameVariable: 'user')]) { + def response = httpRequest """https://${user}:${pass}@devon.s2-eu.capgemini.com/nexus3/service/rest/beta/search/assets?repository=${repository}&format=${format}&group=${group}&name=${name}&maven.groupId=${group}&maven.artifactId=${name}&maven.baseVersion=${params.VERSION}&maven.classifier=${classifier}&maven.extension=${extension}""" + def props = readJSON text: response.content + + def num = -1 + def url = '' + props.items.each { + def n = (it.downloadUrl =~ /.*-(\d*)-bootified\.war/)[0][1] + println n + if (n > num) { + num = n + url = it.downloadUrl + } + } + + sh """wget -O mythaistar.war ${url.replace('https://','https://'+user+':'+pass+'@')}""" + } + } + } + } + + stage('Deployment') { + steps { + sshagent (credentials: ['3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11']) { + sh """ + # Copy resulting ".war" file from workspace to deployment server + scp -o StrictHostKeyChecking=no -r mythaistar.war root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/java/ + + # Launch application in Docker container + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker rm -f mts-java + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker run -itd --name=mts-java -p ${containerPort}:8080 tomcat:latest + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker cp mythaistar/java/mythaistar.war mts-java:/usr/local/tomcat/webapps/ + """ + } + sh 'echo \\"Server available at http://de-mucdevondepl01:9090/mythaistar\\"' + } + } + } + + post { + always { + cleanWs() + } + } +} \ No newline at end of file diff --git a/reverse-proxy/Dockerfile b/reverse-proxy/Dockerfile index c0ee17de7..17db4324b 100644 --- a/reverse-proxy/Dockerfile +++ b/reverse-proxy/Dockerfile @@ -1,9 +1,3 @@ FROM nginx:latest -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apache2-utils \ - && rm -rf /var/lib/apt/lists/* -COPY nginx.conf /etc/nginx/conf.d/default.conf -EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file +COPY nginx.conf /etc/nginx/conf.d/default.conf diff --git a/reverse-proxy/backend/Dockerfile b/reverse-proxy/backend/Dockerfile new file mode 100644 index 000000000..e891e9de2 --- /dev/null +++ b/reverse-proxy/backend/Dockerfile @@ -0,0 +1,5 @@ +FROM java:8 +WORKDIR /app +COPY ./mtsj-server-bootified.war /app/ +ENTRYPOINT ["java","-jar","/app/mtsj-server-bootified.war"] +EXPOSE 8081 \ No newline at end of file diff --git a/reverse-proxy/docker-compose.ci.yml b/reverse-proxy/docker-compose.ci.yml new file mode 100644 index 000000000..a9ea4cb38 --- /dev/null +++ b/reverse-proxy/docker-compose.ci.yml @@ -0,0 +1,29 @@ +version: '3' +services: + web: + build: . + image: my-thai-star/proxy:latest + restart: always + ports: + - '8088:80' + networks: + - my-thai-star + depends_on: + - java + - angular + java: + build: backend/ + image: my-thai-star/java:latest + restart: always + networks: + - my-thai-star + angular: + build: frontend/ + image: my-thai-star/angular:latest + restart: always + networks: + - my-thai-star + +networks: + my-thai-star: + driver: bridge diff --git a/reverse-proxy/docker-compose.yml b/reverse-proxy/docker-compose.yml new file mode 100644 index 000000000..4c87133ef --- /dev/null +++ b/reverse-proxy/docker-compose.yml @@ -0,0 +1,29 @@ +version: '3' +services: + web: + build: . + image: my-thai-star/proxy:latest + restart: always + ports: + - '8088:80' + networks: + - my-thai-star + depends_on: + - java + - angular + java: + build: ../java + image: my-thai-star/java:latest + restart: always + networks: + - my-thai-star + angular: + build: ../angular + image: my-thai-star/angular:latest + restart: always + networks: + - my-thai-star + +networks: + my-thai-star: + driver: bridge diff --git a/reverse-proxy/frontend/Dockerfile b/reverse-proxy/frontend/Dockerfile new file mode 100644 index 000000000..05e541d10 --- /dev/null +++ b/reverse-proxy/frontend/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:latest + +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY ./dist/* /var/www/ \ No newline at end of file diff --git a/reverse-proxy/frontend/nginx.conf b/reverse-proxy/frontend/nginx.conf new file mode 100644 index 000000000..db7a9502d --- /dev/null +++ b/reverse-proxy/frontend/nginx.conf @@ -0,0 +1,75 @@ +# Generated by nginxconfig.io +# https://nginxconfig.io/?domain=_&document_root=%2Ffrontend&https=false&redirect=false&fallback_html&php=false&file_structure=unified&symlink=false&proxy&proxy_pass=http:%2F%2Fjava:8081 + +user www-data; +pid /run/nginx.pid; +worker_processes auto; +worker_rlimit_nofile 65535; + +events { + multi_accept on; + worker_connections 65535; +} + +http { + charset utf-8; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + server_tokens off; + log_not_found off; + types_hash_max_size 2048; + client_max_body_size 16M; + + # MIME + include mime.types; + default_type application/octet-stream; + + # logging + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log warn; + + # load configs + include /etc/nginx/conf.d/*.conf; + + # _ + server { + listen 80; + listen [::]:80; + + server_name _; + root /var/www/; + + # security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always; + + # . files + location ~ /\. { + deny all; + } + + # assets, media + location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ { + expires 7d; + access_log off; + } + + # svg, fonts + location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ { + add_header Access-Control-Allow-Origin "*"; + expires 7d; + access_log off; + } + + # gzip + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml; + } +} diff --git a/reverse-proxy/nginx.conf b/reverse-proxy/nginx.conf index 1bdbfcd58..87bb53374 100644 --- a/reverse-proxy/nginx.conf +++ b/reverse-proxy/nginx.conf @@ -1,14 +1,38 @@ server { - server_name devonfw-reverse-proxy.dev; + server_name _; root /usr/share/nginx/html; - location / { - proxy_pass http://angular:80; - } + # reverse proxy + location / { + proxy_pass http://angular:80; + proxy_http_version 1.1; + proxy_cache_bypass $http_upgrade; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + } - location /api { - proxy_pass http://java:8081/mythaistar; - } + # reverse proxy + location /api { + proxy_pass http://java:8081/mythaistar; + proxy_http_version 1.1; + proxy_cache_bypass $http_upgrade; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + } error_log /var/log/nginx/devonfw_reverseproxy_error.log; access_log /var/log/nginx/devonfw_reverseproxy_access.log; From a19bef81022480e653d684ce2cfc68526bfa412c Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 10:04:50 +0100 Subject: [PATCH 03/15] Updated new deployment --- jenkins/java/deployment/Jenkinsfile | 7 +++---- reverse-proxy/{frontend => angular}/Dockerfile | 0 reverse-proxy/{frontend => angular}/nginx.conf | 0 reverse-proxy/docker-compose.ci.yml | 4 ++-- reverse-proxy/{backend => java}/Dockerfile | 0 5 files changed, 5 insertions(+), 6 deletions(-) rename reverse-proxy/{frontend => angular}/Dockerfile (100%) rename reverse-proxy/{frontend => angular}/nginx.conf (100%) rename reverse-proxy/{backend => java}/Dockerfile (100%) diff --git a/jenkins/java/deployment/Jenkinsfile b/jenkins/java/deployment/Jenkinsfile index 3678c0208..69c83eba3 100644 --- a/jenkins/java/deployment/Jenkinsfile +++ b/jenkins/java/deployment/Jenkinsfile @@ -61,12 +61,11 @@ pipeline{ sshagent (credentials: ['3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11']) { sh """ # Copy resulting ".war" file from workspace to deployment server - scp -o StrictHostKeyChecking=no -r mythaistar.war root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/java/ + scp -o StrictHostKeyChecking=no -r mythaistar.war root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/java/ # Launch application in Docker container - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker rm -f mts-java - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker run -itd --name=mts-java -p ${containerPort}:8080 tomcat:latest - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker cp mythaistar/java/mythaistar.war mts-java:/usr/local/tomcat/webapps/ + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.ci.yml up -d --build web + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.ci.yml up -d --build java """ } sh 'echo \\"Server available at http://de-mucdevondepl01:9090/mythaistar\\"' diff --git a/reverse-proxy/frontend/Dockerfile b/reverse-proxy/angular/Dockerfile similarity index 100% rename from reverse-proxy/frontend/Dockerfile rename to reverse-proxy/angular/Dockerfile diff --git a/reverse-proxy/frontend/nginx.conf b/reverse-proxy/angular/nginx.conf similarity index 100% rename from reverse-proxy/frontend/nginx.conf rename to reverse-proxy/angular/nginx.conf diff --git a/reverse-proxy/docker-compose.ci.yml b/reverse-proxy/docker-compose.ci.yml index a9ea4cb38..63072c05d 100644 --- a/reverse-proxy/docker-compose.ci.yml +++ b/reverse-proxy/docker-compose.ci.yml @@ -12,13 +12,13 @@ services: - java - angular java: - build: backend/ + build: java/ image: my-thai-star/java:latest restart: always networks: - my-thai-star angular: - build: frontend/ + build: angular/ image: my-thai-star/angular:latest restart: always networks: diff --git a/reverse-proxy/backend/Dockerfile b/reverse-proxy/java/Dockerfile similarity index 100% rename from reverse-proxy/backend/Dockerfile rename to reverse-proxy/java/Dockerfile From 61b01b3484e96a2983c8120423f10945fa280dea Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 10:17:16 +0100 Subject: [PATCH 04/15] Updated Angular deployment --- jenkins/angular/deployment/Jenkinsfile | 8 +++----- reverse-proxy/docker-compose.ci.yml | 3 --- reverse-proxy/docker-compose.yml | 3 --- reverse-proxy/java/Dockerfile | 4 ++-- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/jenkins/angular/deployment/Jenkinsfile b/jenkins/angular/deployment/Jenkinsfile index af8a9ad9d..4732ad89d 100644 --- a/jenkins/angular/deployment/Jenkinsfile +++ b/jenkins/angular/deployment/Jenkinsfile @@ -63,13 +63,11 @@ pipeline{ sshagent (credentials: [sshAgentCredentials]) { sh """ # Copy resulting "dist" folder from workspace to deployment server - scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/angular/ + scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/angular/ # Launch application in Docker container - # ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker rm -f mts-angular - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker run -itd --name=mts-angular -p ${containerPort}:80 cbelda/nginx4angular:1.0 - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker exec mts-angular bash -c \\"rm /usr/share/nginx/html/*\\" - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker cp mythaistar/angular/dist/. mts-angular:/usr/share/nginx/html/ + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.ci.yml up -d --build web + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.ci.yml up -d --build angular """ echo "Your app is available in http://${params.EXTERNAL_SERVER_IP}:${containerPort}" } diff --git a/reverse-proxy/docker-compose.ci.yml b/reverse-proxy/docker-compose.ci.yml index 63072c05d..0bb607aa4 100644 --- a/reverse-proxy/docker-compose.ci.yml +++ b/reverse-proxy/docker-compose.ci.yml @@ -8,9 +8,6 @@ services: - '8088:80' networks: - my-thai-star - depends_on: - - java - - angular java: build: java/ image: my-thai-star/java:latest diff --git a/reverse-proxy/docker-compose.yml b/reverse-proxy/docker-compose.yml index 4c87133ef..8ba0f5eb4 100644 --- a/reverse-proxy/docker-compose.yml +++ b/reverse-proxy/docker-compose.yml @@ -8,9 +8,6 @@ services: - '8088:80' networks: - my-thai-star - depends_on: - - java - - angular java: build: ../java image: my-thai-star/java:latest diff --git a/reverse-proxy/java/Dockerfile b/reverse-proxy/java/Dockerfile index e891e9de2..9b8b67d6f 100644 --- a/reverse-proxy/java/Dockerfile +++ b/reverse-proxy/java/Dockerfile @@ -1,5 +1,5 @@ FROM java:8 WORKDIR /app -COPY ./mtsj-server-bootified.war /app/ -ENTRYPOINT ["java","-jar","/app/mtsj-server-bootified.war"] +COPY ./mythaistar.war /app/ +ENTRYPOINT ["java","-jar","/app/mythaistar.war"] EXPOSE 8081 \ No newline at end of file From d6d9d3c3a3a6f668c3ed2c670e1f46f5a686e8d2 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 10:23:20 +0100 Subject: [PATCH 05/15] Updated jenkinsfiles --- jenkins/angular/deployment/Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jenkins/angular/deployment/Jenkinsfile b/jenkins/angular/deployment/Jenkinsfile index 4732ad89d..2462e13ac 100644 --- a/jenkins/angular/deployment/Jenkinsfile +++ b/jenkins/angular/deployment/Jenkinsfile @@ -63,7 +63,8 @@ pipeline{ sshagent (credentials: [sshAgentCredentials]) { sh """ # Copy resulting "dist" folder from workspace to deployment server - scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/angular/ + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} mkdir -p /root/mythaistar/reverse-proxy/angular/dist/ + scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/angular/dist/ # Launch application in Docker container ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.ci.yml up -d --build web From 41330d80deafc33a7ac1a43f05a53a99e15a55e4 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 10:33:47 +0100 Subject: [PATCH 06/15] Updated nginx config --- reverse-proxy/angular/nginx.conf | 5 ----- 1 file changed, 5 deletions(-) diff --git a/reverse-proxy/angular/nginx.conf b/reverse-proxy/angular/nginx.conf index db7a9502d..8c4835de5 100644 --- a/reverse-proxy/angular/nginx.conf +++ b/reverse-proxy/angular/nginx.conf @@ -1,11 +1,6 @@ # Generated by nginxconfig.io # https://nginxconfig.io/?domain=_&document_root=%2Ffrontend&https=false&redirect=false&fallback_html&php=false&file_structure=unified&symlink=false&proxy&proxy_pass=http:%2F%2Fjava:8081 -user www-data; -pid /run/nginx.pid; -worker_processes auto; -worker_rlimit_nofile 65535; - events { multi_accept on; worker_connections 65535; From 8207a49747c2a6b77a76c9fb63186557789eb3e6 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 10:42:38 +0100 Subject: [PATCH 07/15] Updated angular Jenkinsfile --- jenkins/angular/deployment/Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/jenkins/angular/deployment/Jenkinsfile b/jenkins/angular/deployment/Jenkinsfile index 2462e13ac..9fdc5f5f9 100644 --- a/jenkins/angular/deployment/Jenkinsfile +++ b/jenkins/angular/deployment/Jenkinsfile @@ -64,6 +64,7 @@ pipeline{ sh """ # Copy resulting "dist" folder from workspace to deployment server ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} mkdir -p /root/mythaistar/reverse-proxy/angular/dist/ + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} rm -r /root/mythaistar/reverse-proxy/angular/dist/* scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/angular/dist/ # Launch application in Docker container From b6c63769b221f6e4033e843a84703da606a85afa Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 13:59:28 +0100 Subject: [PATCH 08/15] Updated java version number. Updated jenkinsfiles & reverse proxy files --- angular/package.json | 2 +- docker-compose.yml | 7 +++++-- java/mtsj/api/pom.xml | 11 ++++++----- java/mtsj/batch/pom.xml | 6 +++--- java/mtsj/core/pom.xml | 8 ++++---- java/mtsj/pom.xml | 2 +- java/mtsj/server/pom.xml | 8 ++++---- jenkins/angular/deployment/Jenkinsfile | 17 +++++------------ jenkins/java/deployment/Jenkinsfile | 16 +++++++--------- node/package.json | 4 ++-- reverse-proxy/angular/Dockerfile | 4 ++-- reverse-proxy/angular/nginx.conf | 23 ++--------------------- reverse-proxy/docker-compose.ci.yml | 26 -------------------------- reverse-proxy/docker-compose.yml | 9 ++++++--- 14 files changed, 48 insertions(+), 95 deletions(-) delete mode 100644 reverse-proxy/docker-compose.ci.yml diff --git a/angular/package.json b/angular/package.json index 6f79d4dc6..edd6590ae 100644 --- a/angular/package.json +++ b/angular/package.json @@ -1,6 +1,6 @@ { "name": "mythaistar-restaurant", - "version": "1.12.0", + "version": "1.12.2", "main": "main.js", "scripts": { "postinstall": "npm run postinstall:electron && npx electron-builder install-app-deps", diff --git a/docker-compose.yml b/docker-compose.yml index dd83ebb79..7188d2f65 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,11 @@ -version: '2' +version: '3' services: reverse-proxy: build: 'reverse-proxy/' restart: always container_name: 'mts_reverse_proxy' + image: my-thai-star/reverse-proxy:latest networks: - my-thai-star ports: @@ -13,14 +14,16 @@ services: build: 'angular/' restart: always container_name: 'mts_angular' + image: my-thai-star/angular:latest networks: - my-thai-star java: build: 'java/' restart: always container_name: 'mts_java' + image: my-thai-star/java:latest networks: - my-thai-star networks: my-thai-star: - driver: bridge \ No newline at end of file + driver: bridge diff --git a/java/mtsj/api/pom.xml b/java/mtsj/api/pom.xml index b0d6bccea..bae2b3ac8 100644 --- a/java/mtsj/api/pom.xml +++ b/java/mtsj/api/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 com.devonfw.java.mtsj mtsj - 0.1-SNAPSHOT + 1.12.2-SNAPSHOT mtsj-api jar @@ -31,7 +32,7 @@ devon4j-jpa-envers - @@ -40,7 +41,7 @@ com.devonfw.java.modules devon4j-security - + org.hibernate.validator hibernate-validator diff --git a/java/mtsj/batch/pom.xml b/java/mtsj/batch/pom.xml index a6c0d2650..db69fa69c 100644 --- a/java/mtsj/batch/pom.xml +++ b/java/mtsj/batch/pom.xml @@ -1,11 +1,11 @@  - + 4.0.0 com.devonfw.java.mtsj mtsj - 0.1-SNAPSHOT + 1.12.2-SNAPSHOT mtsj-batch jar diff --git a/java/mtsj/core/pom.xml b/java/mtsj/core/pom.xml index 6ca8f74bf..adb840945 100644 --- a/java/mtsj/core/pom.xml +++ b/java/mtsj/core/pom.xml @@ -1,11 +1,11 @@  - + 4.0.0 com.devonfw.java.mtsj mtsj - 0.1-SNAPSHOT + 1.12.2-SNAPSHOT mtsj-core jar @@ -15,7 +15,7 @@ 1.8 h2 - h2mem + h2mem com.h2database h2 1.4.194 diff --git a/java/mtsj/pom.xml b/java/mtsj/pom.xml index 271a440bf..4467fe9e7 100644 --- a/java/mtsj/pom.xml +++ b/java/mtsj/pom.xml @@ -5,7 +5,7 @@ mtsj com.devonfw.java.mtsj - 0.1-SNAPSHOT + 1.12.2-SNAPSHOT pom ${project.artifactId} Application based on the Devonfw framework for Java (devon4j). diff --git a/java/mtsj/server/pom.xml b/java/mtsj/server/pom.xml index 683aecdb4..5bcd21fc1 100644 --- a/java/mtsj/server/pom.xml +++ b/java/mtsj/server/pom.xml @@ -1,11 +1,11 @@  - + 4.0.0 com.devonfw.java.mtsj mtsj - 0.1-SNAPSHOT + 1.12.2-SNAPSHOT mtsj-server war @@ -122,7 +122,7 @@ ${server.war.name} - + ${project.basedir}/src/main/resources diff --git a/jenkins/angular/deployment/Jenkinsfile b/jenkins/angular/deployment/Jenkinsfile index 9fdc5f5f9..b055a39ed 100644 --- a/jenkins/angular/deployment/Jenkinsfile +++ b/jenkins/angular/deployment/Jenkinsfile @@ -10,13 +10,8 @@ pipeline{ environment { containerPort = 8090 sshAgentCredentials = '3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11' - artifactId = 'mythaistar-restaurant' - - nexusApiUrl = 'nexus3-core:8081/nexus3/' - groupId = 'com/devonfw/mythaistar/mythaistar-restaurant/' - repositoryId = 'devon.snapshots' - repositoryUrl = 'nexus3-core:8081/nexus3/repository/releases/' + nexusApiUrl = 'devon.s2-eu.capgemini.com/nexus3/' repository = 'snapshots' format = 'maven2' group = 'com.devonfw.mythaistar' @@ -34,7 +29,7 @@ pipeline{ steps { script { withCredentials([usernamePassword(credentialsId: 'pl-technical-user', passwordVariable: 'pass', usernameVariable: 'user')]) { - def response = httpRequest """https://${user}:${pass}@devon.s2-eu.capgemini.com/nexus3/service/rest/beta/search/assets?repository=${repository}&format=${format}&group=${group}&name=${name}&maven.groupId=${group}&maven.artifactId=${name}&maven.baseVersion=${params.VERSION}&maven.extension=${extension}""" + def response = httpRequest """https://${user}:${pass}@${nexusApiUrl}service/rest/beta/search/assets?repository=${repository}&format=${format}&group=${group}&name=${name}&maven.groupId=${group}&maven.artifactId=${name}&maven.baseVersion=${params.VERSION}&maven.extension=${extension}""" def props = readJSON text: response.content def num = -1 @@ -47,10 +42,10 @@ pipeline{ } } - sh """wget -O ${artifactId}-${params.VERSION}.${extension} ${url.replace('https://','https://'+user+':'+pass+'@')}""" + sh """wget -O ${name}-${params.VERSION}.${extension} ${url.replace('https://','https://'+user+':'+pass+'@')}""" sh "mkdir -p dist" - unzip dir: 'dist', zipFile: """${artifactId}-${params.VERSION}.${extension}""" + unzip dir: 'dist', zipFile: """${name}-${params.VERSION}.${extension}""" } } } @@ -68,10 +63,8 @@ pipeline{ scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/angular/dist/ # Launch application in Docker container - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.ci.yml up -d --build web - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.ci.yml up -d --build angular + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.yml up -d --build """ - echo "Your app is available in http://${params.EXTERNAL_SERVER_IP}:${containerPort}" } } } diff --git a/jenkins/java/deployment/Jenkinsfile b/jenkins/java/deployment/Jenkinsfile index 69c83eba3..9092eedd5 100644 --- a/jenkins/java/deployment/Jenkinsfile +++ b/jenkins/java/deployment/Jenkinsfile @@ -8,17 +8,12 @@ pipeline{ } environment { - containerPort = 9091 sshAgentCredentials = '3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11' artifactId = 'mythaistar-restaurant' - nexusApiUrl = 'nexus3-core:8081/nexus3/' - groupId = 'com/devonfw/mythaistar/mythaistar-restaurant/' - repositoryId = 'devon.snapshots' - repositoryUrl = 'nexus3-core:8081/nexus3/repository/releases/' + nexusApiUrl = 'devon.s2-eu.capgemini.com/nexus3/' nexusCredentialsId = 'pl-technical-user' classifier = 'bootified' - repository = 'snapshots' format = 'maven2' group = 'com.devonfw.java.mtsj' @@ -35,10 +30,13 @@ pipeline{ stage ('Download artifact from Nexus') { steps { script { + // Download artifact from nexus3 using the nexus3 beta api withCredentials([usernamePassword(credentialsId: nexusCredentialsId, passwordVariable: 'pass', usernameVariable: 'user')]) { - def response = httpRequest """https://${user}:${pass}@devon.s2-eu.capgemini.com/nexus3/service/rest/beta/search/assets?repository=${repository}&format=${format}&group=${group}&name=${name}&maven.groupId=${group}&maven.artifactId=${name}&maven.baseVersion=${params.VERSION}&maven.classifier=${classifier}&maven.extension=${extension}""" + // Search the list of artifacts + def response = httpRequest """https://${user}:${pass}@${nexusApiUrl}service/rest/beta/search/assets?repository=${repository}&format=${format}&group=${group}&name=${name}&maven.groupId=${group}&maven.artifactId=${name}&maven.baseVersion=${params.VERSION}&maven.classifier=${classifier}&maven.extension=${extension}""" def props = readJSON text: response.content + // Get the last snapshot download url def num = -1 def url = '' props.items.each { @@ -50,6 +48,7 @@ pipeline{ } } + // Download the snapshot sh """wget -O mythaistar.war ${url.replace('https://','https://'+user+':'+pass+'@')}""" } } @@ -64,8 +63,7 @@ pipeline{ scp -o StrictHostKeyChecking=no -r mythaistar.war root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/java/ # Launch application in Docker container - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.ci.yml up -d --build web - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.ci.yml up -d --build java + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.yml up -d --build """ } sh 'echo \\"Server available at http://de-mucdevondepl01:9090/mythaistar\\"' diff --git a/node/package.json b/node/package.json index 3ff2b397c..70cb82c1d 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "my-thai-star", - "version": "0.0.1", + "version": "1.12.2", "description": "server template for Devon4Node", "authors": [ "mmaganam", @@ -97,4 +97,4 @@ "src" ] } -} +} \ No newline at end of file diff --git a/reverse-proxy/angular/Dockerfile b/reverse-proxy/angular/Dockerfile index 05e541d10..0fb2df37a 100644 --- a/reverse-proxy/angular/Dockerfile +++ b/reverse-proxy/angular/Dockerfile @@ -1,4 +1,4 @@ FROM nginx:latest -COPY nginx.conf /etc/nginx/conf.d/default.conf -COPY ./dist/* /var/www/ \ No newline at end of file +COPY nginx.conf /etc/nginx/nginx.conf +COPY ./dist/ /var/www/ \ No newline at end of file diff --git a/reverse-proxy/angular/nginx.conf b/reverse-proxy/angular/nginx.conf index 8c4835de5..8f01a44fd 100644 --- a/reverse-proxy/angular/nginx.conf +++ b/reverse-proxy/angular/nginx.conf @@ -24,9 +24,6 @@ http { access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log warn; - # load configs - include /etc/nginx/conf.d/*.conf; - # _ server { listen 80; @@ -34,6 +31,8 @@ http { server_name _; root /var/www/; + + try_files $uri $uri/ /index.html; # security headers add_header X-Frame-Options "SAMEORIGIN" always; @@ -42,24 +41,6 @@ http { add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always; - # . files - location ~ /\. { - deny all; - } - - # assets, media - location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ { - expires 7d; - access_log off; - } - - # svg, fonts - location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ { - add_header Access-Control-Allow-Origin "*"; - expires 7d; - access_log off; - } - # gzip gzip on; gzip_vary on; diff --git a/reverse-proxy/docker-compose.ci.yml b/reverse-proxy/docker-compose.ci.yml deleted file mode 100644 index 0bb607aa4..000000000 --- a/reverse-proxy/docker-compose.ci.yml +++ /dev/null @@ -1,26 +0,0 @@ -version: '3' -services: - web: - build: . - image: my-thai-star/proxy:latest - restart: always - ports: - - '8088:80' - networks: - - my-thai-star - java: - build: java/ - image: my-thai-star/java:latest - restart: always - networks: - - my-thai-star - angular: - build: angular/ - image: my-thai-star/angular:latest - restart: always - networks: - - my-thai-star - -networks: - my-thai-star: - driver: bridge diff --git a/reverse-proxy/docker-compose.yml b/reverse-proxy/docker-compose.yml index 8ba0f5eb4..788427cc2 100644 --- a/reverse-proxy/docker-compose.yml +++ b/reverse-proxy/docker-compose.yml @@ -2,20 +2,23 @@ version: '3' services: web: build: . - image: my-thai-star/proxy:latest + image: my-thai-star/reverse-proxy:latest restart: always ports: - '8088:80' networks: - my-thai-star + depends_on: + - java + - angular java: - build: ../java + build: java/ image: my-thai-star/java:latest restart: always networks: - my-thai-star angular: - build: ../angular + build: angular/ image: my-thai-star/angular:latest restart: always networks: From 6a1e60508fbe0bddcd7ffd54c5e15d9d908061e2 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 14:04:57 +0100 Subject: [PATCH 09/15] Updated jenkinsfiles --- jenkins/angular/deployment/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkins/angular/deployment/Jenkinsfile b/jenkins/angular/deployment/Jenkinsfile index b055a39ed..772dd989d 100644 --- a/jenkins/angular/deployment/Jenkinsfile +++ b/jenkins/angular/deployment/Jenkinsfile @@ -59,7 +59,7 @@ pipeline{ sh """ # Copy resulting "dist" folder from workspace to deployment server ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} mkdir -p /root/mythaistar/reverse-proxy/angular/dist/ - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} rm -r /root/mythaistar/reverse-proxy/angular/dist/* + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} rm -r /root/mythaistar/reverse-proxy/angular/dist/* 2> /dev/null scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/angular/dist/ # Launch application in Docker container From 048ae23393686b2f2fdc5a75c7d5c4915c017d22 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 16:06:53 +0100 Subject: [PATCH 10/15] Updated jenkinsfiles --- jenkins/angular/deployment/Jenkinsfile | 13 ++++++++++++- jenkins/java/deployment/Jenkinsfile | 13 ++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/jenkins/angular/deployment/Jenkinsfile b/jenkins/angular/deployment/Jenkinsfile index 772dd989d..0b10ea194 100644 --- a/jenkins/angular/deployment/Jenkinsfile +++ b/jenkins/angular/deployment/Jenkinsfile @@ -8,14 +8,20 @@ pipeline{ } environment { - containerPort = 8090 + // Jenkins credentials id for ssh agent sshAgentCredentials = '3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11' + // Nexus3 API url nexusApiUrl = 'devon.s2-eu.capgemini.com/nexus3/' + // Maven repository repository = 'snapshots' + // Repository format format = 'maven2' + // Artifact group group = 'com.devonfw.mythaistar' + // Application name name = 'mythaistar-restaurant' + // Artifact extension extension = 'zip' } @@ -28,10 +34,13 @@ pipeline{ stage ('Download artifact from Nexus') { steps { script { + // Download artifact from nexus3 using the nexus3 beta api withCredentials([usernamePassword(credentialsId: 'pl-technical-user', passwordVariable: 'pass', usernameVariable: 'user')]) { + // Search the list of artifacts def response = httpRequest """https://${user}:${pass}@${nexusApiUrl}service/rest/beta/search/assets?repository=${repository}&format=${format}&group=${group}&name=${name}&maven.groupId=${group}&maven.artifactId=${name}&maven.baseVersion=${params.VERSION}&maven.extension=${extension}""" def props = readJSON text: response.content + // Get the last snapshot download url def num = -1 def url = '' props.items.each { @@ -42,8 +51,10 @@ pipeline{ } } + // Download the snapshot sh """wget -O ${name}-${params.VERSION}.${extension} ${url.replace('https://','https://'+user+':'+pass+'@')}""" + // Unzip the angular application sh "mkdir -p dist" unzip dir: 'dist', zipFile: """${name}-${params.VERSION}.${extension}""" } diff --git a/jenkins/java/deployment/Jenkinsfile b/jenkins/java/deployment/Jenkinsfile index 9092eedd5..6ed876bb1 100644 --- a/jenkins/java/deployment/Jenkinsfile +++ b/jenkins/java/deployment/Jenkinsfile @@ -8,16 +8,24 @@ pipeline{ } environment { + // Jenkins credentials id for ssh agent sshAgentCredentials = '3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11' - artifactId = 'mythaistar-restaurant' + // Nexus3 API url nexusApiUrl = 'devon.s2-eu.capgemini.com/nexus3/' + // Nexus3 credentials ID nexusCredentialsId = 'pl-technical-user' + // Maven artifact classifier classifier = 'bootified' + // Maven repository repository = 'snapshots' + // Repository format format = 'maven2' + // Artifact group group = 'com.devonfw.java.mtsj' + // Application name name = 'mtsj-server' + // Artifact extension extension = 'war' } @@ -63,10 +71,9 @@ pipeline{ scp -o StrictHostKeyChecking=no -r mythaistar.war root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/java/ # Launch application in Docker container - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.yml up -d --build + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.yml up -d --build java """ } - sh 'echo \\"Server available at http://de-mucdevondepl01:9090/mythaistar\\"' } } } From cdbb03cb7df83fee7367db11081baa9d162c3d30 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 16:32:08 +0100 Subject: [PATCH 11/15] Added jenkinsfile for full application deployment --- jenkins/angular/deployment/Jenkinsfile | 9 ++-- jenkins/deployment/Jenkinsfile | 60 ++++++++++++++++++++++++++ jenkins/java/deployment/Jenkinsfile | 5 ++- 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 jenkins/deployment/Jenkinsfile diff --git a/jenkins/angular/deployment/Jenkinsfile b/jenkins/angular/deployment/Jenkinsfile index 0b10ea194..895897654 100644 --- a/jenkins/angular/deployment/Jenkinsfile +++ b/jenkins/angular/deployment/Jenkinsfile @@ -28,6 +28,7 @@ pipeline{ parameters { string(name: 'VERSION', defaultValue: '1.12.0-SNAPSHOT', description: 'Version number') string(name: 'EXTERNAL_SERVER_IP', defaultValue: '10.40.235.244', description: 'Server IP') + strint(name: 'APPLICATION_DIR', defaultValue: '/root/mythaistar/reverse-proxy/', description: 'My Thai Star application directory') } stages { @@ -69,12 +70,12 @@ pipeline{ sshagent (credentials: [sshAgentCredentials]) { sh """ # Copy resulting "dist" folder from workspace to deployment server - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} mkdir -p /root/mythaistar/reverse-proxy/angular/dist/ - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} rm -r /root/mythaistar/reverse-proxy/angular/dist/* 2> /dev/null - scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/angular/dist/ + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} mkdir -p ${params.APPLICATION_DIR}angular/dist/ + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} rm -r ${params.APPLICATION_DIR}angular/dist/* 2> /dev/null + scp -o StrictHostKeyChecking=no -r . root@${params.EXTERNAL_SERVER_IP}:${params.APPLICATION_DIR}angular/dist/ # Launch application in Docker container - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.yml up -d --build + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f ${params.APPLICATION_DIR}docker-compose.yml up -d --build """ } } diff --git a/jenkins/deployment/Jenkinsfile b/jenkins/deployment/Jenkinsfile new file mode 100644 index 000000000..5f3812bb7 --- /dev/null +++ b/jenkins/deployment/Jenkinsfile @@ -0,0 +1,60 @@ +pipeline{ + agent any + + options { + buildDiscarder(logRotator(artifactDaysToKeepStr: '1', artifactNumToKeepStr: '1', daysToKeepStr: '5', numToKeepStr: '50')) + // Disable concurrent builds. It will wait until the pipeline finish before start a new one + disableConcurrentBuilds() + } + + environment { + // Jenkins credentials id for ssh agent + sshAgentCredentials = '3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11' + + // Java Deploy Pipeline name + javaDeployPipeline = 'deployment_java' + // Angular Deploy Pipeline name + angularDeployPipeline = 'deployment_angular' + } + + parameters { + string(name: 'JAVA_VERSION', defaultValue: '0.1-SNAPSHOT', description: 'Java Version number') + string(name: 'ANGULAR_VERSION', defaultValue: '1.12.0-SNAPSHOT', description: 'Angulalr Version number') + string(name: 'EXTERNAL_SERVER_IP', defaultValue: '10.40.235.244', description: 'Server IP') + strint(name: 'APPLICATION_DIR', defaultValue: '/root/mythaistar/reverse-proxy/', description: 'My Thai Star application directory') + } + + + stages { + stage ('Copy files to remote server') { + steps { + sshagent (credentials: [sshAgentCredentials]) { + sh """ + mkdir -p ${params.APPLICATION_DIR} + scp -o StrictHostKeyChecking=no -r reverse-proxy/ root@${params.EXTERNAL_SERVER_IP}:${params.APPLICATION_DIR} + """ + } + } + } + + stage ('Deploy java application') { + steps { + build job: javaDeployPipeline, parameters: [ + string(name: 'VERSION', value: params.JAVA_VERSION), + string(name: 'EXTERNAL_SERVER_IP', value: params.EXTERNAL_SERVER_IP), + string(name: 'APPLICATION_DIR', value: params.APPLICATION_DIR) + ] + } + } + + stage ('Deploy angular application') { + steps { + build job: angularDeployPipeline, parameters: [ + string(name: 'VERSION', value: params.ANGULAR_VERSION), + string(name: 'EXTERNAL_SERVER_IP', value: params.EXTERNAL_SERVER_IP), + string(name: 'APPLICATION_DIR', value: params.APPLICATION_DIR) + ] + } + } + } +} \ No newline at end of file diff --git a/jenkins/java/deployment/Jenkinsfile b/jenkins/java/deployment/Jenkinsfile index 6ed876bb1..c12c87128 100644 --- a/jenkins/java/deployment/Jenkinsfile +++ b/jenkins/java/deployment/Jenkinsfile @@ -32,6 +32,7 @@ pipeline{ parameters { string(name: 'VERSION', defaultValue: '0.1-SNAPSHOT', description: 'Version number') string(name: 'EXTERNAL_SERVER_IP', defaultValue: '10.40.235.244', description: 'Server IP') + strint(name: 'APPLICATION_DIR', defaultValue: '/root/mythaistar/reverse-proxy/', description: 'My Thai Star application directory') } stages { @@ -68,10 +69,10 @@ pipeline{ sshagent (credentials: ['3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11']) { sh """ # Copy resulting ".war" file from workspace to deployment server - scp -o StrictHostKeyChecking=no -r mythaistar.war root@${params.EXTERNAL_SERVER_IP}:/root/mythaistar/reverse-proxy/java/ + scp -o StrictHostKeyChecking=no -r mythaistar.war root@${params.EXTERNAL_SERVER_IP}:${params.APPLICATION_DIR}java/ # Launch application in Docker container - ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f /root/mythaistar/reverse-proxy/docker-compose.yml up -d --build java + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} docker-compose -f ${params.APPLICATION_DIR}docker-compose.yml up -d --build java """ } } From fb594688eee6659118daa7f4b0fe8d2c3cdd73b2 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 16:34:40 +0100 Subject: [PATCH 12/15] Corrected typo error in the word 'strint' --- jenkins/angular/deployment/Jenkinsfile | 2 +- jenkins/deployment/Jenkinsfile | 2 +- jenkins/java/deployment/Jenkinsfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jenkins/angular/deployment/Jenkinsfile b/jenkins/angular/deployment/Jenkinsfile index 895897654..9cc0d79f2 100644 --- a/jenkins/angular/deployment/Jenkinsfile +++ b/jenkins/angular/deployment/Jenkinsfile @@ -28,7 +28,7 @@ pipeline{ parameters { string(name: 'VERSION', defaultValue: '1.12.0-SNAPSHOT', description: 'Version number') string(name: 'EXTERNAL_SERVER_IP', defaultValue: '10.40.235.244', description: 'Server IP') - strint(name: 'APPLICATION_DIR', defaultValue: '/root/mythaistar/reverse-proxy/', description: 'My Thai Star application directory') + string(name: 'APPLICATION_DIR', defaultValue: '/root/mythaistar/reverse-proxy/', description: 'My Thai Star application directory') } stages { diff --git a/jenkins/deployment/Jenkinsfile b/jenkins/deployment/Jenkinsfile index 5f3812bb7..2da04d86a 100644 --- a/jenkins/deployment/Jenkinsfile +++ b/jenkins/deployment/Jenkinsfile @@ -21,7 +21,7 @@ pipeline{ string(name: 'JAVA_VERSION', defaultValue: '0.1-SNAPSHOT', description: 'Java Version number') string(name: 'ANGULAR_VERSION', defaultValue: '1.12.0-SNAPSHOT', description: 'Angulalr Version number') string(name: 'EXTERNAL_SERVER_IP', defaultValue: '10.40.235.244', description: 'Server IP') - strint(name: 'APPLICATION_DIR', defaultValue: '/root/mythaistar/reverse-proxy/', description: 'My Thai Star application directory') + string(name: 'APPLICATION_DIR', defaultValue: '/root/mythaistar/reverse-proxy/', description: 'My Thai Star application directory') } diff --git a/jenkins/java/deployment/Jenkinsfile b/jenkins/java/deployment/Jenkinsfile index c12c87128..7b4186bfd 100644 --- a/jenkins/java/deployment/Jenkinsfile +++ b/jenkins/java/deployment/Jenkinsfile @@ -32,7 +32,7 @@ pipeline{ parameters { string(name: 'VERSION', defaultValue: '0.1-SNAPSHOT', description: 'Version number') string(name: 'EXTERNAL_SERVER_IP', defaultValue: '10.40.235.244', description: 'Server IP') - strint(name: 'APPLICATION_DIR', defaultValue: '/root/mythaistar/reverse-proxy/', description: 'My Thai Star application directory') + string(name: 'APPLICATION_DIR', defaultValue: '/root/mythaistar/reverse-proxy/', description: 'My Thai Star application directory') } stages { From 31f0674601b61757ab5898ae70573ef3ca812980 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 16:38:45 +0100 Subject: [PATCH 13/15] Updated deployment jenkinsfile --- jenkins/deployment/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkins/deployment/Jenkinsfile b/jenkins/deployment/Jenkinsfile index 2da04d86a..49bee2365 100644 --- a/jenkins/deployment/Jenkinsfile +++ b/jenkins/deployment/Jenkinsfile @@ -30,7 +30,7 @@ pipeline{ steps { sshagent (credentials: [sshAgentCredentials]) { sh """ - mkdir -p ${params.APPLICATION_DIR} + ssh -o StrictHostKeyChecking=no root@${params.EXTERNAL_SERVER_IP} mkdir -p ${params.APPLICATION_DIR} scp -o StrictHostKeyChecking=no -r reverse-proxy/ root@${params.EXTERNAL_SERVER_IP}:${params.APPLICATION_DIR} """ } From 700a29b670734f06a6ecc1dd9caa3a073b16a1a8 Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Mon, 10 Dec 2018 18:42:46 +0100 Subject: [PATCH 14/15] Added pipeline definition --- jenkins/angular/cicd/pipeline.xml | 28 +++++++++++++++++++++++++ jenkins/angular/deployment/pipeline.xml | 28 +++++++++++++++++++++++++ jenkins/create-pipelines.sh | 6 ++++++ jenkins/deployment/pipeline.xml | 28 +++++++++++++++++++++++++ jenkins/folder.xml | 22 +++++++++++++++++++ jenkins/java/cicd/pipeline.xml | 28 +++++++++++++++++++++++++ jenkins/java/deployment/pipeline.xml | 28 +++++++++++++++++++++++++ 7 files changed, 168 insertions(+) create mode 100644 jenkins/angular/cicd/pipeline.xml create mode 100644 jenkins/angular/deployment/pipeline.xml create mode 100644 jenkins/create-pipelines.sh create mode 100644 jenkins/deployment/pipeline.xml create mode 100644 jenkins/folder.xml create mode 100644 jenkins/java/cicd/pipeline.xml create mode 100644 jenkins/java/deployment/pipeline.xml diff --git a/jenkins/angular/cicd/pipeline.xml b/jenkins/angular/cicd/pipeline.xml new file mode 100644 index 000000000..e4d6d8a66 --- /dev/null +++ b/jenkins/angular/cicd/pipeline.xml @@ -0,0 +1,28 @@ + + + + false + + + + 2 + + + https://github.com/dario-rodriguez/my-thai-star.git + + + + + */cicd-and-deployment + + + false + + + + jenkins/angular/cicd/Jenkinsfile + true + + + false + \ No newline at end of file diff --git a/jenkins/angular/deployment/pipeline.xml b/jenkins/angular/deployment/pipeline.xml new file mode 100644 index 000000000..c654ab0fc --- /dev/null +++ b/jenkins/angular/deployment/pipeline.xml @@ -0,0 +1,28 @@ + + + + false + + + + 2 + + + https://github.com/dario-rodriguez/my-thai-star.git + + + + + */cicd-and-deployment + + + false + + + + jenkins/deployment/cicd/Jenkinsfile + true + + + false + \ No newline at end of file diff --git a/jenkins/create-pipelines.sh b/jenkins/create-pipelines.sh new file mode 100644 index 000000000..880d3c2d2 --- /dev/null +++ b/jenkins/create-pipelines.sh @@ -0,0 +1,6 @@ + + +CRUMB=$(curl -s 'https://devon.s2-eu.capgemini.com/jenkins/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)' -u $1:$2) +curl -s -XPOST 'https://devon.s2-eu.capgemini.com/jenkins/createItem?name=MTS2' -u $1:$2 --data-binary @./folder.xml -H "$CRUMB" -H "Content-Type:text/xml" +curl -s -XPOST 'https://devon.s2-eu.capgemini.com/jenkins/job/MTS2/createItem?name=CICD' -u $1:$2 --data-binary @./folder.xml -H "$CRUMB" -H "Content-Type:text/xml" +curl -s -XPOST 'https://devon.s2-eu.capgemini.com/jenkins/job/MTS2/createItem?name=deployment' -u $1:$2 --data-binary @./folder.xml -H "$CRUMB" -H "Content-Type:text/xml" \ No newline at end of file diff --git a/jenkins/deployment/pipeline.xml b/jenkins/deployment/pipeline.xml new file mode 100644 index 000000000..008f1d8bd --- /dev/null +++ b/jenkins/deployment/pipeline.xml @@ -0,0 +1,28 @@ + + + + false + + + + 2 + + + https://github.com/dario-rodriguez/my-thai-star.git + + + + + */cicd-and-deployment + + + false + + + + jenkins/deployment/Jenkinsfile + true + + + false + \ No newline at end of file diff --git a/jenkins/folder.xml b/jenkins/folder.xml new file mode 100644 index 000000000..d8e46f537 --- /dev/null +++ b/jenkins/folder.xml @@ -0,0 +1,22 @@ + + + + + + + + All + false + false + + + + + + + + false + + + + \ No newline at end of file diff --git a/jenkins/java/cicd/pipeline.xml b/jenkins/java/cicd/pipeline.xml new file mode 100644 index 000000000..0e64c9a1e --- /dev/null +++ b/jenkins/java/cicd/pipeline.xml @@ -0,0 +1,28 @@ + + + + false + + + + 2 + + + https://github.com/dario-rodriguez/my-thai-star.git + + + + + */cicd-and-deployment + + + false + + + + jenkins/java/cicd/Jenkinsfile + true + + + false + \ No newline at end of file diff --git a/jenkins/java/deployment/pipeline.xml b/jenkins/java/deployment/pipeline.xml new file mode 100644 index 000000000..63153166d --- /dev/null +++ b/jenkins/java/deployment/pipeline.xml @@ -0,0 +1,28 @@ + + + + false + + + + 2 + + + https://github.com/dario-rodriguez/my-thai-star.git + + + + + */cicd-and-deployment + + + false + + + + jenkins/java/deployment/Jenkinsfile + true + + + false + \ No newline at end of file From fdad556bfd0823e8a5fdd1e588d41dac8e92cfbc Mon Sep 17 00:00:00 2001 From: Dario Rodriguez Gonzalez Date: Tue, 11 Dec 2018 09:53:14 +0100 Subject: [PATCH 15/15] Added documentation for deployment --- README.adoc | 12 ++++++++++++ jenkins/README.md | 17 +++++++++++++++++ jenkins/console.png | Bin 0 -> 36381 bytes jenkins/create-pipelines.sh | 22 ++++++++++++++++++---- jenkins/deployment/Jenkinsfile | 4 ++-- reverse-proxy/docker-compose.yml | 2 +- 6 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 jenkins/console.png diff --git a/README.adoc b/README.adoc index 6a209d1fa..65b1e26ec 100644 --- a/README.adoc +++ b/README.adoc @@ -48,6 +48,8 @@ Use them at your own risk. == Deployment +=== From code + This application can be easily deployed using **Docker** and **docker-compose**. `$ docker-compose up` @@ -64,3 +66,13 @@ fdb63e26d299 mythaistar_angular "nginx -g 'daemon of…" 3 minu ``` The usage of the `reverse-proxy` only uses 1 port of the Docker host (where this is deployed), the `8080`. All internal communication of containers is done using docker alias of services. + +=== From artifact + +If we are using a CICD pipeline and we store the artifact on nexus, we can also deploy it without recompile all code. For this purpose there are three deployment pipelines: + +- deployment: deploy all application with a reserve-proxy +- java: deploy only the java application +- angular: deploy angular application + reverse-proxy (you must run the java deployment at least once before running this deployment) + +The result of this deployment will be the same as in the deployment from code, but instead of compiling the artifact again downloads it from nexus. All resources (docker-compose.yml, Dockerfiles and nginx.conf) are stored in the reverse-proxy folder. diff --git a/jenkins/README.md b/jenkins/README.md index ed63ea684..ce8ebbee4 100644 --- a/jenkins/README.md +++ b/jenkins/README.md @@ -148,6 +148,7 @@ In order to run all pipelines, we have installed the following plugins (maybe no - Xvnc plugin (xvnc) - Dashboard for Blue Ocean (blueocean-dashboard) - Script Security Plugin (script-security) +- **HTTP Request Plugin (http_request)** Most of them come pre-installed with the production line instance. The ones we need are the ones related to the pipeline and the ones in bold. @@ -169,6 +170,22 @@ Most of them come pre-installed with the production line instance. The ones we n You only need to create a new pipeline and modify the configuration following the image: ![](./pipeline-config.png) +Or you can execute the create-pipelines.sh as follows: + +```bash +$ ./create-pipelines.sh +``` + +example: + +```bash +$ ./create-pipelines.sh devon.s2-eu.capgemini.com/jenkins devonfw capgemini +``` + +**Important:** you need a bash shell in order to execute the command. If you are using windows, you can open a new bash shell in the devonfw console: +![console](./console.png) + + **TIP**: All environment variables used on both Jenkinsfiles should be declared in the correspondant Jenkins Pipeline configuration more or less like this: ![](./jenkins-pipelines-params.png) diff --git a/jenkins/console.png b/jenkins/console.png new file mode 100644 index 0000000000000000000000000000000000000000..0cf5048f392250a4c90a18fe7c1ddf1a6c31631f GIT binary patch literal 36381 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 " + exit -1 +fi -CRUMB=$(curl -s 'https://devon.s2-eu.capgemini.com/jenkins/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)' -u $1:$2) -curl -s -XPOST 'https://devon.s2-eu.capgemini.com/jenkins/createItem?name=MTS2' -u $1:$2 --data-binary @./folder.xml -H "$CRUMB" -H "Content-Type:text/xml" -curl -s -XPOST 'https://devon.s2-eu.capgemini.com/jenkins/job/MTS2/createItem?name=CICD' -u $1:$2 --data-binary @./folder.xml -H "$CRUMB" -H "Content-Type:text/xml" -curl -s -XPOST 'https://devon.s2-eu.capgemini.com/jenkins/job/MTS2/createItem?name=deployment' -u $1:$2 --data-binary @./folder.xml -H "$CRUMB" -H "Content-Type:text/xml" \ No newline at end of file +JENKINS_URL=$1 +REQUEST="https://$JENKINS_URL" +ENDING='/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)' +CRUMB=$(curl -s "$REQUEST$ENDING" -u $2:$3) +curl -s -XPOST "https://$JENKINS_URL/createItem?name=MTS" -u $2:$3 --data-binary @./folder.xml -H "$CRUMB" -H "Content-Type:text/xml" +curl -s -XPOST "https://$JENKINS_URL/job/MTS/createItem?name=CICD" -u $2:$3 --data-binary @./folder.xml -H "$CRUMB" -H "Content-Type:text/xml" +curl -s -XPOST "https://$JENKINS_URL/job/MTS/createItem?name=deployment" -u $2:$3 --data-binary @./folder.xml -H "$CRUMB" -H "Content-Type:text/xml" +curl -s -XPOST "https://$JENKINS_URL/job/MTS/job/CICD/createItem?name=angular" -u $2:$3 --data-binary @./angular/cicd/pipeline.xml -H "$CRUMB" -H "Content-Type:text/xml" +curl -s -XPOST "https://$JENKINS_URL/job/MTS/job/CICD/createItem?name=java" -u $2:$3 --data-binary @./java/cicd/pipeline.xml -H "$CRUMB" -H "Content-Type:text/xml" +curl -s -XPOST "https://$JENKINS_URL/job/MTS/job/deployment/createItem?name=angular" -u $2:$3 --data-binary @./angular/deployment/pipeline.xml -H "$CRUMB" -H "Content-Type:text/xml" +curl -s -XPOST "https://$JENKINS_URL/job/MTS/job/deployment/createItem?name=java" -u $2:$3 --data-binary @./java/deployment/pipeline.xml -H "$CRUMB" -H "Content-Type:text/xml" +curl -s -XPOST "https://$JENKINS_URL/job/MTS/job/deployment/createItem?name=deployment" -u $2:$3 --data-binary @./deployment/pipeline.xml -H "$CRUMB" -H "Content-Type:text/xml" \ No newline at end of file diff --git a/jenkins/deployment/Jenkinsfile b/jenkins/deployment/Jenkinsfile index 49bee2365..0417b666c 100644 --- a/jenkins/deployment/Jenkinsfile +++ b/jenkins/deployment/Jenkinsfile @@ -12,9 +12,9 @@ pipeline{ sshAgentCredentials = '3d0fa2a4-5cf0-4cf5-a3fd-23655eb33c11' // Java Deploy Pipeline name - javaDeployPipeline = 'deployment_java' + javaDeployPipeline = 'java' // Angular Deploy Pipeline name - angularDeployPipeline = 'deployment_angular' + angularDeployPipeline = 'angular' } parameters { diff --git a/reverse-proxy/docker-compose.yml b/reverse-proxy/docker-compose.yml index 788427cc2..da79390a6 100644 --- a/reverse-proxy/docker-compose.yml +++ b/reverse-proxy/docker-compose.yml @@ -1,6 +1,6 @@ version: '3' services: - web: + reverse-proxy: build: . image: my-thai-star/reverse-proxy:latest restart: always