diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9c492405b66d62bdb3db96f5cde90ea642c8b76f..424df1f9df79018b3bcb534ff1aac99bd405a2b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,2 +1,37 @@ include: - - local: semantic-release-for-gitlab-ci-includes.yml + - /includes/cache.yml + - /includes/before-script-yarn.yml + - /includes/rules.yml + - /includes/docker-build.yml + - /semantic-release-for-gitlab-ci-includes.yml + +# build the two gitlab-ci pipeline docker images +docker-build-pipeline: + stage: docker-build + image: docker:20 + variables: + CACHE_IMAGE_TAG: cache + IMAGE_TAG: latest + IMAGE_NAME: $CI_REGISTRY_IMAGE/pipeline + + DOCKER_DIR: "./dockerfiles/pipeline" + extends: + - .docker-build-app-base + +docker-build-docker-in-docker: + stage: docker-build + image: docker:20 + variables: + CACHE_IMAGE_TAG: cache + IMAGE_TAG: latest + IMAGE_NAME: $CI_REGISTRY_IMAGE/docker-build + DOCKER_DIR: "./dockerfiles/docker-build" + extends: + - .docker-build-app-base + +stages: + - docker-build + - release + +release: + extends: .release diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000000000000000000000000000000000000..2992e0ebeae7558f5d955038c7c45301c82d7889 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,8 @@ +# Development + +the yaml files in the root folder are meant to be used directly by other gitlab projects (see readme). + +The pipeline stages use a docker image that is build with this repo as well: [git.panter.ch:5001/catladder/gitlab-ci/pipeline](/dockerfiles/pipeline/Dockerfile) +This image is used for all stages, but the docker stage. + +the docker stage requires a special docker-in-docker image, which we build [here](/dockerfiles/docker-build/Dockerfile) as well. diff --git a/dockerfiles/docker-build/Dockerfile b/dockerfiles/docker-build/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d971101d1ee68392c412bee0b283d4ae9021c5c3 --- /dev/null +++ b/dockerfiles/docker-build/Dockerfile @@ -0,0 +1,11 @@ + +FROM docker:20 + +# add bash +RUN apk add --no-cache bash + +RUN mkdir p /scripts +COPY ./scripts/* /scripts/ +RUN chmod +x /scripts/* + +ENV PATH="/scripts:${PATH}" diff --git a/dockerfiles/docker-build/scripts/ensureMeteorDockerfile b/dockerfiles/docker-build/scripts/ensureMeteorDockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4fd6ee85a2b2db0dda7def5a07bf203f0c9987a6 --- /dev/null +++ b/dockerfiles/docker-build/scripts/ensureMeteorDockerfile @@ -0,0 +1,43 @@ +#!/bin/bash + +if [ ! -f .dockerignore ]; then + cat >.dockerignore <Dockerfile <.dockerignore <Dockerfile <> /etc/apt/sources.list.d/google.list' +RUN apt-get update && apt-get install -y google-chrome-stable + +# set high timeout for yarn, see https://github.com/dhis2/notes/issues/29 +RUN yarn config set network-timeout 600000 -g +# add meteor (used by some apps) +RUN curl https://install.meteor.com/ | sh +# install some additional used released (should theoretically speed up ci-builds) +RUN meteor update --release 2.2 --allow-superuser + +RUN yarn global add semantic-release @semantic-release/commit-analyzer @semantic-release/release-notes-generator @semantic-release/git @semantic-release/changelog @semantic-release/gitlab; + +ENV KUBERNETES_VERSION 1.18.20 +RUN curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl" ;\ + chmod +x /usr/bin/kubectl ;\ + kubectl version --client + +ENV HELM_2_VERSION 2.14.3 +RUN curl "https://get.helm.sh/helm-v${HELM_2_VERSION}-linux-amd64.tar.gz" | tar zx ;\ + mv linux-amd64/helm /usr/bin/ ;\ + helm version --client +RUN cp /usr/bin/helm /usr/bin/helm2 +# also install helm3 +ENV HELM_VERSION 3.6.3 +RUN curl "https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx ;\ + mv linux-amd64/helm /usr/bin/helm3 ;\ + helm3 version --client + +RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash +RUN source /root/.nvm/nvm.sh +RUN chmod +x /root/.nvm/nvm.sh +RUN /root/.nvm/nvm.sh install 10 +RUN /root/.nvm/nvm.sh install 11 +RUN /root/.nvm/nvm.sh install 12 +RUN /root/.nvm/nvm.sh install 13 +RUN /root/.nvm/nvm.sh install 14 +RUN /root/.nvm/nvm.sh install 15 +RUN /root/.nvm/nvm.sh install 16 +# ENV NVM_DIR=/root/.nvm +# RUN bash -c 'source /root/.nvm/nvm.sh' +# RUN echo '. /root/.nvm/nvm.sh' >> /root/.bashrc +# RUN bash -c 'source /root/.nvm/nvm.sh' +# RUN echo '. ~/.nvm/nvm.sh' >> $HOME/.profile +# RUN . /root/.nvm/nvm.sh && nvm --version +RUN mkdir p /scripts +COPY ./scripts/* /scripts/ +RUN chmod +x /scripts/* + +ENV PATH="/scripts:${PATH}" diff --git a/dockerfiles/pipeline/scripts/kubernetesCreateSecret b/dockerfiles/pipeline/scripts/kubernetesCreateSecret new file mode 100644 index 0000000000000000000000000000000000000000..a698a21847aa72afca165fb153b9cebab089087a --- /dev/null +++ b/dockerfiles/pipeline/scripts/kubernetesCreateSecret @@ -0,0 +1,8 @@ +#!/bin/bash +kubectl create secret -n "$NAMESPACE" \ + docker-registry $IMAGE_PULL_SECRET \ + --docker-server="$CI_REGISTRY" \ + --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \ + --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \ + --docker-email="$GITLAB_USER_EMAIL" \ + -o yaml --dry-run | kubectl replace -n "$NAMESPACE" --force -f - diff --git a/dockerfiles/pipeline/scripts/kubernetesDelete b/dockerfiles/pipeline/scripts/kubernetesDelete new file mode 100644 index 0000000000000000000000000000000000000000..aef8252aa53384abe4be3bea4ce1cc7eccc193b8 --- /dev/null +++ b/dockerfiles/pipeline/scripts/kubernetesDelete @@ -0,0 +1,4 @@ +#!/bin/bash +RELEASE_NAME=${CUSTOMER_NAME}-${APP_NAME}-${CI_ENVIRONMENT_SLUG} +echo "Delete $RELEASE_NAME" +helm3 uninstall "$RELEASE_NAME" --namespace "$NAMESPACE" diff --git a/dockerfiles/pipeline/scripts/kubernetesDeploy b/dockerfiles/pipeline/scripts/kubernetesDeploy new file mode 100644 index 0000000000000000000000000000000000000000..ae19c31c6a302966247d0974cefbba0d50f8fea3 --- /dev/null +++ b/dockerfiles/pipeline/scripts/kubernetesDeploy @@ -0,0 +1,57 @@ +#!/bin/bash + +echo "Deploy to kubernetes" +RELEASE_NAME=${CUSTOMER_NAME}-${APP_NAME}-${CI_ENVIRONMENT_SLUG} +echo "Release: $RELEASE_NAME" +echo "URL (canonical): $HOST_CANONICAL" +#helm3 init --client-only --stable-repo-url=https://charts.helm.sh/stable +helm3 chart pull $CI_REGISTRY/$HELM_GITLAB_CHART_PATH/$HELM_GITLAB_CHART_NAME:v$HELM_GITLAB_CHART_VERSION +helm3 chart export $CI_REGISTRY/$HELM_GITLAB_CHART_PATH/$HELM_GITLAB_CHART_NAME:v$HELM_GITLAB_CHART_VERSION -d /tmp/ +# use helmArgs defined in variables +helmArgsEvaluated=$(echo $helmArgs | envsubst) + +# this bash-brainfuck simply splits $helmArgs by newline to an array... +readarray -t helmArgsArray <<<"$helmArgsEvaluated" + +#Log the time until selfdestruct in case of review environment +if [[ ${ENV_SHORT} == "review" ]]; then echo "NOTE - This deployment will stop itself after 2 weeks"; fi + +# find all values files and generate an array out of it +files=(values.yml values-$ENV_SHORT.yml values-$CI_ENVIRONMENT_SLUG.yml) +values=() +for i in "${files[@]}"; do if [ -f $VALUES_PATH/$i ]; then values+=(--values $VALUES_PATH/$i); fi; done +echo "reading values files from $VALUES_PATH. found ${values[@]}" + +echo "doing helm upgrade" +helm3 upgrade --install "$RELEASE_NAME" /tmp/$HELM_GITLAB_CHART_NAME \ + --wait \ + --timeout 15m0s \ + --cleanup-on-fail \ + --set global.componentName="$KUBE_APP_NAME" \ + --set global.ENV_SHORT="$ENV_SHORT" \ + --set global.BUILD_ID="$BUILD_ID" \ + --set image.repository="$CI_REGISTRY_IMAGE" \ + --set image.pullSecret="$IMAGE_PULL_SECRET" \ + --set image.tag="$IMAGE_TAG" \ + --set image.pullPolicy=IfNotPresent \ + --set storybook.enabled=$STORYBOOK \ + --set application.replicas="$REPLICAS" \ + --set application.hostCanonical="$HOST_CANONICAL" \ + --set application.worker.enabled=$WORKER_ENABLED \ + --set mongodb.enabled=$MONGODB_ENABLED \ + --set mongodb.backup.enabled=$MONGODB_BACKUP_ENABLED \ + --set mongodb-replicaset.replicas=$MONGODB_REPLICAS \ + --set mongodb-replicaset.fullnameOverride=$KUBE_APP_NAME-mongodb-replicaset \ + --set mongodb-replicaset.persistentVolume.storageClass="$MONGODB_STORAGE_CLASS" \ + --set prisma.enabled=$PRISMA_ENABLED \ + --set gitlab.visualReviewEnabled="$GITLAB_VISUAL_REVIEW_ENABLED" \ + --set gitlab.app="$CI_PROJECT_PATH_SLUG" \ + --set gitlab.projectPath="$CI_PROJECT_PATH" \ + --set gitlab.mergeRequestId="$CI_MERGE_REQUEST_IID" \ + --set gitlab.projectId="$CI_PROJECT_ID" \ + --set gitlab.env="$CI_ENVIRONMENT_SLUG" \ + --set namespace="$NAMESPACE" \ + --namespace="$NAMESPACE" \ + ${helmArgsArray[@]} \ + ${values[@]} +echo "Deployment successful 😻" diff --git a/dockerfiles/pipeline/scripts/kubernetesEnsureNamespace b/dockerfiles/pipeline/scripts/kubernetesEnsureNamespace new file mode 100644 index 0000000000000000000000000000000000000000..9bbeb211acc84cd9dc8adbbe5b59ed39b763b25f --- /dev/null +++ b/dockerfiles/pipeline/scripts/kubernetesEnsureNamespace @@ -0,0 +1,3 @@ +#!/bin/bash +echo "Ensure Namespace $NAMESPACE" +kubectl describe namespace "$NAMESPACE" || kubectl create namespace "$NAMESPACE" diff --git a/helm-chart.yml b/helm-chart.yml index 4a3f1cd03420585b73b06613925e02c4a00776ce..dfb253d13b9d3c86947f29f52cbef1ba5889839b 100644 --- a/helm-chart.yml +++ b/helm-chart.yml @@ -30,5 +30,5 @@ release-helm: - tags create-release: - image: panterch/docker-ci-kubernetes-deploy + image: git.panter.ch:5001/catladder/gitlab-ci/pipeline extends: .create-release diff --git a/includes/docker-build.yml b/includes/docker-build.yml index bbc50e8c19ee4eea33844f2dc8c9130b61048464..fd380fc94f42413eb29720bf6a460d0caf11c9c1 100644 --- a/includes/docker-build.yml +++ b/includes/docker-build.yml @@ -1,3 +1,7 @@ +variables: + DOCKER_HOST: tcp://0.0.0.0:2375 + DOCKER_TLS_CERTDIR: "" + .ensureStorybookDockerfile: &ensureStorybookDockerfile | function ensureStorybookDockerfile() { @@ -14,22 +18,23 @@ } .docker-build-base: - image: docker:20 + image: git.panter.ch:5001/catladder/gitlab-ci/docker-build services: - name: docker:20-dind command: ["--tls=false"] # see https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27300#note_466755332 variables: DOCKER_DRIVER: overlay2 + IMAGE_NAME: $CI_REGISTRY_IMAGE .docker-build-app-base: extends: .docker-build-base script: - time docker login --username gitlab-ci-token --password $CI_JOB_TOKEN $CI_REGISTRY - - time docker pull $CI_REGISTRY_IMAGE:$CACHE_IMAGE_TAG || true - - time docker build --cache-from $CI_REGISTRY_IMAGE:$CACHE_IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$IMAGE_TAG $DOCKER_DIR - - time docker push $CI_REGISTRY_IMAGE:$IMAGE_TAG - - time docker tag $CI_REGISTRY_IMAGE:$IMAGE_TAG $CI_REGISTRY_IMAGE:$CACHE_IMAGE_TAG - - time docker push $CI_REGISTRY_IMAGE:$CACHE_IMAGE_TAG + - time docker pull $IMAGE_NAME:$CACHE_IMAGE_TAG || true + - time docker build --cache-from $IMAGE_NAME:$CACHE_IMAGE_TAG --tag $IMAGE_NAME:$IMAGE_TAG $DOCKER_DIR + - time docker push $IMAGE_NAME:$IMAGE_TAG + - time docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:$CACHE_IMAGE_TAG + - time docker push $IMAGE_NAME:$CACHE_IMAGE_TAG .docker-build-storybook-base: extends: .docker-build-base diff --git a/includes/kubernetes.yml b/includes/kubernetes.yml index 4f6ac6c22be225875eba91ef7cc5eb846414214c..36d78070bf19c291ce62755aa743b14c284bdfed 100644 --- a/includes/kubernetes.yml +++ b/includes/kubernetes.yml @@ -1,93 +1,8 @@ -.auto_devops: &auto_devops | - - function kubernetesEnsureNamespace() { - echo "Ensure Namespace $NAMESPACE" - kubectl describe namespace "$NAMESPACE" || kubectl create namespace "$NAMESPACE" - } - - function kubernetesCreateSecret() { - kubectl create secret -n "$NAMESPACE" \ - docker-registry $IMAGE_PULL_SECRET \ - --docker-server="$CI_REGISTRY" \ - --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \ - --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \ - --docker-email="$GITLAB_USER_EMAIL" \ - -o yaml --dry-run | kubectl replace -n "$NAMESPACE" --force -f - - } - - function kubernetesDeploy() { - - echo "Deploy to kubernetes" - RELEASE_NAME=${CUSTOMER_NAME}-${APP_NAME}-${CI_ENVIRONMENT_SLUG} - echo "Release: $RELEASE_NAME" - echo "URL (canonical): $HOST_CANONICAL" - #helm3 init --client-only --stable-repo-url=https://charts.helm.sh/stable - helm3 chart pull $CI_REGISTRY/$HELM_GITLAB_CHART_PATH/$HELM_GITLAB_CHART_NAME:v$HELM_GITLAB_CHART_VERSION - helm3 chart export $CI_REGISTRY/$HELM_GITLAB_CHART_PATH/$HELM_GITLAB_CHART_NAME:v$HELM_GITLAB_CHART_VERSION -d /tmp/ - # use helmArgs defined in variables - helmArgsEvaluated=$(echo $helmArgs | envsubst ) - - # this bash-brainfuck simply splits $helmArgs by newline to an array... - readarray -t helmArgsArray <<<"$helmArgsEvaluated" - - #Log the time until selfdestruct in case of review environment - if [[ ${ENV_SHORT} == "review" ]]; then echo "NOTE - This deployment will stop itself after 2 weeks"; fi - - # find all values files and generate an array out of it - files=(values.yml values-$ENV_SHORT.yml values-$CI_ENVIRONMENT_SLUG.yml) - values=() - for i in "${files[@]}"; do if [ -f $VALUES_PATH/$i ]; then values+=(--values $VALUES_PATH/$i); fi; done - echo "reading values files from $VALUES_PATH. found ${values[@]}" - - - - echo "doing helm upgrade" - helm3 upgrade --install "$RELEASE_NAME" /tmp/$HELM_GITLAB_CHART_NAME \ - --wait \ - --timeout 15m0s \ - --cleanup-on-fail \ - --set global.componentName="$KUBE_APP_NAME" \ - --set global.ENV_SHORT="$ENV_SHORT" \ - --set global.BUILD_ID="$BUILD_ID" \ - --set image.repository="$CI_REGISTRY_IMAGE" \ - --set image.pullSecret="$IMAGE_PULL_SECRET" \ - --set image.tag="$IMAGE_TAG" \ - --set image.pullPolicy=IfNotPresent \ - --set storybook.enabled=$STORYBOOK \ - --set application.replicas="$REPLICAS" \ - --set application.hostCanonical="$HOST_CANONICAL" \ - --set application.worker.enabled=$WORKER_ENABLED \ - --set mongodb.enabled=$MONGODB_ENABLED \ - --set mongodb.backup.enabled=$MONGODB_BACKUP_ENABLED \ - --set mongodb-replicaset.replicas=$MONGODB_REPLICAS \ - --set mongodb-replicaset.fullnameOverride=$KUBE_APP_NAME-mongodb-replicaset \ - --set mongodb-replicaset.persistentVolume.storageClass="$MONGODB_STORAGE_CLASS" \ - --set prisma.enabled=$PRISMA_ENABLED \ - --set gitlab.visualReviewEnabled="$GITLAB_VISUAL_REVIEW_ENABLED" \ - --set gitlab.app="$CI_PROJECT_PATH_SLUG" \ - --set gitlab.projectPath="$CI_PROJECT_PATH" \ - --set gitlab.mergeRequestId="$CI_MERGE_REQUEST_IID" \ - --set gitlab.projectId="$CI_PROJECT_ID" \ - --set gitlab.env="$CI_ENVIRONMENT_SLUG" \ - --set namespace="$NAMESPACE" \ - --namespace="$NAMESPACE" \ - ${helmArgsArray[@]} \ - ${values[@]} - echo "Deployment successful 😻" - } - - function kubernetesDelete() { - RELEASE_NAME=${CUSTOMER_NAME}-${APP_NAME}-${CI_ENVIRONMENT_SLUG} - echo "Delete $RELEASE_NAME" - helm3 uninstall "$RELEASE_NAME" --namespace "$NAMESPACE" - } - .deploy-to-kubernetes: extends: - .retry-default - .env-base script: - - *auto_devops - kubernetesEnsureNamespace - kubernetesCreateSecret - kubernetesDeploy @@ -108,7 +23,6 @@ kubernetes: namespace: "${CUSTOMER_NAME}-${APP_NAME}-${ENV_SHORT}" script: - - *auto_devops - kubernetesDelete .deploy-to-kubernetes-dev: diff --git a/meteor-kubernetes.yml b/meteor-kubernetes.yml index b90d4bccc59b9944bea1e20b2b5fc1e60dc17f06..74d9e97f6abf0a338cc527f81dc1448c27aa9d6f 100644 --- a/meteor-kubernetes.yml +++ b/meteor-kubernetes.yml @@ -20,54 +20,7 @@ app-build: - $CI_PROJECT_DIR/__build_info.json - $CI_PROJECT_DIR/bundle -.ensureDocker: &ensureDocker | - function ensureDockerfile() { - if [ ! -f .dockerignore ]; then - cat > .dockerignore < Dockerfile < .dockerignore < Dockerfile <