Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • varac/stackspin
  • xeruf/stackspin
  • stackspin/stackspin
3 results
Show changes
Commits on Source (4435)
Showing
with 1651 additions and 860 deletions
......@@ -12,9 +12,12 @@
/.direnv
.envrc
# Ignore files created during CI
# Ignore files created during CI or development
/test/group_vars/all/
/test/inventory*
/test/cypress/videos/
/test/cypress/screenshots/
/test/cypress/logs/
/clusters
/install/installation-kustomization/*.txt
......@@ -26,5 +29,11 @@ __pycache__
*.swp
*.pyc
# Personal taskfile(s)
Taskfile.yaml
# Documentation files
/docs/_build
# node modules for cypress
node_modules
This diff is collapsed.
---
workflow:
rules:
# Only run our pipelines for merge requests.
- if: $CI_MERGE_REQUEST_IID
# Or when the pipeline was scheduled (nightly
# run on main in particular).
- if: '$CI_PIPELINE_SOURCE == "schedule"'
include:
- local: .gitlab/ci_templates/stackspin_common.yml
stages:
- get-artifacts
- base-ready
- integration-test
# Stage: optional-apps-ready
# ================
#
# Check that the kustomizations of all installed apps are ready.
# Downloads the artifacts from the parent pipeline, used by all the other jobs
# in this pipeline. This needs to happen in a separate job, because all the
# other jobs are optional.
get-artifacts:
stage:
get-artifacts
# This job needs a script, otherwise GitLab CI lint will complain.
script:
- echo "This job only exists to get artifacts from the parent pipeline"
extends:
- .child_require_artifacts
- .report_artifacts
kustomization-ready:
extends:
- .kustomization_ready
rules:
- if: '$SKIP_KUSTOMIZATION_READY == "true"'
when: never
- when: always
cert:
stage: base-ready
script:
- cd test/
- pytest -v -s -m 'certs' --resource="$RESOURCE" --desired-revision "${STACKSPIN_BRANCH}@sha1:${STACKSPIN_COMMIT_SHA}" --reruns 120 --reruns-delay 10
interruptible: true
rules:
- if: '$SKIP_CERT == "true"'
when: never
- when: always
cypress:
stage: integration-test
image:
# https://hub.docker.com/r/cypress/included/tags
name: cypress/included:14.1.0
entrypoint: [""]
variables:
CYPRESS_USE_SSO_LOGIN: "true"
CYPRESS_SSO_USER: "admin@ci.stackspin.net"
CYPRESS_SSO_PW: "$SSO_PASSWORD"
CYPRESS_SSO_LOGOUT: "https://sso.$FQDN/oauth2/sessions/logout"
GIT_STRATEGY: "${INSTALL_GIT_STRATEGY}"
script:
- !reference [.checkout_branch, script]
- cd test
- npm install
- cypress run --record --key "$CYPRESS_RECORD_KEY" --spec "cypress/e2e/$RESOURCE.cy.js"
interruptible: true
rules:
- if: '$CYPRESS_TEST == "true"'
when: always
- when: never
helm-test:
stage: integration-test
image:
name: alpine/helm:3.17.1
entrypoint: ["/bin/sh", "-c"]
script:
- export KUBECONFIG="${PWD}/clusters/${HOSTNAME}/kube_config_cluster.yml"
- time helm test -n $HELM_RELEASE_NAMESPACE --logs --timeout 30m $HELM_RELEASE_NAME
interruptible: true
rules:
- if: '$HELM_TEST == "true"'
when: always
- when: never
---
workflow:
rules:
# Only run our pipelines for merge requests.
- if: $CI_MERGE_REQUEST_IID
# Or when the pipeline was scheduled (nightly
# run on main in particular).
- if: '$CI_PIPELINE_SOURCE == "schedule"'
include:
- /.gitlab/ci_templates/kaniko.yml
- /.gitlab/ci_templates/stackspin_common.yml
stages:
- build
- install-cluster
- install-stackspin
- apps-ready
- cluster-health
variables:
# Useful for debugging
# !!! CAREFUL - this will expose private keys in the CI logs !!!
# !!! Make sure to delete the resulting job output from the !!!
# !!! job's output site (Trash can symbol at the top) !!!
# CI_DEBUG_TRACE: "true"
# This decides which branch gets used by the create-vps and install-stackspin
# jobs. It also indirectly influences the commit sha that's checked for in the
# kustomization ready jobs
INSTALL_STACKSPIN_BRANCH: ${CI_COMMIT_REF_NAME}
# This pipeline uses the "fetch" (default) git strategy, but the
# "upgrade_test" pipeline needs the slower "clone" git strategy to be able to
# install an older version of Stackspin
INSTALL_GIT_STRATEGY: "fetch"
HOSTNAME: ${CI_COMMIT_REF_SLUG}
# Stage: build
# ============
#
# Builds CI test container image
# There are 2 moments in which we (re)build the container image. If some files are
# changed, or when the job is triggered with TRIGGER_JOBS.
ci-test-image-build:
stage: build
after_script:
- |
echo "CI_CONTAINER_TAG=${CI_COMMIT_REF_SLUG}" | tee .ci.env
artifacts:
paths:
- .ci.env
expire_in: 1 month
when: always
reports:
dotenv:
.ci.env
environment:
name: image/${CI_COMMIT_REF_SLUG}
url:
https://open.greenhost.net:4567/stackspin/stackspin/stackspin-ci:${CI_COMMIT_REF_SLUG}
auto_stop_in: 3 weeks
rules:
# Automatically rebuild the container image if this file, the Dockerfile,
# the installed requirements or the kaniko template change
- changes:
- Dockerfile
- requirements.txt
- .gitlab/ci_templates/kaniko.yml
# Also rebuild when the CI variable contain this jobs name
# or commit msg contains /TRIGGER_JOBS=.*ci-test-image-build/
- if: '$TRIGGER_JOBS =~ /ci-test-image-build/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*ci-test-image-build/'
extends:
- .kaniko_build
interruptible: true
report-ci-image-tag:
stage: build
image: "curlimages/curl"
script:
- |
TAG_INFORMATION=$(curl -sS https://open.greenhost.net/api/v4/projects/stackspin%2Fstackspin/registry/repositories/73/tags/${CI_COMMIT_REF_SLUG});
echo "Tag information: ${TAG_INFORMATION}"
if [ "$TAG_INFORMATION" == '{"message":"404 Tag Not Found"}' ]; then
CI_CONTAINER_TAG="$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
else
CI_CONTAINER_TAG="${CI_COMMIT_REF_SLUG}"
fi
echo "CI_CONTAINER_TAG=${CI_CONTAINER_TAG}" | tee .ci.env
artifacts:
paths:
- .ci.env
expire_in: 1 month
when: always
reports:
dotenv:
.ci.env
rules:
# Make sure this job does not run if ci-test-image-build runs
- changes:
- Dockerfile
- requirements.txt
- .gitlab/ci_templates/kaniko.yml
when: never # Never run on file changes that trigger ci-test-image-build
- if: '$TRIGGER_JOBS =~ /ci-test-image-build/'
when: never # Never run when ci-test-image is triggered manually
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*ci-test-image-build/'
when: never # Never run when ci-test-image is triggered manually
- when: always
interruptible: true
# Stage: install-cluster
# ======================
#
# * Creates the vps for the pipeline
# * Installs k8s with ansible
create-vps:
stage: install-cluster
variables:
SUBDOMAIN: "${HOSTNAME}.ci"
DOMAIN: "stackspin.net"
GIT_STRATEGY: "${INSTALL_GIT_STRATEGY}"
API_URL: "https://open.greenhost.net/api/v4"
script:
# Find out which apps will get installed so the create_vps.sh script
# can create a droplet with proper memory size.
# Then create a VPS based on a custom CI image for which the ansible playbook
# has already run.
- |
curl --silent --show-error --header "PRIVATE-TOKEN: $CREATE_VPS_TOKEN" "${API_URL}/projects/6/pipelines/${CI_PIPELINE_ID}/bridges" -o bridges.json
export ENABLED_APPS=$(jq -r '.[] | select(.name | contains("app-ready")) | .name' bridges.json | sed 's/-app-ready//' | tr '\n' ',' | sed 's/,$//')
# See CONTRIBUTING.md#ci-pipeline-image for more info
bash .gitlab/ci_scripts/create_vps.sh
extends:
- .ssh_setup
- .report_artifacts
- .general_rules
environment:
name: $CI_COMMIT_REF_SLUG
url: https://$FQDN
on_stop: terminate-droplet
auto_stop_in: 1 week
retry: 1
interruptible: true
test-dns:
stage: install-cluster
needs:
- job: create-vps
# Needs a pytest ansible connection to get the configured system resolvers
script:
- cd ansible/
- pytest -v -s -m 'dns' --connection=ansible --ansible-inventory=../${CLUSTER_DIR}/inventory.yml --hosts='ansible://*'
extends:
- .general_rules
interruptible: true
# Terminates a droplet and deletes the branch container image once the MR for it is merged
terminate-droplet:
# Stage has to be the same as the step that created the VPS
# https://docs.gitlab.com/ee/ci/environments.html#automatically-stopping-an-environment
stage: install-cluster
# Gets triggered by on_stop of create-vps job
when: manual
variables:
GIT_STRATEGY: none
before_script:
- echo "Default before script is overwritten because this job has no access to the Git repo"
script:
# Delete droplet
- python3 -c "import greenhost_cloud; greenhost_cloud.terminate_droplets_by_name(\"^${HOSTNAME}\")"
# Delete container image if one was created. Do not delete `main` nor `v*`.
- >
if [[ "$CI_CONTAINER_TAG" != "main" && ! "$CI_CONTAINER_TAG" =~ v\d+ ]]; then
curl --request DELETE --header "PRIVATE-TOKEN: ${CLEANER_TOKEN}" https://open.greenhost.net/api/v4/projects/stackspin%2Fstackspin/registry/repositories/73/tags/${CI_CONTAINER_TAG};
fi
# We have problems with cosmos not allowing deletion even though the droplet
# was just stopped.
retry: 2
environment:
name: $CI_COMMIT_REF_SLUG
action: stop
install-stackspin:
stage: install-stackspin
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
trigger:
include:
- local: .gitlab/ci_pipelines/install_stackspin.yml
strategy: depend
extends:
- .general_rules
# Stage: apps
# ================
#
# Check that the kustomizations of all installed apps are ready.
hedgedoc-app-ready:
stage: apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
RESOURCE: "hedgedoc"
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
CYPRESS_TEST: "true"
CYPRESS_BASE_URL: "https://hedgedoc.$FQDN"
extends:
- .hedgedoc_rules
- .trigger_apps_ready_pipeline
monitoring-app-ready:
stage: apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
RESOURCE: "monitoring"
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
CYPRESS_TEST: "true"
CYPRESS_BASE_URL: "https://grafana.$FQDN"
extends:
- .monitoring_rules
- .trigger_apps_ready_pipeline
nextcloud-app-ready:
stage: apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
RESOURCE: "nextcloud"
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
# Use `helm test` to run Cypress tests instead of directly in CI image
HELM_TEST: "true"
HELM_RELEASE_NAME: "nc"
HELM_RELEASE_NAMESPACE: "stackspin-apps"
extends:
- .nextcloud_rules
- .trigger_apps_ready_pipeline
velero-app-ready:
stage: apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
RESOURCE: "velero"
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
# Do not run `cert` and cypress tests, because we don't have them for velero
SKIP_CERT: "true"
extends:
- .velero_rules
- .trigger_apps_ready_pipeline
wekan-app-ready:
stage: apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
RESOURCE: "wekan"
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
CYPRESS_TEST: "true"
CYPRESS_BASE_URL: "https://wekan.$FQDN"
extends:
- .wekan_rules
- .trigger_apps_ready_pipeline
wordpress-app-ready:
stage: apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
RESOURCE: "wordpress"
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
CYPRESS_TEST: "true"
CYPRESS_BASE_URL: "https://www.$FQDN"
extends:
- .wordpress_rules
- .trigger_apps_ready_pipeline
zulip-app-ready:
stage: apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
RESOURCE: "zulip"
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
CYPRESS_TEST: "true"
CYPRESS_BASE_URL: "https://zulip.$FQDN"
extends:
- .zulip_rules
- .trigger_apps_ready_pipeline
single-sign-on-app-ready:
stage: apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
RESOURCE: "single-sign-on"
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
# kustomization-ready has already run as part of the base-ready in
# the install-stackspin pipeline
SKIP_KUSTOMIZATION_READY: "true"
extends:
- .general_rules
- .trigger_apps_ready_pipeline
dashboard-app-ready:
stage: apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: create-vps
RESOURCE: "dashboard"
STACKSPIN_BRANCH: $INSTALL_STACKSPIN_BRANCH
# kustomization-ready has already run as part of the base-ready in
# the install-stackspin pipeline
SKIP_KUSTOMIZATION_READY: "true"
HELM_TEST: "true"
HELM_RELEASE_NAME: "dashboard"
HELM_RELEASE_NAMESPACE: "stackspin"
extends:
- .general_rules
- .trigger_apps_ready_pipeline
# Stage: cluster-health
# =====================
#
# General cluster health checks
testinfra:
stage: cluster-health
extends:
- .testinfra
prometheus-alerts:
stage: cluster-health
extends:
- .prometheus_alerts
---
workflow:
rules:
# Only run our pipelines for merge requests.
- if: $CI_MERGE_REQUEST_IID
# Or when the pipeline was scheduled (nightly
# run on main in particular).
- if: '$CI_PIPELINE_SOURCE == "schedule"'
include:
- local: .gitlab/ci_templates/stackspin_common.yml
- local: .gitlab/ci_templates/base_ready.yml
stages:
- install-stackspin
- base-ready
- enable-apps
- configure-stackspin
# Stage: install-stackspin
# ========================
#
# Installs flux and stackspin with it
install-stackspin:
stage: install-stackspin
variables:
GIT_STRATEGY: "${INSTALL_GIT_STRATEGY}"
SSO_PASSWORD: "$SSO_PASSWORD"
script:
# For upgrade_test pipelines, we install an older version (latest stable)
- !reference [.checkout_branch, script]
# Customize env file, remove all comments and empty lines
- cp install/.flux.env.example ${CLUSTER_DIR}/.flux.env
- sed -i "s/1.2.3.4/$IP_ADDRESS/" ${CLUSTER_DIR}/.flux.env
# Set admin user email address
- sed -i "s/admin_email=admin@example.org/admin_email=admin@ci.stackspin.net/" ${CLUSTER_DIR}/.flux.env
# Delete comments
- sed -i "/^\s*#.*$/d; /^\s*$/d" ${CLUSTER_DIR}/.flux.env
# Disable outgoing mail
- sed -i "s/outgoing_mail_enabled=true/outgoing_mail_enabled=false/" ${CLUSTER_DIR}/.flux.env
# Some apps (looking at you Zulip) don't have a way to disable email, so we
# set the mail domain like this so we don't contact the Greenhost server
# anyway (with known wrong credentials).
- sed -i "s/outgoing_mail_smtp_host=.*/outgoing_mail_smtp_host=localhost/" ${CLUSTER_DIR}/.flux.env
- sed -i "s/example.org/$FQDN/" ${CLUSTER_DIR}/.flux.env
# Deploy secret/stackspin-cluster-variables
- cp install/kustomization.yaml ${CLUSTER_DIR}
- |
kubectl create namespace flux-system -o yaml --dry-run=client --save-config | kubectl apply -f -
kubectl create namespace cert-manager -o yaml --dry-run=client --save-config | kubectl apply -f -
kubectl create namespace stackspin -o yaml --dry-run=client --save-config | kubectl apply -f -
- kubectl apply -k ${CLUSTER_DIR}
# Add an override so cert-manager uses the SSL.com ClusterIssuer
- kubectl apply -f ./install/overrides/ci/stackspin-cert-manager-override.yaml
# TODO: this is only necessary for the upgrade pipeline between 2.6 and
# 2.7, and can be removed after 2.7 is released.
- |
if [ ! -e ./install/overrides/ci/stackspin-dashboard-override.yaml ]; then cat > ./install/overrides/ci/stackspin-dashboard-override.yaml <<EOF
---
apiVersion: v1
kind: ConfigMap
metadata:
namespace: stackspin
name: stackspin-dashboard-override
data:
values.yaml: |
EOF
fi
# Add an override to populate the dashboard helm test credentials.
- |
cat >> ./install/overrides/ci/stackspin-dashboard-override.yaml <<EOF
tests:
credentials:
user: 'admin@ci.stackspin.net'
password: '$SSO_PASSWORD'
EOF
- cat ./install/overrides/ci/stackspin-dashboard-override.yaml
- kubectl apply -f ./install/overrides/ci/stackspin-dashboard-override.yaml
# Install flux and general, non-app specific secrets
- ./install/install-stackspin.sh
extends:
- .general_rules
- .child_require_artifacts
- .report_artifacts
interruptible: true
.enable_app_template:
stage: enable-apps
needs:
- install-stackspin
- dashboard-kustomization-ready
# Make sure only one enable-app job runs against a given cluster at the same
# time.
# TODO: this actually doesn't work, not sure why
resource_group: $CI_ENVIRONMENT_NAME
variables:
GIT_STRATEGY: "${INSTALL_GIT_STRATEGY}"
before_script:
# For upgrade_test pipelines, we install an older version (latest stable)
- |
if [[ "$CI_COMMIT_REF_NAME" != "$STACKSPIN_BRANCH" ]]; then
# NOTE: this command will fail if GIT_STRATEGY is not set to "clone"
git checkout "$STACKSPIN_BRANCH"
fi
script:
# Add optional override values we need for the CI pipeline only
- >
[ -f ./install/overrides/ci/stackspin-${RESOURCE}-override.yaml ] &&
kubectl apply -f ./install/overrides/ci/stackspin-${RESOURCE}-override.yaml
- kubectl exec -n stackspin deploy/dashboard-backend -- flask cli app install ${RESOURCE}
interruptible: true
enable-monitoring:
variables:
RESOURCE: "monitoring"
extends:
- .enable_app_template
- .monitoring_rules
enable-nextcloud:
variables:
RESOURCE: "nextcloud"
before_script:
# First do the git checkout, otherwise that will refuse to override the
# changes we make to the override file.
- !reference [.enable_app_template, before_script]
# Add Cypress record key so screenshots/videos of helm test are uploaded to
# the Cypress dashboard
- |
cat >> ./install/overrides/ci/stackspin-nextcloud-override.yaml<< EOF
tests:
ssoLogin:
enabled: true
username: 'admin@ci.stackspin.net'
password: '$SSO_PASSWORD'
cypress:
projectId: '$CYPRESS_PROJECT_ID'
recordKey: '$CYPRESS_RECORD_KEY'
commitInfo:
branch: '$CI_COMMIT_REF_NAME'
message: |
$CI_COMMIT_TITLE
author: |
$CI_COMMIT_AUTHOR
sha: '$CI_COMMIT_SHORT_SHA'
EOF
extends:
- .enable_app_template
- .nextcloud_rules
enable-velero:
variables:
RESOURCE: "velero"
extends:
- .enable_app_template
- .velero_rules
enable-wekan:
variables:
RESOURCE: "wekan"
extends:
- .enable_app_template
- .wekan_rules
enable-wordpress:
variables:
RESOURCE: "wordpress"
extends:
- .enable_app_template
- .wordpress_rules
enable-zulip:
variables:
RESOURCE: "zulip"
extends:
- .enable_app_template
- .zulip_rules
enable-hedgedoc:
variables:
RESOURCE: "hedgedoc"
extends:
- .enable_app_template
- .hedgedoc_rules
# Stage: configure-stackspin
#
# Configure cluster after basic installation
# i.e. CI-related config like clusterIssuer
configure-sslcom-issuer:
stage: configure-stackspin
needs:
- cert-manager-kustomization-ready
- install-stackspin
script:
# Install custom ClusterIssuer for SSL.com production certificates
- bash ./.gitlab/ci_scripts/install_sslcom_issuer.sh
extends:
- .general_rules
interruptible: true
configure-zerossl-issuer:
stage: configure-stackspin
needs:
- cert-manager-kustomization-ready
- install-stackspin
script:
# Install custom ClusterIssuer for zerossl.com production certificates
- bash ./.gitlab/ci_scripts/install_zerossl_issuer.sh
extends:
- .general_rules
interruptible: true
# Set the password for the initial Stackspin admin user to a fixed value, for
# easy testing and debugging.
admin-user-password:
stage: configure-stackspin
needs:
- install-stackspin
- dashboard-kustomization-ready
script:
- kubectl exec -n stackspin deploy/dashboard-backend -- flask cli user setpassword admin@ci.stackspin.net $SSO_PASSWORD
---
# This pipeline first runs the whole `default.yml` pipeline, but instead of
# installing the newest version of Stackspin, it will install the latest stable
# (defined in the INSTALL_STACKSPIN_BRANCH variable).
#
# After the default pipeline is finished, the stackspin `GitRepository` object
# is patched to track the current branch and commit. Then, the apps and
# cluster-health jobs are run again, to make sure that the upgrade broke
# nothing.
include:
- /.gitlab/ci_pipelines/default.yml
- local: .gitlab/ci_templates/base_ready.yml
stages:
# First 5 stages are just the Default pipeline with different variables
- build
- install-cluster
- install-stackspin
- apps-ready
- cluster-health
# Upgrade the cluster to the current version
- upgrade-stackspin
# Run tests again
- base-ready
- upgraded-apps-ready
- upgrade-stackspin-health
# Overrides stackspin branch used for create-vps and install-stackspin jobs
variables:
INSTALL_STACKSPIN_BRANCH: v2
INSTALL_GIT_STRATEGY: clone
UPGRADE_STACKSPIN_BRANCH: $CI_COMMIT_REF_NAME
UPGRADE_STACKSPIN_COMMIT_SHA: $CI_COMMIT_SHA
# This variable is used by the `base_ready` template and the `app_tests` child
# pipelines after the upgrade has been applied. It is overridden in all jobs
# that rely on the INSTALL_STACKSPIN_BRANCH.
STACKSPIN_BRANCH: $UPGRADE_STACKSPIN_BRANCH
HOSTNAME: "upgrade-test"
CLUSTER_DIR: "clusters/upgrade-test"
# Increase disk size to 40G for upgrade test, see
# https://open.greenhost.net/stackspin/stackspin/-/issues/1271
# Outdated as we've grown the default disk size.
# DISK_SIZE: "40"
# override environment name for upgrade-test, otherwise it will be `main`
create-vps:
environment:
name: upgrade-test
terminate-droplet:
environment:
name: upgrade-test
upgrade-stackspin:
stage: upgrade-stackspin
# Make sure this has the create-vps artifacts
dependencies:
- create-vps
script:
# Change the `stackspin` GitRepo to switch to the new branch.
- kubectl patch -n flux-system gitrepository stackspin -p "{\"spec\":{\"ref\":{\"branch\":\"$UPGRADE_STACKSPIN_BRANCH\",\"commit\":\"$UPGRADE_STACKSPIN_COMMIT_SHA\"}}}" --type="merge"
# Make sure flux acts on that change right away, instead of waiting for the
# next nightly maintenance window to start upgrading.
- flux resume source git stackspin
# Set new STACKSPIN_COMMIT_SHA variable
- sed -i "s/STACKSPIN_COMMIT_SHA=.*/STACKSPIN_COMMIT_SHA=${UPGRADE_STACKSPIN_COMMIT_SHA}/" "${CLUSTER_DIR}/.cluster.env"
extends:
- .general_rules
- .report_artifacts
interruptible: true
# These are imported, but we have to override `dependencies` for them in the
# upgrade scenario, so that gitlab knows which exported environment (dotenv) to
# use for these jobs. We have two jobs that export the `cluster.env` file:
# first the create-vps one that first creates the cluster, but then later the
# upgrade-stackspin one above which changes the `STACKSPIN_COMMIT_SHA`. We need
# the latter for these jobs, because they should check that the new versions
# (after the upgrade) of the kustomizations are ready.
.kustomization_ready:
dependencies:
- upgrade-stackspin
upgraded-monitoring-app-ready:
stage: upgraded-apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: upgrade-stackspin
RESOURCE: "monitoring"
CYPRESS_TEST: "true"
CYPRESS_BASE_URL: "https://grafana.$FQDN"
extends:
- .monitoring_rules
- .trigger_apps_ready_pipeline
# Override the script in the report-ci-image-tag job from
# .gitlab/ci_pipelines/default.yml to always report current stable (`v2`) as
# the image tag.
report-ci-image-tag:
script:
- echo "CI_CONTAINER_TAG=v2" | tee .ci.env
# Similar to report-ci-image-tag: we override the script to use the `v2`
# container tag in pre-release pipelines.
ci-test-image-build:
script:
- echo "CI_CONTAINER_TAG=v2" | tee .ci.env
after_script:
- echo "Removed default after_script behavior"
environment: null
upgraded-nextcloud-app-ready:
stage: upgraded-apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: upgrade-stackspin
RESOURCE: "nextcloud"
extends:
- .nextcloud_rules
- .trigger_apps_ready_pipeline
upgraded-velero-app-ready:
stage: upgraded-apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: upgrade-stackspin
RESOURCE: "velero"
# Do not run `cert` and cypress tests, because we don't have them for velero
SKIP_CERT: "true"
extends:
- .velero_rules
- .trigger_apps_ready_pipeline
upgraded-wekan-app-ready:
stage: upgraded-apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: upgrade-stackspin
RESOURCE: "wekan"
extends:
- .wekan_rules
- .trigger_apps_ready_pipeline
upgraded-wordpress-app-ready:
stage: upgraded-apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: upgrade-stackspin
RESOURCE: "wordpress"
extends:
- .wordpress_rules
- .trigger_apps_ready_pipeline
upgraded-zulip-app-ready:
stage: upgraded-apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: upgrade-stackspin
RESOURCE: "zulip"
extends:
- .zulip_rules
- .trigger_apps_ready_pipeline
upgraded-hedgedoc-app-ready:
stage: upgraded-apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: upgrade-stackspin
RESOURCE: "hedgedoc"
extends:
- .hedgedoc_rules
- .trigger_apps_ready_pipeline
upgraded-single-sign-on-app-ready:
stage: upgraded-apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: upgrade-stackspin
RESOURCE: "single-sign-on"
# kustomization-ready has already run as part of the base-ready in
# the install-stackspin pipeline
SKIP_KUSTOMIZATION_READY: "true"
extends:
- .general_rules
- .trigger_apps_ready_pipeline
upgraded-dashboard-app-ready:
stage: upgraded-apps-ready
variables:
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
ARTIFACT_JOB: upgrade-stackspin
RESOURCE: "dashboard"
# kustomization-ready has already run as part of the base-ready in
# the install-stackspin pipeline
SKIP_KUSTOMIZATION_READY: "true"
extends:
- .general_rules
- .trigger_apps_ready_pipeline
# Stage: upgrade-cluster-health
# =====================
#
# Check if cluster is still healthy after upgrade
upgrade-testinfra:
stage: upgrade-stackspin-health
extends:
- .testinfra
upgrade-prometheus-alerts:
stage: upgrade-stackspin-health
extends:
- .prometheus_alerts
#!/usr/bin/env sh
# Check if cluster directory was available from cache
#!/usr/bin/env bash
set -ve
set -euo pipefail
mem_requirements() {
# Adds all mem requirements from all value configmaps of a given app
# This is not perfect but ok for a rough estimate
app="$1"
if [[ "$app" =~ (single-sign-on|dashboard) ]]; then
root_dir='flux2/core/base'
else
root_dir='flux2/apps'
fi
echo $(($(grep -A9999 'values.yaml' \
"$root_dir"/"$app"/*-values-configmap.yaml |
grep ' *requests:' -iA2 | grep ' *memory:' | cut -d':' -f2 | numfmt \
--from=auto | tr '\n' '+' | sed 's/$/0/')))
}
# Calculate droplet mem size
echo "ENABLED_APPS: $ENABLED_APPS"
total_apps_mem=0
IFS="," read -ra apps <<<"$ENABLED_APPS"
for app in "${apps[@]}"; do
app_mem=$(mem_requirements "$app")
total_apps_mem=$((total_apps_mem + app_mem))
echo "Memory request for $app: $(echo "$app_mem" | numfmt --to=iec-i)"
done
# Total mem utilization for a barebone stackspin cluster without any optional apps
# 2.8 GiB
total_core_mem=3006477107
total_mem=$((total_core_mem + total_apps_mem))
echo -e "\nMemory requests for core components: $(echo "$total_core_mem" | numfmt --to=iec-i)"
echo "Memory request for all additional apps: ""$(echo "$total_apps_mem" | numfmt --to=iec-i)"
echo "Total memory requests: ""$(echo "$total_mem" | numfmt --to=iec-i)"
total_mb=$((total_mem / 1024 / 1024))
echo "Creating a droplet with $total_mb MB."
# shellcheck disable=SC2039,SC3028
VPS_HOSTNAME="$HOSTNAME"
if [[ "$CI_COMMIT_REF_NAME" != "$INSTALL_STACKSPIN_BRANCH" ]]; then
# NOTE: this command will fail if GIT_STRATEGY is not set to "clone"
git checkout "$INSTALL_STACKSPIN_BRANCH"
fi
# Delete old machine if it still exists
echo "Deleting old machine"
python3 -c "import greenhost_cloud;
greenhost_cloud.terminate_droplets_by_name(\"^${VPS_HOSTNAME}$\")"
python3 -c "import greenhost_cloud; greenhost_cloud.terminate_droplets_by_name(\"^$VPS_HOSTNAME$\")"
echo "Creating new machine"
# Uses a custom disk image built with 1f2bee2 on 2021-10-19. See
# Uses a custom disk image built from commit
# 33ccc519e5d239395e94e655a3fbd5cfc3640a60 (around 2.12) on 2024-06-18. See
# CONTRIBUTING.md#ci-pipeline-image for more info.
python3 -m stackspin "$VPS_HOSTNAME" create \
--create-droplet "$DOMAIN" \
--create-hostname "$VPS_HOSTNAME" \
--ssh-key-id "$SSH_KEY_ID" \
--create-domain-records \
--subdomain "$SUBDOMAIN" \
--disk-image-id '-9102' \
args=(
--create-droplet "$DOMAIN"
--create-hostname "$VPS_HOSTNAME"
--ssh-key-id "$SSH_KEY_ID"
--mem "$total_mb"
--create-domain-records
--subdomain "$SUBDOMAIN"
--disk-image-id '-22353'
--truncate-subdomain
)
if [ "${DISK_SIZE:-}" != "" ]; then
args+=(--disk-size "$DISK_SIZE")
fi
python3 -m stackspin "$VPS_HOSTNAME" create "${args[@]}"
# Disabled for now, see https://open.greenhost.net/stackspin/stackspin/-/issues/1057
# --docker-mirror-server="${CI_DEPENDENCY_PROXY_SERVER}" \
# --docker-mirror-endpoint="${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}" \
# --docker-mirror-username="${CI_RUNNER_PERSONAL_ACCESS_USERNAME}" \
# --docker-mirror-password="${CI_RUNNER_PERSONAL_ACCESS_TOKEN}"
# Install Kubernetes, firewalle, etc.
python3 -m stackspin "$VPS_HOSTNAME" install
# Make sure .ci.env variables are not lost
cat .ci.env >>"$CLUSTER_DIR/.cluster.env"
# Save with which git commit we're installing Stackspin, so we can make sure
# our Kustomizations are up to date
echo "STACKSPIN_COMMIT_SHA=$(git rev-parse HEAD)" >>"$CLUSTER_DIR/.cluster.env"
#!/usr/bin/env bash
#
# Waits until cert-manager HelmRelease is ready, and then installs a SSL.com
# ClusterIssuer with our credentials into the cert-manager namespace
#
# Usage:
#
# ./install_sslcom_issuer.sh
set -euo pipefail
# Create secret with HMAC key
b64secret=$(echo -n "${SSL_COM_EAB_HMAC_KEY}" | base64 -w0)
# Add SSL.com ClusterIssuer
kubectl apply -n cert-manager -f - <<EOF
---
apiVersion: v1
kind: Secret
metadata:
namespace: cert-manager
name: sslcom-eabsecret
data:
secret: ${b64secret}
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: sslcom-issuer
spec:
acme:
# The email address used for signing up with ssl.com
email: ${SSL_COM_EMAIL_ADDRESS}
# The ACME server URL
server: https://acme.ssl.com/sslcom-dv-rsa
externalAccountBinding:
keyID: ${SSL_COM_EAB_KID}
keySecretRef:
name: sslcom-eabsecret
key: secret
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: sslcom-prod
solvers:
- http01:
ingress:
class: nginx
EOF
#!/usr/bin/env bash
#
# Waits until cert-manager HelmRelease is ready, and then installs a ZeroSSL
# ClusterIssuer with our credentials into the cert-manager namespace
# Installs a ClusterIssuer for zerossl.com with our credentials.
#
# Usage:
#
......@@ -9,28 +8,34 @@
set -euo pipefail
# Create secret with HMAC key
kubectl -n cert-manager create secret generic zerossl-eabsecret --from-literal "secret=${ZEROSSL_EAB_HMAC_KEY}"
b64secret=$(echo -n "${ZEROSSL_EAB_HMAC_KEY}" | base64 -w0)
# Wait until cert-manager is ready
"$(dirname "$0")/retry_cmd_until_success.sh" 30 10 "flux get helmrelease -n cert-manager --status-selector ready=true --no-header | grep cert-manager"
# Add ZeroSSL ClusterIssuer
# Add SSL.com ClusterIssuer
kubectl apply -n cert-manager -f - <<EOF
---
apiVersion: v1
kind: Secret
metadata:
namespace: cert-manager
name: zerossl-eabsecret
data:
secret: ${b64secret}
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: zerossl-issuer
spec:
acme:
# The email address used for signing up with zerossl.com
email: ${ZEROSSL_EMAIL_ADDRESS}
# The ACME server URL
server: https://acme.zerossl.com/v2/DV90
server: "https://acme.zerossl.com/v2/DV90"
externalAccountBinding:
keyID: ${ZEROSSL_EAB_KID}
keySecretRef:
name: zerossl-eabsecret
key: secret
keyAlgorithm: HS256
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: zerossl-prod
......
#!/usr/bin/env sh
echo "Env vars:"
echo
env | grep -E '^(HOSTNAME|CLUSTER_NAME|FQDN|IP_ADDRESS|CLUSTER_DIR|ANSIBLE_HOST_KEY_CHECKING|KANIKO_BUILD_IMAGENAME|SSH_KEY_ID|SHELL|CI_PROJECT_DIR|STACKSPIN_BRANCH|STACKSPIN_COMMIT_SHA)='
echo
echo "Uptime: $(uptime)"
echo "KANIKO build image ref: ${CI_REGISTRY_IMAGE}/${KANIKO_BUILD_IMAGENAME}:${CI_CONTAINER_TAG}"
echo
if [ -f .ci.env ]; then
echo "Content of .ci.env:"
cat .ci.env
fi
if [ -f .cluster.env ]; then
echo "Content of .cluster.env:"
cat .cluster.env
fi
---
# Stage: base-ready
# ====================
#
# Test if base kustomizations are ready, before configuration can get applied
# that makes use of CRDs, i.e. clusterIssuer
cert-manager-kustomization-ready:
variables:
RESOURCE: "cert-manager"
extends:
- .kustomization_ready
dashboard-kustomization-ready:
variables:
RESOURCE: "dashboard"
extends:
- .kustomization_ready
letsencrypt-issuer-kustomization-ready:
variables:
RESOURCE: "letsencrypt-issuer"
extends:
- .kustomization_ready
local-path-provisioner-kustomization-ready:
variables:
RESOURCE: "local-path-provisioner"
extends:
- .kustomization_ready
metallb-kustomization-ready:
variables:
RESOURCE: "metallb"
extends:
- .kustomization_ready
namespaces-kustomization-ready:
variables:
RESOURCE: "namespaces"
extends:
- .kustomization_ready
nginx-kustomization-ready:
variables:
RESOURCE: "nginx"
extends:
- .kustomization_ready
single-sign-on-kustomization-ready:
variables:
RESOURCE: "single-sign-on"
extends:
- .kustomization_ready
sources-kustomization-ready:
variables:
RESOURCE: "sources"
extends:
- .kustomization_ready
stackspin-kustomization-ready:
variables:
RESOURCE: "stackspin"
extends:
- .kustomization_ready
system-upgrade-config-ready:
variables:
RESOURCE: "system-upgrade-config"
extends:
- .kustomization_ready
system-upgrade-controller-ready:
variables:
RESOURCE: "system-upgrade-controller"
extends:
- .kustomization_ready
dummy-job:
stage: dummy
script:
- echo "This is a dummy job which does nothing else but triggers a pipeline"
- echo "nonetheless, useful for situations where CI rules would not trigger"
- echo "a pipeline automatically, i.e. changes to renovate.json"
- echo "(see https://open.greenhost.net/stackspin/wordpress-helm/-/merge_requests/127#note_38728)"
when: manual
# Include this file if you want to package your helm chart to a helm registry.
# You'll need to set two variables:
#
# 1. CHART_NAME: the name of the helm chart. Should correspond to the name in
# Chart.yaml
# 2. CHART_DIR (optional): the directory where your helm chart is in you
# repository. HAS TO END WITH A SLASH if you choose to override it.
variables:
CHART_DIR: ""
.chart_release_rules:
rules:
- changes:
- ${CHART_DIR}Chart.yaml
.chart_changes_rules:
rules:
- changes:
- ${CHART_DIR}templates/**/*
- ${CHART_DIR}values-local.yaml.example
- ${CHART_DIR}values.yaml
- ${CHART_DIR}Chart.yaml
lint-helm:
stage: lint-helm-chart
image:
name: alpine/helm:3.17.1
entrypoint: ["/bin/sh", "-c"]
script:
- cd ${CHART_DIR:-"."}
- helm dep update
- helm lint .
artifacts:
paths:
- '${CHART_DIR}charts/**'
expire_in: 1 week
# Even if lint fails, upload the charts/ folder as artifact
when: always
extends:
- .chart_changes_rules
package-chart:
stage: package-helm-chart
image:
name: alpine/helm:3.17.1
entrypoint: ["/bin/sh", "-c"]
script:
- cd ${CHART_DIR:-"."}
- helm package .
artifacts:
paths:
- ${CHART_DIR}${CHART_NAME}-*
expire_in: 1 week
extends:
- .chart_release_rules
# Push helm chart. Charts on the default branch are pushed to `stable`, others
# are pushed to the `unstable` channel.
release-helm:
image: "rancher/curlimages-curl:7.73.0"
stage: release-helm-chart
script:
- cd ${CHART_DIR:-"."}
- if [ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ] || [ -n "$CI_COMMIT_TAG" ]; then export HELM_CHANNEL='stable'; else export HELM_CHANNEL='unstable'; fi
- export CHART_FILE=$(ls ${CHART_NAME}-*.tgz)
- curl --fail --request POST --user gitlab-ci-token:$CI_JOB_TOKEN --form "chart=@${CHART_FILE}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/api/${HELM_CHANNEL}/charts"
- echo "Chart '${CHART_FILE}' published to helm repository '${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/api/${HELM_CHANNEL}/charts'"
extends:
- .chart_release_rules
......@@ -17,7 +17,8 @@
image:
# We need a shell to provide the registry credentials, so we need to use the
# kaniko debug image (https://github.com/GoogleContainerTools/kaniko#debug-image)
name: gcr.io/kaniko-project/executor:v1.6.0-debug
# https://console.cloud.google.com/gcr/images/kaniko-project/global/executor
name: gcr.io/kaniko-project/executor:v1.23.2-debug
entrypoint: [""]
script:
- mkdir -p /kaniko/.docker/
......
.ssh_setup:
before_script:
- mkdir ~/.ssh
- echo -e 'Host *\n stricthostkeychecking no' > ~/.ssh/config
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
# General rules
# =============
#
# Rules that enable the cluster to be built and are applied to most steps
# (except for application-specific steps)
.general_rules:
rules:
- changes:
- .gitlab-ci.yml
- .gitlab/ci_scripts/*
- .gitlab/ci_templates/*
- .gitlab/ci_pipelines/*
- Dockerfile
- ansible/**/*
- flux/**/*
- flux2/**/*
- install/**/*
- test/**/*
- stackspin/**/*
- requirements.txt
- VERSION
- CHANGELOG.md
- if: '$TRIGGER_JOBS =~ /enable-.*/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*enable-/'
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
# app rules
#
# Define the rules when/if app specific jobs are run.
# Just add the variable RESOURCE to the job like this:
# variables:
# RESOURCE: "nextcloud"
# and import the templates with i.e.
# extends: .nextcloud_rules
# .nextcloud_rules will ensure that the job is only executed:
# - when files related to the app changed in the repo
# - A pipeline gets started from the UI and the job name is included in the
# CI variable `TRIGGER_JOBS`
# - A commit is pushed containing the pattern TRIGGER_JOBS=.*<job name>
# (i.e. TRIGGER_JOBS=ci-test-image-build,enable-nextcloud)
#
# Gitlab CI allows pushing CI vars via `git push` but a bug prevents this when
# using merge request pipelines (see https://gitlab.com/gitlab-org/gitlab/-/issues/326098)
.monitoring_rules:
rules:
- changes:
- flux2/apps/monitoring/*.yaml
- flux2/cluster/optional/monitoring/*.yaml
- flux2/core/base/sources/grafana-helmrepository.yaml
- flux2/core/base/sources/wikimedia-helmrepository.yaml
- flux2/core/base/sources/prometheus-community-helmrepository.yaml
- flux2/config/monitoring/*.yaml
# Install and test if we change anything about SSO.
- flux2/core/base/single-sign-on/*.yaml
- install/install-app.sh
- install/flux-version-check.sh
- test/cypress/e2e/monitoring.cy.js
- .gitlab/ci_pipelines/apps_ready.yml
- if: '$TRIGGER_JOBS =~ /enable-monitoring/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*enable-monitoring/'
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
.nextcloud_rules:
rules:
- changes:
- flux2/apps/$RESOURCE/*.yaml
- flux2/cluster/optional/$RESOURCE/*.yaml
- flux2/core/base/sources/nextcloud-helmrepository.yaml
# Install and test if we change anything about SSO.
- flux2/core/base/single-sign-on/*.yaml
- install/install-app.sh
- install/flux-version-check.sh
- .gitlab/ci_pipelines/apps_ready.yml
- if: '$TRIGGER_JOBS =~ /enable-nextcloud/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*enable-nextcloud/'
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
.single_sign_on_rules:
rules:
- changes:
- flux2/core/base/$RESOURCE/*.yaml
- flux2/infrastructure/sources/single-sign-on-helmrepository.yaml
- install/install-stackspin.sh
- .gitlab/ci_pipelines/apps_ready.yml
- if: '$TRIGGER_JOBS =~ /enable-single-sign-on/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*enable-single-sign-on/'
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
.velero_rules:
rules:
- changes:
- flux2/apps/$RESOURCE/*.yaml
- flux2/cluster/optional/$RESOURCE/*.yaml
- flux2/core/base/sources/vmware-tanzu-helmrepository.yaml
- install/install-app.sh
- install/flux-version-check.sh
- .gitlab/ci_pipelines/apps_ready.yml
- if: '$TRIGGER_JOBS =~ /enable-velero/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*enable-velero/'
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
.wekan_rules:
rules:
- changes:
- flux2/apps/$RESOURCE/*.yaml
- flux2/cluster/optional/$RESOURCE/*.yaml
- flux2/core/base/sources/wekan-helmrepository.yaml
# Install and test if we change anything about SSO.
- flux2/core/base/single-sign-on/*.yaml
- install/install-app.sh
- install/flux-version-check.sh
- test/cypress/e2e/wekan.cy.js
- .gitlab/ci_pipelines/apps_ready.yml
- if: '$TRIGGER_JOBS =~ /enable-wekan/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*enable-wekan/'
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
.wordpress_rules:
rules:
- changes:
- flux2/apps/$RESOURCE/*.yaml
- flux2/cluster/optional/$RESOURCE/*.yaml
- flux2/core/base/sources/wordpress-helmrepository.yaml
# Install and test if we change anything about SSO.
- flux2/core/base/single-sign-on/*.yaml
- install/install-app.sh
- install/flux-version-check.sh
- test/cypress/e2e/wordpress.cy.js
- .gitlab/ci_pipelines/apps_ready.yml
- if: '$TRIGGER_JOBS =~ /enable-wordpress/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*enable-wordpress/'
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
.zulip_rules:
rules:
- changes:
- flux2/apps/$RESOURCE/*.yaml
- flux2/cluster/optional/$RESOURCE/*.yaml
- flux2/core/base/sources/zulip-helmrepository.yaml
# Install and test if we change anything about SSO.
- flux2/core/base/single-sign-on/*.yaml
- install/install-app.sh
- install/flux-version-check.sh
- test/cypress/e2e/zulip.cy.js
- .gitlab/ci_pipelines/apps_ready.yml
- if: '$TRIGGER_JOBS =~ /enable-zulip/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*enable-zulip/'
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
.hedgedoc_rules:
rules:
- changes:
- flux2/apps/$RESOURCE/*.yaml
- flux2/cluster/optional/$RESOURCE/*.yaml
- flux2/core/base/sources/hedgedoc-helmrepository.yaml
# Install and test if we change anything about SSO.
- flux2/core/base/single-sign-on/*.yaml
- install/install-app.sh
- install/flux-version-check.sh
- test/cypress/e2e/hedgedoc.cy.js
- .gitlab/ci_pipelines/apps_ready.yml
- if: '$TRIGGER_JOBS =~ /enable-hedgedoc/'
- if: '$CI_COMMIT_MESSAGE =~ /TRIGGER_JOBS=.*enable-hedgedoc/'
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
# Common variables
# ================
#
# Note on variables: some variables are defined in the .cluster.env file
# generated by the `create-vps` job.
variables:
SSH_KEY_ID: "411"
ANSIBLE_HOST_KEY_CHECKING: "False"
KANIKO_BUILD_IMAGENAME: "stackspin-ci"
CLUSTER_DIR: "clusters/${CI_COMMIT_REF_SLUG}"
default:
image: "${CI_REGISTRY_IMAGE}/${KANIKO_BUILD_IMAGENAME}:${CI_CONTAINER_TAG}"
before_script:
- sh .gitlab/ci_scripts/print_debug_info.sh
# Common job definitions
# ======================
#
# Re-usable job definitions
.kustomization_ready:
stage: base-ready
script:
- cd test/
- export KUBECONFIG="${PWD}/../clusters/${HOSTNAME}/kube_config_cluster.yml"
- pytest -v -s -m 'kustomizations' --resource="$RESOURCE" --desired-revision "${STACKSPIN_BRANCH}@sha1:${STACKSPIN_COMMIT_SHA}" --reruns 120 --reruns-delay 20
extends:
- .general_rules
interruptible: true
# The dotenv report requires us to report the artifacts in every job that is
# required with a `needs:` from another job.
.report_artifacts:
artifacts:
paths:
- clusters
expire_in: 1 month
when: always
reports:
dotenv:
$CLUSTER_DIR/.cluster.env
.ssh_setup:
before_script:
- bash .gitlab/ci_scripts/print_debug_info.sh
- mkdir ~/.ssh
- echo -e 'Host *\n stricthostkeychecking no' > ~/.ssh/config
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
# Get create-vps artifacts in a child pipeline. Requires you to set a
# PARENT_PIPELINE_ID from the parent pipeline
.child_require_artifacts:
needs:
- pipeline: $PARENT_PIPELINE_ID
job: $ARTIFACT_JOB
.require_artifacts:
dependencies:
- create-vps
.trigger_apps_ready_pipeline:
trigger:
include:
- local: .gitlab/ci_pipelines/apps_ready.yml
strategy: depend
.testinfra:
script:
- cd ansible/
- pytest -v -s -m 'testinfra' --connection=ansible --ansible-inventory=../${CLUSTER_DIR}/inventory.yml --hosts='ansible://*'
extends:
- .ssh_setup
- .general_rules
- .require_artifacts
interruptible: true
.prometheus_alerts:
variables:
# RESOURCE var is used in job specific rules (i.e. ..monitoring_rules)
RESOURCE: "kube-prometheus-stack"
script:
- export BASIC_AUTH_PW=$(python3 -m stackspin $HOSTNAME secrets | grep stackspin-prometheus-basic-auth | awk '{ print $4 }')
- cd test/
- bash ../.gitlab/ci_scripts/retry_cmd_until_success.sh 30 10 pytest -s -m prometheus
extends:
- .monitoring_rules
- .require_artifacts
interruptible: true
# Checkout older stackspin branch
# used i.e. in upgrade pipelines where an older version branch of Stackspin
# (defined by the STACKSPIN_BRANCH env var)
# is installed and tested before an upgrade to the current version is performed
.checkout_branch: &checkout_branch
script:
- |
if [[ "$CI_COMMIT_REF_NAME" != "$STACKSPIN_BRANCH" ]]; then
# NOTE: this command will fail if GIT_STRATEGY is not set to "clone"
git checkout "$STACKSPIN_BRANCH"
fi
......@@ -4,12 +4,15 @@
# Additional CI directives
#
# Trigger upgrade-test pipeline:
# UPGRADE_TEST
#
# Include apps to install even there's no code change that would
# trigger an atomated installation, i.e.:
# TRIGGER_JOBS=enable-nextcloud
#
# or trigger all jobs:
# TRIGGER_JOBS=enable-monitoring,enable-nextcloud,enable-single-sign-on,enable-wekan,enable-wordpress,enable-zulip
# TRIGGER_JOBS=enable-monitoring,enable-nextcloud,enable-velero,enable-wekan,enable-wordpress,enable-zulip
#
# Reference issue number with one of:
#
......
## Preparation
1. [ ] Received necessary information
- [ ] SMTP login credentials
- [ ] Domain
- [ ] List of requested applications
- [ ] Customization (custom subdomains, etc.)
- [ ] IP address of VPS
- [ ] Access to VPS
- [ ] Email address of the first admin
2. [ ] DNS is configured, one of:
- [ ] Wildcard CNAME `*.{domain}` & A record for `{domain}`, or
- [ ] Domains for all the requested apps
## Installation
3. [ ] Steps from [create
cluster](https://docs.stackspin.net/en/v2/installation/create_cluster.html)
finished
4. [ ] Edited `.flux.env`
5. [ ] Steps from [Install
Stackspin](https://docs.stackspin.net/en/v2/installation/install_stackspin.html)
finished
6. [ ] Installed apps
7. [ ] Copied cluster folder from `./clusters/<client>` into `cluster-configs` repository
## Test
8. [ ] Confirmed all `kustomizations` and `helmreleases` are `Ready`.
9. [ ] Confirmed apps are reachable through requested domains
## Communication
10. [ ] Communicated admin user credentials?
11. [ ] Sent the following email:
- [ ] Replaced `{customer}` with customer name
- [ ] Replaced `{me}` with my name
- [ ] Replaced `{domain}` with right domain (and changed any custom (sub)domains)
- [ ] Removed apps that were not installed
> Dear `{customer}`,
>
> I have installed Stackspin and created an admin account for you.
> You can set a password for your account by going to the following link:
>
> https://dashboard.{domain}/web/recovery
>
> There, you have to enter your email address
> (the one I'm sending this email to)
> and then you'll be emailed another link
> which allows you to set the password you use to log in.
>
> After setting that password you can use it to log in to:
>
> - The dashboard at: https://dashboard.{domain}
>
> - The Zulip hosted at: https://talk.prototypefund.de
>
> - Nextcloud (file storage and sharing, password storage and a calendar)
> at https://files.{domain}
>
> - WordPress (Website CMS) at https://www.{domain}
>
> - Wekan ("Kanban" planning board) at https://wekan.{domain}
>
> Feel free to contact us if you have any questions.
>
> Kind regards,
> `{me}`
> Stackspin
# Integrate new app
# Integrate a new app
Based on Wekan as a Stackspin app example.
## Flux
* [ ] Create new source if needed in `flux2/infrastructure/sources/APP.yaml`
* [ ] Include `APP.yaml` in `flux2/infrastructure/sources/kustomization.yaml`
* [ ] In case of a core app, add healthChecks to `core` `Kustomization`
(`flux2/cluster/base/core.yaml`)
* [ ] Add app secret: `install/templates/stackspin-APP-variables.yaml.jinja`
* [ ] Flux kustomization: `flux2/cluster/optional/APP/APP.yaml`
* [ ] K8s kustomization: `flux2/apps/APP/kustomization.yaml`
* [ ] If needed, add PVCs in `flux2/apps/APP/pvc.yaml`
* [ ] Add `HelmRelease` in `flux2/apps/APP/release.yaml`
* Mem resource requests/limits: See https://open.greenhost.net/stackspin/stackspin/-/issues/1027
* [ ] mem request: the median weekly memory usage of an app
* [ ] mem limit: 150% of the weekly max memory usage
### Single sign-on
### Cluster config
* [ ] Flux kustomization: `flux2/cluster/optional/APP/APP.yaml` (Example: `flux2/cluster/optional/wekan/wekan.yaml`)
### Source (helmRepository / gitRepository)
* [ ] Create new `helmRepository` if needed in `flux2/core/base/sources/APP-helmrepository.yaml` (Example: `flux2/core/base/sources/wekan-helmrepository.yaml`)
* [ ] Include `APP-helmrepository.yaml` in `flux2/core/base/sources/kustomization.yaml`
### App config
* [ ] Add app secrets template (in `dashboard` repo): `backend/areas/apps/templates/stackspin-APP-variables.yaml.jinja` (Example: `backend/areas/apps/templates/stackspin-wekan-variables.yaml.jinja`)
* [ ] If the app is storing state to disk, add PVCs in `flux2/apps/APP/pvc.yaml` (Example: `flux2/apps/wekan/pvc.yaml`)
* [ ] Helm chart values configmap: `flux2/apps/APP/APP-values-configmap.yaml` (Example: `flux2/apps/wekan/wekan-values-configmap.yaml`)
* [ ] `HelmRelease` in `flux2/apps/APP/release.yaml` (Example: `flux2/apps/wekan/release.yaml`)
* [ ] Create a kustomization config map for your app that sets the app subdomain by adding an entry to `flux2/core/base/migration-scripts/create-kustomization-variables-configmaps-script-configmap.yaml`
* [ ] Add your app in the right places in the dashboard source, in particular including an app icon.
#### Single sign-on
* Integrate the new app into the single sign-on system
* [ ] Add OAuth client secret to `install/templates/stackspin-oauth-variables.yaml.jinja`
* In `flux2/core/base/single-sign-on/release.yaml`:
* [ ] Add app `userbackend.applications`
* [ ] Add app to `oAuthClients`
* [ ] Configure app OIDC settings in `HelmRelease` `flux2/apps/APP/release.yaml`
* [ ] Disable user/pw login if possible
* [ ] Admin-login should grant admin privileges
* [ ] Non-admin should not grant admin privileges
* [ ] Oauth client resource: `flux2/apps/APP/APP-oauth-client.yaml` (Example: `flux2/apps/wekan/wekan-oauth-client.yaml`)
* [ ] Configure app OIDC settings, probably via its helm values: `flux2/apps/APP/APP-values-configmap.yaml` (Example: `flux2/apps/wekan/wekan-values-configmap.yaml`)
* [ ] Add the app to the list of dashboard apps and oauthclients:
- in `flux2/core/base/dashboard/dashboard-apps-configmap.yaml` if this app will be part of the default Stackspin app set.
- If you have a self-managed cluster please create new configmaps `stackspin-apps-custom` and `stackspin-oauthclients-custom` with the same structure as the official ones.
* [ ] Disable user/pw login if possible (#881)
* Test SSO:
* [ ] Admin login should grant admin privileges
* [ ] Non-admin login should not grant admin privileges
#### Backup/restore
This applies if the app has any persistent storage that needs to be part of
backups.
* [ ] Add the label `stackspin.net/backupSet: "APP"` to some kubernetes
resources. This label is used by Velero when instructed to restore a single
app. Typically you should add it to:
* [ ] the pvc(s) in `flux2/apps/APP/pvc*.yaml` (Example: `flux2/apps/wekan/pvc.yaml`)
* [ ] any pod(s) that use those pvc(s); this would go in the chart's helm
values configmap, with the value typically called `podLabels`, or if it doesn't have
that maybe `commonLabels`: `flux2/apps/APP/APP-values-configmap.yaml` (Example: `flux2/apps/wekan/wekan-values-configmap.yaml`)
* [ ] the kubernetes objects controlling those pods, typically a deployment
(`deploymentLabels` or `commonLabels`) or statefulset (`statefulSetLabels`
or `commonLabels`).
* [ ] To the same pods, i.e., the ones that use the pvcs that need to be
backed up, add an annotation `backup.velero.io/backup-volumes: "volume-name"`,
where `volume-name` is the name of the volume internal to the pod kubernetes
object, as shown for example in `kubectl describe pod` output.
* [ ] Add app-specific backup/restore instructions to `docs/maintenance.rst` if
necessary.
### Etc
* [ ] Add app to `dump_secrets()` in `stackspin/cluster.py`
If you made it until here you have completed all necessary steps for adding a
custom app to your cluster.
If you intend to contribute to Stackspin by a new app merge request please
continue and follow the rest of the steps below.
## Tests
* [ ] Make sure testing app resources work (`test/pytest/test_resources.py`)
* [ ] Make sure testing app cert works (`test/pytest/test_certs.py`)
* [ ] Add Taiko test (`tests/taiko`)
* [ ] Add automatic tests for your app. We use the cypress framework for this.
See `test/cypress/e2e` for the existing tests.
## CI
* Add the following elements to `.gitlab-ci.yml`:
* [ ] `.APP-rules` partial
* [ ] `enable-APP` job
* [ ] `APP-kustomization-ready` job
* [ ] `APP-cert` job
* [ ] `APP-taiko` test job
* Add the following elements:
* [ ] `.APP-rules` partial to `.gitlab/ci_templates/stackspin_common.yml`
* [ ] `enable-APP` job to `.gitlab/ci_pipelines/install_stackspin.yml`
* [ ] `APP-app-ready` job to `.gitlab/ci_pipelines/default.yml`
* [ ] `upgraded-APP-app-ready` job to `.gitlab/ci_pipelines/upgrade_test.yml`
* [ ] an entry to `app_subdomains` in `test/pytest/test_certs.py`
## Renovatebot
* [ ] Make sure the needed `HelmRelease` fields for renovatebot are in place and
order, i.e.
```
# renovate: registryUrl=https://helm-charts.wikimedia.org/stable/
chart: eventrouter
version: 0.3.6
```
**Note**: the order of these lines is important for renovatebot's regular expression to match them.
For adding the registryUrl automatically, you could use the
[renovate-helm-releases](https://github.com/k8s-at-home/renovate-helm-releases)
script:
```
git clone https://github.com/k8s-at-home/renovate-helm-releases /tmp/renovate-helm-releases
/tmp/renovate.py --cluster-path flux2
```
* [ ] Make sure that latest [renovate pipeline](https://open.greenhost.net/stackspin/renovate/-/pipelines)
checks for app updates **after the new app is merged into the main branch**
## Documentation
* Add app to:
* [ ] `docs/installation_instructions.rst`
* [ ] `docs/testing_instructions.rst`
* [ ] `docs/usage.rst`
* [ ] `Step 3: Install additional applications` in `docs/installation/install_stackspin.rst`
* [ ] `docs/installation/testing.rst`
* [ ] `docs/system_administration/migrating.rst`
* [ ] `docs/usage/applications.rst`
## Demo instance
* [ ] Install the app on `demo.stackspin.net` so it can be previewed by anyone.
* [ ] Update the nightly reset script for the demo instance, on `cli.stackspin.net`,
in `/srv/stackspin/clusters/demo.stackspin.net/custom-scripts/vars.sh`.
## Follow-up issues
Create follow-up issue with:
* [ ] Fine-tune CPU and mem limits (https://open.greenhost.net/stackspin/stackspin/-/issues/1027)
* [ ] Limit settings (<https://blog.kubecost.com/blog/requests-and-limits/#our-solution>)
* [ ] Set CPU request
* CPU limits are unset.
* Set CPU request to average CPU usage.
* [ ] Set memory requests and limits
* Set memory request to:
```
1.5 * avg(quantile_over_time(.99,container_memory_working_set_bytes{container_name!="POD",container_name!=""}[7d])) by (container_name,pod_name,namespace)`
```
* Limit set to `2 * request`
- [ ] Remove elevated permissions from Gitlab account on `open.stackspin.net`.
- Disable/remove Stackspin accounts on `stackspin.net`:
- [ ] Remove Stackspin account in Stackspin dashboard.
- [ ] Deactivate user in Zulip.
- [ ] Disable user in Nextcloud.
- [ ] Disable user in Wordpress (change role to “no role for this site”).
- [ ] For developers/ops: remove from Signal group "Stackspin escalation".
- [ ] Remove email forwards from `info@stackspin.net`, `admin@stackspin.net`.
- [ ] Remove personal ceph object storage credentials if present.