|
| 1 | +#!/bin/sh |
| 2 | +# Copyright (c) 2022 Gitpod GmbH. All rights reserved. |
| 3 | +# Licensed under the MIT License. See License-MIT.txt in the project root for license information. |
| 4 | + |
| 5 | +# shellcheck disable=SC2050,SC2153 |
| 6 | + |
| 7 | +set -e |
| 8 | + |
| 9 | +echo "Gitpod: Killing any in-progress installations" |
| 10 | + |
| 11 | +kubectl delete jobs.batch -n "${NAMESPACE}" -l component="gitpod-installer,cursor!=${CURSOR}" --force --grace-period 0 || true |
| 12 | +kubectl delete pod -n "${NAMESPACE}" -l component="gitpod-installer,cursor!=${CURSOR}" --force --grace-period 0 || true |
| 13 | + |
| 14 | +if [ "$(helm status -n "${NAMESPACE}" gitpod -o json | jq '.info.status == "deployed"')" = "false" ]; |
| 15 | +then |
| 16 | +echo "Gitpod: Deployment in-progress - clearing" |
| 17 | + |
| 18 | +VERSION="$(helm status -n "${NAMESPACE}" gitpod -o json | jq '.version')" |
| 19 | +if [ "${VERSION}" -le 1 ]; |
| 20 | +then |
| 21 | + echo "Gitpod: Uninstall application" |
| 22 | + helm uninstall -n "${NAMESPACE}" gitpod --wait || true |
| 23 | +else |
| 24 | + echo "Gitpod: Rolling back application" |
| 25 | + helm rollback -n "${NAMESPACE}" gitpod --wait || true |
| 26 | +fi |
| 27 | +fi |
| 28 | + |
| 29 | +echo "Gitpod: Generate the base Installer config" |
| 30 | +/app/installer init > "${CONFIG_FILE}" |
| 31 | + |
| 32 | +echo "Gitpod: auto-detecting ShiftFS support on host machine" |
| 33 | +kubectl wait job -n "${NAMESPACE}" --for=condition=complete -l component=shiftfs-module-loader --timeout=30s || true |
| 34 | +ENABLE_SHIFTFS=$(kubectl get jobs.batch -n "${NAMESPACE}" -l component=shiftfs-module-loader -o jsonpath='{.items[0].status.succeeded}') |
| 35 | + |
| 36 | +if [ "${ENABLE_SHIFTFS}" = "1" ]; then |
| 37 | + echo "Gitpod: enabling ShiftFS support" |
| 38 | + |
| 39 | + yq e -i '.workspace.runtime.fsShiftMethod = "shiftfs"' "${CONFIG_FILE}" |
| 40 | +fi |
| 41 | + |
| 42 | +echo "Gitpod: auto-detecting containerd location on host machine" |
| 43 | +if [ -d "/mnt/node0${CONTAINERD_DIR_K3S}" ]; then |
| 44 | + echo "Gitpod: containerd dir detected as k3s" |
| 45 | + |
| 46 | + yq e -i ".workspace.runtime.containerdRuntimeDir = \"${CONTAINERD_DIR_K3S}\"" "${CONFIG_FILE}" |
| 47 | +elif [ -d "/mnt/node0${CONTAINERD_DIR_AL}" ]; then |
| 48 | + echo "Gitpod: containerd dir detected as ${CONTAINERD_DIR_AL}" |
| 49 | + |
| 50 | + yq e -i ".workspace.runtime.containerdRuntimeDir = \"${CONTAINERD_DIR_AL}\"" "${CONFIG_FILE}" |
| 51 | +fi |
| 52 | + |
| 53 | +if [ -S "/mnt/node0${CONTAINERD_SOCKET_K3S}" ]; then |
| 54 | + echo "Gitpod: containerd socket detected as k3s" |
| 55 | + |
| 56 | + yq e -i ".workspace.runtime.containerdSocket = \"${CONTAINERD_SOCKET_K3S}\"" "${CONFIG_FILE}" |
| 57 | +elif [ -S "/mnt/node0${CONTAINERD_SOCKET_AL}" ]; then |
| 58 | + echo "Gitpod: containerd socket detected as ${CONTAINERD_SOCKET_AL}" |
| 59 | + |
| 60 | + yq e -i ".workspace.runtime.containerdSocket = \"${CONTAINERD_SOCKET_AL}\"" "${CONFIG_FILE}" |
| 61 | +fi |
| 62 | + |
| 63 | +echo "Gitpod: Inject the Replicated variables into the config" |
| 64 | +yq e -i ".domain = \"${DOMAIN}\"" "${CONFIG_FILE}" |
| 65 | +yq e -i '.license.kind = "secret"' "${CONFIG_FILE}" |
| 66 | +yq e -i '.license.name = "gitpod-license"' "${CONFIG_FILE}" |
| 67 | + |
| 68 | +if [ "${OPEN_VSX_URL}" != "" ]; |
| 69 | +then |
| 70 | + echo "Gitpod: Setting Open VSX Registry URL" |
| 71 | + yq e -i ".openVSX.url = \"${OPEN_VSX_URL}\"" "${CONFIG_FILE}" |
| 72 | +fi |
| 73 | + |
| 74 | +if [ "${DB_INCLUSTER_ENABLED}" = "0" ] && [ "${DB_CLOUDSQL_INSTANCE}" = "1" ]; |
| 75 | +then |
| 76 | + echo "Gitpod: configuring CloudSQLProxy" |
| 77 | + |
| 78 | + yq e -i ".database.inCluster = false" "${CONFIG_FILE}" |
| 79 | + yq e -i ".database.cloudSQL.instance = \"${DB_INCLUSTER_ENABLED}\"" "${CONFIG_FILE}" |
| 80 | + yq e -i ".database.cloudSQL.serviceAccount.kind = \"secret\"" "${CONFIG_FILE}" |
| 81 | + yq e -i ".database.cloudSQL.serviceAccount.name = \"cloudsql\"" "${CONFIG_FILE}" |
| 82 | +fi |
| 83 | + |
| 84 | +if [ "${DB_INCLUSTER_ENABLED}" = "0" ] && [ "${DB_CLOUDSQL_INSTANCE}" = "0" ]; |
| 85 | +then |
| 86 | + echo "Gitpod: configuring external database" |
| 87 | + |
| 88 | + yq e -i ".database.inCluster = false" "${CONFIG_FILE}" |
| 89 | + yq e -i ".database.external.certificate.kind = \"secret\"" "${CONFIG_FILE}" |
| 90 | + yq e -i ".database.external.certificate.name = \"database\"" "${CONFIG_FILE}" |
| 91 | +fi |
| 92 | + |
| 93 | +if [ "${HAS_LOCAL_REGISTRY}" = "true" ]; |
| 94 | +then |
| 95 | + echo "Gitpod: configuring mirrored container registry for airgapped installation" |
| 96 | + |
| 97 | + yq e -i ".repository = \"${LOCAL_REGISTRY_ADDRESS}\"" "${CONFIG_FILE}" |
| 98 | + yq e -i ".imagePullSecrets[0].kind = \"secret\"" "${CONFIG_FILE}" |
| 99 | + yq e -i ".imagePullSecrets[0].name = \"${IMAGE_PULL_SECRET_NAME}\"" "${CONFIG_FILE}" |
| 100 | + yq e -i '.dropImageRepo = true' "${CONFIG_FILE}" |
| 101 | + |
| 102 | + # Add the registry to the server allowlist - keep docker.io in case it's just using the mirrored registry functionality without being airgapped |
| 103 | + yq e -i ".containerRegistry.privateBaseImageAllowList += \"${LOCAL_REGISTRY_HOST}\"" "${CONFIG_FILE}" |
| 104 | + yq e -i ".containerRegistry.privateBaseImageAllowList += \"docker.io\"" "${CONFIG_FILE}" |
| 105 | +fi |
| 106 | + |
| 107 | +# Output the local registry secret - this is proxy.replicated.com if user hasn't set their own |
| 108 | +echo "${LOCAL_REGISTRY_IMAGE_PULL_SECRET}" | base64 -d > /tmp/kotsregistry.json |
| 109 | + |
| 110 | +if [ "${REG_INCLUSTER_ENABLED}" = "0" ]; |
| 111 | +then |
| 112 | + echo "Gitpod: configuring external container registry" |
| 113 | + |
| 114 | + # Get the external-container-registry secret so we can merge the external registry and KOTS registry keys |
| 115 | + kubectl get secret external-container-registry \ |
| 116 | + --namespace "${NAMESPACE}" \ |
| 117 | + -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d > /tmp/gitpodregistry.json |
| 118 | + |
| 119 | + cat /tmp/kotsregistry.json /tmp/gitpodregistry.json | jq -s '.[0] * .[1]' - - > /tmp/container-registry-secret |
| 120 | + |
| 121 | + echo "Gitpod: create the container-registry secret" |
| 122 | + kubectl create secret docker-registry container-registry \ |
| 123 | + --namespace "${NAMESPACE}" \ |
| 124 | + --from-file=.dockerconfigjson=/tmp/container-registry-secret \ |
| 125 | + -o yaml --dry-run=client | \ |
| 126 | + kubectl replace --namespace "${NAMESPACE}" --force -f - |
| 127 | + |
| 128 | + yq e -i ".containerRegistry.inCluster = false" "${CONFIG_FILE}" |
| 129 | + yq e -i ".containerRegistry.external.url = \"${REG_URL}\"" "${CONFIG_FILE}" |
| 130 | + yq e -i ".containerRegistry.external.certificate.kind = \"secret\"" "${CONFIG_FILE}" |
| 131 | + yq e -i ".containerRegistry.external.certificate.name = \"container-registry\"" "${CONFIG_FILE}" |
| 132 | +else |
| 133 | + if [ "${REG_INCLUSTER_STORAGE}" = "s3" ]; |
| 134 | + then |
| 135 | + echo "Gitpod: configuring container registry S3 backend" |
| 136 | + |
| 137 | + yq e -i ".containerRegistry.s3storage.region = \"${REG_INCLUSTER_STORAGE_S3_REGION}\"" "${CONFIG_FILE}" |
| 138 | + yq e -i ".containerRegistry.s3storage.endpoint = \"${REG_INCLUSTER_STORAGE_S3_ENDPOINT}\"" "${CONFIG_FILE}" |
| 139 | + yq e -i ".containerRegistry.s3storage.bucket = \"${REG_INCLUSTER_STORAGE_S3_BUCKETNAME}\"" "${CONFIG_FILE}" |
| 140 | + yq e -i ".containerRegistry.s3storage.certificate.kind = \"secret\"" "${CONFIG_FILE}" |
| 141 | + yq e -i ".containerRegistry.s3storage.certificate.name = \"container-registry-s3-backend\"" "${CONFIG_FILE}" |
| 142 | + fi |
| 143 | +fi |
| 144 | + |
| 145 | +if [ "${STORE_PROVIDER}" != "incluster" ]; |
| 146 | +then |
| 147 | + echo "Gitpod: configuring the storage" |
| 148 | + |
| 149 | + yq e -i ".metadata.region = \"${STORE_REGION}\"" "${CONFIG_FILE}" |
| 150 | + yq e -i ".objectStorage.inCluster = false" "${CONFIG_FILE}" |
| 151 | + |
| 152 | + if [ "${STORE_PROVIDER}" = "azure" ]; |
| 153 | + then |
| 154 | + echo "Gitpod: configuring storage for Azure" |
| 155 | + |
| 156 | + yq e -i ".objectStorage.azure.credentials.kind = \"secret\"" "${CONFIG_FILE}" |
| 157 | + yq e -i ".objectStorage.azure.credentials.name = \"storage-azure\"" "${CONFIG_FILE}" |
| 158 | + fi |
| 159 | + |
| 160 | + if [ "${STORE_PROVIDER}" = "gcp" ]; |
| 161 | + then |
| 162 | + echo "Gitpod: configuring storage for GCP" |
| 163 | + |
| 164 | + yq e -i ".objectStorage.cloudStorage.project = \"${STORE_GCP_PROJECT}\"" "${CONFIG_FILE}" |
| 165 | + yq e -i ".objectStorage.cloudStorage.serviceAccount.kind = \"secret\"" "${CONFIG_FILE}" |
| 166 | + yq e -i ".objectStorage.cloudStorage.serviceAccount.name = \"storage-gcp\"" "${CONFIG_FILE}" |
| 167 | + fi |
| 168 | + |
| 169 | + if [ "${STORE_PROVIDER}" = "s3" ]; |
| 170 | + then |
| 171 | + echo "Gitpod: configuring storage for S3" |
| 172 | + |
| 173 | + yq e -i ".objectStorage.s3.endpoint = \"${STORE_S3_ENDPOINT}\"" "${CONFIG_FILE}" |
| 174 | + yq e -i ".objectStorage.s3.bucket = \"${STORE_S3_BUCKET}\"" "${CONFIG_FILE}" |
| 175 | + yq e -i ".objectStorage.s3.credentials.kind = \"secret\"" "${CONFIG_FILE}" |
| 176 | + yq e -i ".objectStorage.s3.credentials.name = \"storage-s3\"" "${CONFIG_FILE}" |
| 177 | + fi |
| 178 | +fi |
| 179 | + |
| 180 | +if [ "${SSH_GATEWAY}" = "1" ]; |
| 181 | +then |
| 182 | + echo "Gitpod: Generate SSH host key" |
| 183 | + ssh-keygen -t rsa -q -N "" -f host.key |
| 184 | + kubectl create secret generic ssh-gateway-host-key --from-file=host.key -n "${NAMESPACE}" || echo "SSH Gateway Host Key secret has not been created. Does it exist already?" |
| 185 | + yq e -i '.sshGatewayHostKey.kind = "secret"' "${CONFIG_FILE}" |
| 186 | + yq e -i '.sshGatewayHostKey.name = "ssh-gateway-host-key"' "${CONFIG_FILE}" |
| 187 | +fi |
| 188 | + |
| 189 | +if [ "${TLS_SELF_SIGNED_ENABLED}" = "1" ]; |
| 190 | +then |
| 191 | + echo "Gitpod: Generating a self-signed certificate with the internal CA" |
| 192 | + yq e -i '.customCACert.kind = "secret"' "${CONFIG_FILE}" |
| 193 | + yq e -i '.customCACert.name = "ca-issuer-ca"' "${CONFIG_FILE}" |
| 194 | +elif [ "${TLS_SELF_SIGNED_ENABLED}" = "0" ] && [ "${CERT_MANAGER_ENABLED}" = "0" ] && [ "${TLS_CUSTOM_CA_CRT_ENABLED}" = "true" ]; |
| 195 | +then |
| 196 | + echo "Gitpod: Setting CA to be used for certificate" |
| 197 | + yq e -i '.customCACert.kind = "secret"' "${CONFIG_FILE}" |
| 198 | + yq e -i '.customCACert.name = "ca-certificate"' "${CONFIG_FILE}" |
| 199 | +fi |
| 200 | + |
| 201 | +if [ "${USER_MANAGEMENT_BLOCK_ENABLED}" = "1" ]; |
| 202 | +then |
| 203 | + echo "Gitpod: Adding blockNewUsers to config" |
| 204 | + yq e -i '.blockNewUsers.enabled = true' "${CONFIG_FILE}" |
| 205 | + |
| 206 | + for domain in ${USER_MANAGEMENT_BLOCK_PASSLIST} |
| 207 | + do |
| 208 | + echo "Gitpod: Adding domain \"${domain}\" to blockNewUsers config" |
| 209 | + yq e -i ".blockNewUsers.passlist += \"${domain}\"" "${CONFIG_FILE}" |
| 210 | + done |
| 211 | +fi |
| 212 | + |
| 213 | +if [ "${ADVANCED_MODE_ENABLED}" = "1" ]; |
| 214 | +then |
| 215 | + echo "Gitpod: Applying advanced configuration" |
| 216 | + |
| 217 | + if [ "${COMPONENT_PROXY_SERVICE_SERVICETYPE}" != "" ]; |
| 218 | + then |
| 219 | + # Empty string defaults to LoadBalancer. This maintains backwards compatibility with the deprecated experimental value |
| 220 | + echo "Gitpod: Applying Proxy service type" |
| 221 | + yq e -i ".components.proxy.service.serviceType = \"${COMPONENT_PROXY_SERVICE_SERVICETYPE}\"" "${CONFIG_FILE}" |
| 222 | + fi |
| 223 | + |
| 224 | + if [ -s "${CUSTOMIZATION_PATCH_FILE}" ]; |
| 225 | + then |
| 226 | + CUSTOMIZATION="$(base64 "${CUSTOMIZATION_PATCH_FILE}")" |
| 227 | + echo "Gitpod: Applying customization patch ${CUSTOMIZATION}" |
| 228 | + |
| 229 | + # Apply the customization property - if something else is set, this will be ignored |
| 230 | + yq e -i ".customization = $(echo "${CUSTOMIZATION}" | base64 -d | yq e -o json '.customization' - | jq -rc) // []" "${CONFIG_FILE}" |
| 231 | + fi |
| 232 | +else |
| 233 | + echo "Gitpod: No advanced configuration applied" |
| 234 | +fi |
| 235 | + |
| 236 | +echo "Gitpod: Update platform telemetry value" |
| 237 | +yq eval-all --inplace ".experimental.telemetry.data.platform = \"${DISTRIBUTION}\"" "${CONFIG_FILE}" |
| 238 | + |
| 239 | +echo "Gitpod: Patch Gitpod config" |
| 240 | +base64 -d "${CONFIG_PATCH_FILE}" > /tmp/patch.yaml |
| 241 | +config_patch=$(cat /tmp/patch.yaml) |
| 242 | +echo "Gitpod: ${CONFIG_PATCH_FILE}=${config_patch}" |
| 243 | +yq eval-all --inplace 'select(fileIndex == 0) * select(fileIndex == 1)' "${CONFIG_FILE}" /tmp/patch.yaml |
| 244 | + |
| 245 | +echo "Gitpod: Generate the Kubernetes objects" |
| 246 | +config=$(cat "${CONFIG_FILE}") |
| 247 | +echo "Gitpod: ${CONFIG_FILE}=${config}" |
| 248 | + |
| 249 | +echo "Gitpod: Create a Helm template directory" |
| 250 | +rm -Rf "${GITPOD_OBJECTS}" |
| 251 | +mkdir -p "${GITPOD_OBJECTS}/templates" |
| 252 | +cat <<EOF >> "${GITPOD_OBJECTS}/Chart.yaml" |
| 253 | +apiVersion: v2 |
| 254 | +name: gitpod-kots |
| 255 | +description: Always ready-to-code |
| 256 | +version: "1.0.0" |
| 257 | +appVersion: "$(/app/installer version | yq e '.version' -)" |
| 258 | +EOF |
| 259 | + |
| 260 | +echo "Gitpod: render Kubernetes manifests" |
| 261 | +/app/installer render -c "${CONFIG_FILE}" --namespace "${NAMESPACE}" --use-experimental-config > "${GITPOD_OBJECTS}/templates/gitpod.yaml" |
| 262 | + |
| 263 | +if [ "${REG_INCLUSTER_ENABLED}" = "1" ]; |
| 264 | +then |
| 265 | + echo "Gitpod: Add the local registry secret to the in-cluster registry secret" |
| 266 | + |
| 267 | + # Get the in-cluster registry secret |
| 268 | + yq eval-all '(select(.kind == "Secret" and .metadata.name == "builtin-registry-auth") | .data.".dockerconfigjson")' \ |
| 269 | + "${GITPOD_OBJECTS}/templates/gitpod.yaml" \ |
| 270 | + | base64 -d \ |
| 271 | + > /tmp/gitpodregistry.json |
| 272 | + |
| 273 | + REGISTRY_SECRET="$(cat /tmp/kotsregistry.json /tmp/gitpodregistry.json | jq -s '.[0] * .[1]' - - | base64 -w 0)" |
| 274 | + export REGISTRY_SECRET |
| 275 | + |
| 276 | + echo "Gitpod: update the in-cluster registry secret" |
| 277 | + yq eval-all --inplace '(select(.kind == "Secret" and .metadata.name == "builtin-registry-auth") | .data.".dockerconfigjson") |= env(REGISTRY_SECRET)' \ |
| 278 | + "${GITPOD_OBJECTS}/templates/gitpod.yaml" |
| 279 | +fi |
| 280 | + |
| 281 | +echo "Gitpod: Escape any Golang template values" |
| 282 | +# shellcheck disable=SC2016 |
| 283 | +sed -i -r 's/(.*\{\{.*)/{{`\1`}}/' "${GITPOD_OBJECTS}/templates/gitpod.yaml" |
| 284 | + |
| 285 | +# If certificate secret already exists, set the timeout to 5m |
| 286 | +CERT_SECRET=$(kubectl get secrets -n "${NAMESPACE}" https-certificates -o jsonpath='{.metadata.name}' || echo '') |
| 287 | +HELM_TIMEOUT="5m" |
| 288 | +if [ "${CERT_SECRET}" = "" ]; then |
| 289 | + HELM_TIMEOUT="1h" |
| 290 | +fi |
| 291 | + |
| 292 | +# The long timeout is to ensure the TLS cert is created (if required) |
| 293 | +echo "Gitpod: Apply the Kubernetes objects with timeout of ${HELM_TIMEOUT}" |
| 294 | +helm upgrade \ |
| 295 | + --atomic \ |
| 296 | + --cleanup-on-fail \ |
| 297 | + --create-namespace \ |
| 298 | + --install \ |
| 299 | + --namespace="${NAMESPACE}" \ |
| 300 | + --reset-values \ |
| 301 | + --timeout "${HELM_TIMEOUT}" \ |
| 302 | + --wait \ |
| 303 | + gitpod \ |
| 304 | + "${GITPOD_OBJECTS}" |
| 305 | + |
| 306 | +echo "Gitpod: Restarting installation status job" |
| 307 | +kubectl delete pod -n "${NAMESPACE}" -l component=gitpod-installer-status || true |
| 308 | + |
| 309 | +echo "Gitpod: Installer job finished - goodbye" |
0 commit comments