diff --git a/.idea/modules/spring-postgresql-demo_test.iml b/.idea/modules/spring-postgresql-demo_test.iml index fa1e4be..5d57bb7 100644 --- a/.idea/modules/spring-postgresql-demo_test.iml +++ b/.idea/modules/spring-postgresql-demo_test.iml @@ -16,7 +16,6 @@ - diff --git a/Jenkins/ci/Jenkinsfile b/Jenkins/ci/Jenkinsfile index 8c9e06f..12afe82 100644 --- a/Jenkins/ci/Jenkinsfile +++ b/Jenkins/ci/Jenkinsfile @@ -3,67 +3,67 @@ def PROJECT_NAME = "spring-postgresql-demo" pipeline { - agent any - tools { - gradle 'gradle' - } - stages { - stage('Checkout GitHub') { - steps { - git changelog: true, poll: true, - branch: 'h2', - url: "https://github.com/garystafford/${PROJECT_NAME}" - } - } - stage('Build') { - steps { - sh 'gradle wrapper' - sh './gradlew clean build -x test' - } - } - stage('Unit Test') { - steps { - withEnv(['SPRING_DATASOURCE_URL=jdbc:h2:file:~/elections']) { - sh './gradlew cleanTest test' - } - junit '**/build/test-results/test/*.xml' - } - } - stage('SonarQube Analysis') { - steps { - withSonarQubeEnv('Local SonarQube') { - sh "./gradlew sonarqube -Dsonar.projectName=${PROJECT_NAME}" + agent any + tools { + gradle 'gradle' } - } - } - stage('Archive Artifact') { - steps { - archiveArtifacts 'build/libs/*.jar' - } - } - stage('Publish Artifact') { - steps { - withCredentials([string(credentialsId: 'GIT_TOKEN', variable: 'GIT_TOKEN')]) { - dir('build/libs/') { - sh "git init \ - && git config user.name \"jenkins-ci\" \ - && git config user.email \"jenkins-ci@jenkins-ci.com\" \ - && git add *.jar \ + stages { + stage('Checkout GitHub') { + steps { + git changelog: true, poll: true, + branch: 'master', + url: "https://github.com/garystafford/${PROJECT_NAME}" + } + } + stage('Build') { + steps { + sh 'gradle wrapper' + sh './gradlew clean build -x test' + } + } + stage('Unit Test') { // unit test against h2 + steps { + withEnv(['SPRING_DATASOURCE_URL=jdbc:h2:file:~/elections']) { + sh './gradlew cleanTest test' + } + junit '**/build/test-results/test/*.xml' + } + } + stage('SonarQube Analysis') { + steps { + withSonarQubeEnv('sonarqube') { + sh "./gradlew sonarqube -Dsonar.projectName=${PROJECT_NAME}" + } + } + } + stage('Archive Artifact') { // option 1 to build Dockerfile in next pipeline + steps { + archiveArtifacts 'build/libs/*.jar' + } + } + stage('Publish Artifact') { // option 2 to build Dockerfile in next pipeline + steps { + withCredentials([string(credentialsId: 'GIT_TOKEN', variable: 'GIT_TOKEN')]) { + dir('build/libs/') { + sh "git init \ + && git config user.name \"jenkins-ci\" \ + && git config user.email \"jenkins-ci@jenkins-ci.com\" \ + && git add *.jar \ && git commit -m \"Publish build artifact\" \ - && git push --force --quiet --progress \ + && git push --force --quiet --progress \ \"https://x-access-token:${GIT_TOKEN}@github.com/garystafford/${PROJECT_NAME}.git\" \ - master:build-artifacts-gke" - } + master:build-artifacts-gke" + } + } + } + } + } + post { + success { + slackSend(color: '#008000', message: "SUCCESS: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") + } + failure { + slackSend(color: '#FF0000', message: "FAILURE: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") + } } - } - } - } - post { - success { - slackSend(color: '#008000', message: "SUCCESS: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") - } - failure { - slackSend(color: '#FF0000', message: "FAILURE: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})") - } - } } diff --git a/Jenkins/docker/Jenkinsfile b/Jenkins/docker/Jenkinsfile index a4076c7..7d0a153 100644 --- a/Jenkins/docker/Jenkinsfile +++ b/Jenkins/docker/Jenkinsfile @@ -2,40 +2,43 @@ def DOCKER_HUB_ACCOUNT = "garystafford" def PROJECT_NAME = "spring-postgresql-demo" -// def IMAGE_TAG = "2.1.0" pipeline { - agent any - parameters { - string defaultValue: 'latest', - description: 'Tag applied to new Docker Image', - name: 'IMAGE_TAG', trim: true -} -stages { - stage('Checkout GitHub') { - steps { - git changelog: true, poll: true, - branch: 'h2', - url: "https://github.com/garystafford/${PROJECT_NAME}" - } - } - stage('Build Image') { - steps { - copyArtifacts filter: 'build/libs/*.jar', fingerprintArtifacts: true, - flatten: true, projectName: 'election-ci', selector: lastSuccessful(), - target: 'docker/' - withCredentials([usernamePassword(credentialsId: 'DOCKER_HUB', - usernameVariable: 'DOCKER_HUB_USERNAME', - passwordVariable: 'DOCKER_HUB_PASSWORD')]) { - sh "docker login --username ${DOCKER_HUB_USERNAME} --password ${DOCKER_HUB_PASSWORD}" + agent any + parameters { + string defaultValue: 'latest', + description: 'Tag applied to new Docker Image', + name: 'IMAGE_TAG', trim: true } - dir('docker') { - // sh 'ls -al' - sh 'docker version' - sh "docker build --file Dockerfile_Jenkins --no-cache --tag ${DOCKER_HUB_ACCOUNT}/${PROJECT_NAME}:${params.IMAGE_TAG} ." - sh "docker push ${DOCKER_HUB_ACCOUNT}/${PROJECT_NAME}:${params.IMAGE_TAG}" + stages { + stage('Checkout GitHub') { + steps { + git changelog: true, poll: false, + branch: 'master', + url: "https://github.com/garystafford/${PROJECT_NAME}" + } + } + stage('Build Image') { // uses option 1 from ci job to build Dockerfile + steps { + copyArtifacts filter: 'build/libs/*.jar', fingerprintArtifacts: true, + flatten: true, projectName: 'election-ci', selector: lastSuccessful(), + target: 'docker/' + withCredentials([usernamePassword(credentialsId: 'DOCKER_HUB', + usernameVariable: 'DOCKER_HUB_USERNAME', + passwordVariable: 'DOCKER_HUB_PASSWORD')]) { + sh "docker login --username ${DOCKER_HUB_USERNAME} --password ${DOCKER_HUB_PASSWORD}" + } + dir('docker') { + sh "docker build --file Dockerfile_Jenkins --no-cache --tag ${DOCKER_HUB_ACCOUNT}/${PROJECT_NAME}:${params.IMAGE_TAG} ." + } + } + } + stage('Push Image') { + steps { + dir('docker') { + sh "docker push ${DOCKER_HUB_ACCOUNT}/${PROJECT_NAME}:${params.IMAGE_TAG}" + } + } + } } - } - } - } } diff --git a/Jenkins/integration/Jenkinsfile b/Jenkins/integration/Jenkinsfile index 6e74b7c..afea43d 100644 --- a/Jenkins/integration/Jenkinsfile +++ b/Jenkins/integration/Jenkinsfile @@ -3,33 +3,34 @@ def PROJECT_NAME = "spring-postgresql-demo" pipeline { - agent any - stages { - stage('Checkout SCM') { - steps { - git changelog: true, poll: false, - url: "https://github.com/garystafford/${PROJECT_NAME}" - } - } - stage('Smoke Tests') { - steps { - dir('postman') { - nodejs('nodejs') { - sh 'sh newman-smoke-tests-minikube.sh' - } - junit '**/newman/*.xml' + agent any + stages { + stage('Checkout SCM') { + steps { + git changelog: true, poll: false, + branch: 'master', + url: "https://github.com/garystafford/${PROJECT_NAME}" + } + } + stage('Smoke Tests') { + steps { + dir('postman') { + nodejs('nodejs') { + sh 'sh newman-smoke-tests-minikube.sh' + } + junit '**/newman/*.xml' + } + } + } + stage('Integration Tests') { + steps { + dir('postman') { + nodejs('nodejs') { + sh 'sh newman-integration-tests-minikube.sh' + } + junit '**/newman/*.xml' + } + } + } } - } - } - stage('Integration Tests') { - steps { - dir('postman') { - nodejs('nodejs') { - sh 'sh newman-integration-tests-minikube.sh' - } - junit '**/newman/*.xml' - } - } - } - } } diff --git a/Jenkins/k8s-demo-deploy/Jenkinsfile b/Jenkins/k8s-demo-deploy/Jenkinsfile new file mode 100644 index 0000000..c732804 --- /dev/null +++ b/Jenkins/k8s-demo-deploy/Jenkinsfile @@ -0,0 +1,59 @@ +#!groovy + +def PROJECT_NAME = "spring-postgresql-demo" + +pipeline { + agent any + stages { + stage('Checkout GitHub') { + steps { + git changelog: true, poll: false, + branch: 'master', + url: "https://github.com/garystafford/${PROJECT_NAME}" + } + } + stage('Get Cluster Creds') { + steps { + dir('kubernetes') { + sh 'sh ./part1b-get-cluster-creds.sh' + } + } + } + stage('Build Environments') { // dev, test, uat + steps { + dir('kubernetes') { + sh 'sh ./part2-create-environments.sh' + } + } + } + stage('Deploy Postgres to dev') { // doesn't work with istio! + steps { + dir('kubernetes') { + sh 'sh ./deploy-postgres-gke-dev.sh' + } + } + } + stage('Deploy Election v1 to dev') { + environment { // get cluster ip ranges for deploy script + IP_RANGES = sh(returnStdout: true, + script: 'sh ./kubernetes/get-cluster-ip-ranges.sh') + } + steps { + dir('kubernetes') { + sh 'env | sort' + sh 'sh ./part3a-deploy-v1-dev.sh' + } + } + } + stage('Smoke Test') { // assumes 'api.dev.voter-demo.com' reachable + steps { + dir('postman') { + nodejs('nodejs') { + sh 'sh newman-smoke-tests-minikube.sh' + } + junit '**/newman/*.xml' + } + } + } + } +} diff --git a/docker/docker-build-push-v1.0.0.sh b/docker/docker-build-push-v1.0.0.sh deleted file mode 100644 index 63085eb..0000000 --- a/docker/docker-build-push-v1.0.0.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# docker login --username --password - -# docker build -t spring-postgresql-demo:$(git rev-parse --short HEAD) . -# docker build --no-cache -t garystafford/spring-postgresql-demo:latest . - -docker build --no-cache -t garystafford/spring-postgresql-demo:1.0.0 . -docker push garystafford/spring-postgresql-demo:1.0.0 diff --git a/docker/docker-build-push-v1.1.0.sh b/docker/docker-build-push-v1.1.0.sh new file mode 100644 index 0000000..50cd7ad --- /dev/null +++ b/docker/docker-build-push-v1.1.0.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# docker login --username --password + +docker build --no-cache -t garystafford/spring-postgresql-demo:1.1.0 . +docker push garystafford/spring-postgresql-demo:1.1.0 diff --git a/kubernetes/README.md b/kubernetes/README.md index b34352e..7cb4965 100644 --- a/kubernetes/README.md +++ b/kubernetes/README.md @@ -1,4 +1,4 @@ -# Kubernetes Installation +# Kubernetes Installation Notes ## Amazon RDS @@ -15,3 +15,7 @@ aws rds delete-db-instance \ --db-instance-identifier elections-dev \ --skip-final-snapshot ``` + +```bash +kubectl -n dev run curl --image=radial/busyboxplus:curl -i --tty +``` diff --git a/kubernetes/deploy-postgres-gke-dev.sh b/kubernetes/deploy-postgres-gke-dev.sh new file mode 100644 index 0000000..7d67e92 --- /dev/null +++ b/kubernetes/deploy-postgres-gke-dev.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# deploy postgres within cluster vs. external postgres (i.e. AWS) + +# set -x + +# postgresql in a pod +kubectl apply -f ./postgres-local/postgres-deployment.yaml -n dev + +kubectl apply -f ./postgres-local/postgres-service.yaml -n dev + +kubectl get pods -n dev diff --git a/kubernetes/deployments/election-deployment-v1-dev.yaml b/kubernetes/deployments/election-deployment-v1-dev.yaml index b7a2b2b..5240078 100644 --- a/kubernetes/deployments/election-deployment-v1-dev.yaml +++ b/kubernetes/deployments/election-deployment-v1-dev.yaml @@ -19,12 +19,12 @@ spec: spec: containers: - name: election-v1 - image: garystafford/spring-postgresql-demo:1.0.0 + image: garystafford/spring-postgresql-demo:1.1.0 env: - name: SPRING_PROFILES_ACTIVE value: dev - name: SPRING_DATASOURCE_HIKARI_MAXIMUM-POOL-SIZE - value: 2 + value: '2' - name: SPRING_DATASOURCE_URL valueFrom: secretKeyRef: diff --git a/kubernetes/deployments/election-deployment-v1-test.yaml b/kubernetes/deployments/election-deployment-v1-test.yaml index a8e9ced..7282f1a 100644 --- a/kubernetes/deployments/election-deployment-v1-test.yaml +++ b/kubernetes/deployments/election-deployment-v1-test.yaml @@ -19,7 +19,7 @@ spec: spec: containers: - name: election-v1 - image: garystafford/spring-postgresql-demo:1.0.0 + image: garystafford/spring-postgresql-demo:1.1.0 env: - name: SPRING_PROFILES_ACTIVE value: test diff --git a/kubernetes/deployments/election-deployment-v1-uat.yaml b/kubernetes/deployments/election-deployment-v1-uat.yaml index d8f24dc..e3bf0a7 100644 --- a/kubernetes/deployments/election-deployment-v1-uat.yaml +++ b/kubernetes/deployments/election-deployment-v1-uat.yaml @@ -19,7 +19,7 @@ spec: spec: containers: - name: election-v1 - image: garystafford/spring-postgresql-demo:1.0.0 + image: garystafford/spring-postgresql-demo:1.1.0 env: - name: SPRING_PROFILES_ACTIVE value: uat diff --git a/kubernetes/get-cluster-ip-ranges.sh b/kubernetes/get-cluster-ip-ranges.sh index ccf501a..25ec9ee 100644 --- a/kubernetes/get-cluster-ip-ranges.sh +++ b/kubernetes/get-cluster-ip-ranges.sh @@ -4,12 +4,17 @@ # capture the clusterIpv4Cidr and servicesIpv4Cidr values # required for manual sidecar injection with kube-inject -CLUSTER_IPV4_CIDR=$(gcloud container clusters describe election-nonprod-cluster \ - --zone us-east1-b --project springdemo-199819 \ +# change to match your environment +GCP_PROJECT="springdemo-199819" +GKE_CLUSTER="election-nonprod-cluster" +GCP_ZONE="us-east1-b" + +CLUSTER_IPV4_CIDR=$(gcloud container clusters describe ${GKE_CLUSTER} \ + --zone ${GCP_ZONE} --project ${GCP_PROJECT} \ | egrep clusterIpv4Cidr | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{2}\b") -SERVICES_IPV4_CIDR=$(gcloud container clusters describe election-nonprod-cluster \ - --zone us-east1-b --project springdemo-199819 \ +SERVICES_IPV4_CIDR=$(gcloud container clusters describe ${GKE_CLUSTER} \ + --zone ${GCP_ZONE} --project ${GCP_PROJECT} \ | egrep servicesIpv4Cidr | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{2}\b") export IP_RANGES="$CLUSTER_IPV4_CIDR,$SERVICES_IPV4_CIDR" diff --git a/kubernetes/part1-create-gke-cluster.sh b/kubernetes/part1-create-gke-cluster.sh index ef64504..f78a7c8 100644 --- a/kubernetes/part1-create-gke-cluster.sh +++ b/kubernetes/part1-create-gke-cluster.sh @@ -5,10 +5,12 @@ # change to match your environment ISTIO_HOME="/Applications/istio-0.7.1" GCP_DEPLOYMENT_MANAGER="$ISTIO_HOME/install/gcp/deployment_manager" + GCP_PROJECT="springdemo-199819" GKE_CLUSTER="election-nonprod-cluster" -ISTIO_VER="0.7.1" GCP_ZONE="us-east1-b" + +ISTIO_VER="0.7.1" NODE_COUNT="1" INSTANCE_TYPE="n1-standard-1" diff --git a/kubernetes/part1B-get-cluster-creds.sh b/kubernetes/part1B-get-cluster-creds.sh new file mode 100644 index 0000000..b18f454 --- /dev/null +++ b/kubernetes/part1B-get-cluster-creds.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# part 1b: just get gke/istio cluster creds + +# change to match your environment +GCP_PROJECT="springdemo-199819" +GKE_CLUSTER="election-nonprod-cluster" +GCP_ZONE="us-east1-b" + +# get creds for cluster +gcloud container clusters get-credentials $GKE_CLUSTER \ + --zone $GCP_ZONE --project $GCP_PROJECT diff --git a/kubernetes/part3-deploy-v1-all-envs.sh b/kubernetes/part3-deploy-v1-all-envs.sh index db7444a..6012589 100644 --- a/kubernetes/part3-deploy-v1-all-envs.sh +++ b/kubernetes/part3-deploy-v1-all-envs.sh @@ -36,6 +36,6 @@ kubectl apply -f ./routerules/routerule-election-v1.yaml -n test kubectl apply -f ./routerules/routerule-election-v1.yaml -n uat # kubectl describe routerule -n dev -# kubectl get pods -n dev -# kubectl get pods -n test -# kubectl get pods -n uat +kubectl get pods -n dev +kubectl get pods -n test +kubectl get pods -n uat diff --git a/kubernetes/part3a-deploy-v1-dev.sh b/kubernetes/part3a-deploy-v1-dev.sh new file mode 100644 index 0000000..519525d --- /dev/null +++ b/kubernetes/part3a-deploy-v1-dev.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# apply resources part 3a: just v1 to dev + +# election v1 deployment with manual sidecar injection +# kubectl apply -f ./deployments/election-deployment-v1-dev.yaml +istioctl kube-inject –kubeconfig "~/.kube/config" \ + -f ./deployments/election-deployment-v1-dev.yaml \ + --includeIPRanges=$IP_RANGES > \ + election-deployment-istio.yaml \ + && kubectl apply -f election-deployment-istio.yaml \ + && rm election-deployment-istio.yaml + +# services +kubectl apply -f ./services/election-service.yaml -n dev + +# route rules +kubectl apply -f ./routerules/routerule-election-v1.yaml -n dev + +kubectl get pods -n dev diff --git a/kubernetes/postgres-local/postgres-deployment.yaml b/kubernetes/postgres-local/postgres-deployment.yaml new file mode 100644 index 0000000..5e6b36a --- /dev/null +++ b/kubernetes/postgres-local/postgres-deployment.yaml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: postgres + name: postgres +spec: + selector: + matchLabels: + app: postgres + replicas: 1 + strategy: {} + template: + metadata: + labels: + app: postgres + spec: + containers: + - image: postgres:10.3-alpine + env: + - name: POSTGRES_DB + value: elections + - name: POSTGRES_PASSWORD + value: postgres1234 + - name: POSTGRES_USERNAME + value: postgres + name: postgres + ports: + - containerPort: 5432 + restartPolicy: Always +status: {} diff --git a/kubernetes/postgres-local/postgres-service.yaml b/kubernetes/postgres-local/postgres-service.yaml new file mode 100644 index 0000000..1a5e3df --- /dev/null +++ b/kubernetes/postgres-local/postgres-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + istio-injection: enabled + app: postgres + name: postgres +spec: + ports: + - name: tcp + protocol: TCP + port: 5432 + targetPort: 5432 + selector: + app: postgres +status: {} diff --git a/kubernetes/secrets/secret-postgresql-conn-info-dev.yaml b/kubernetes/secrets/secret-postgresql-conn-info-dev.yaml index c6a6406..eada282 100644 --- a/kubernetes/secrets/secret-postgresql-conn-info-dev.yaml +++ b/kubernetes/secrets/secret-postgresql-conn-info-dev.yaml @@ -8,6 +8,6 @@ metadata: namespace: dev type: Opaque data: - url: - username: - password: + url: amRiYzpwb3N0Z3Jlc3FsOi8vcG9zdGdyZXM6NTQzMi9lbGVjdGlvbnM= + username: cG9zdGdyZXM= + password: cG9zdGdyZXMxMjM0 diff --git a/kubernetes/secrets/secret-postgresql-conn-info-test.yaml b/kubernetes/secrets/secret-postgresql-conn-info-test.yaml index 41eb5d6..fc29f36 100644 --- a/kubernetes/secrets/secret-postgresql-conn-info-test.yaml +++ b/kubernetes/secrets/secret-postgresql-conn-info-test.yaml @@ -8,6 +8,6 @@ metadata: namespace: test type: Opaque data: - url: - username: - password: + url: amRiYzpwb3N0Z3Jlc3FsOi8vcG9zdGdyZXM6NTQzMi9lbGVjdGlvbnM= + username: cG9zdGdyZXM= + password: cG9zdGdyZXMxMjM0 diff --git a/kubernetes/secrets/secret-postgresql-conn-info-uat.yaml b/kubernetes/secrets/secret-postgresql-conn-info-uat.yaml index b7fb9b6..f89489b 100644 --- a/kubernetes/secrets/secret-postgresql-conn-info-uat.yaml +++ b/kubernetes/secrets/secret-postgresql-conn-info-uat.yaml @@ -8,6 +8,6 @@ metadata: namespace: uat type: Opaque data: - url: - username: - password: + url: amRiYzpwb3N0Z3Jlc3FsOi8vcG9zdGdyZXM6NTQzMi9lbGVjdGlvbnM= + username: cG9zdGdyZXM= + password: cG9zdGdyZXMxMjM0 diff --git a/minikube_db_local/part2-deploy-v2.sh b/minikube_db_local/part2-deploy-v2.sh index 8d8bb84..6c68df5 100644 --- a/minikube_db_local/part2-deploy-v2.sh +++ b/minikube_db_local/part2-deploy-v2.sh @@ -2,6 +2,8 @@ # part 2: deploy v2 to minimkube local dev +kubectl config use-context minikube + # election v2 deployment with manual sidecar injection istioctl kube-inject –kubeconfig "~/.kube/config" \ -f ./resources/election-deployment-v2.yaml \ diff --git a/postman/newman-smoke-tests-dev.sh b/postman/newman-smoke-tests-dev.sh new file mode 100644 index 0000000..8d7977b --- /dev/null +++ b/postman/newman-smoke-tests-dev.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +COLLECTION="election-k8s-smoke.postman_collection.json" +ENVIRONMENT="spring-postgresql-demo_gke_dev.postman_environment.json" +ITERATIONS=1 +DELAY=250 + +newman run $COLLECTION \ + --environment $ENVIRONMENT \ + --iteration-count $ITERATIONS \ + --no-color \ + --reporters cli,junit