diff --git a/.gitignore b/.gitignore index 2de3b94..3d96f80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .env .kubeconfig* *.json +gitpod.yaml +gitpod-config.yaml diff --git a/Dockerfile b/Dockerfile index 3b449c6..82d17dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,7 @@ +ARG GITPOD_VERSION="main.1792" + +FROM eu.gcr.io/gitpod-core-dev/build/installer:$GITPOD_VERSION as installer + FROM alpine:3.14 RUN apk add --no-cache \ @@ -32,6 +36,8 @@ RUN gcloud components install alpha RUN curl -fsSL https://github.com/mikefarah/yq/releases/download/v4.12.2/yq_linux_amd64 -o /usr/local/bin/yq \ && chmod +x /usr/local/bin/yq +COPY --from=installer /app/installer /usr/local/bin/gitpod-installer + WORKDIR /gitpod COPY . /gitpod diff --git a/Makefile b/Makefile index 7733a12..7339746 100644 --- a/Makefile +++ b/Makefile @@ -9,15 +9,14 @@ build: ## Build docker image containing the required tools for the installation @docker build --squash --quiet . -t ${IMG} DOCKER_RUN_CMD = docker run -it \ - --pull always \ --volume $$HOME/.config/gcloud:/root/.config/gcloud \ - --volume $$HOME/.kube/config:/root/.kube/config \ + --volume $$HOME/.kube:/root/.kube \ --volume $$PWD:/gitpod \ ${IMG} $(1) install: ## Install Gitpod @echo "Starting install process..." - @test $(shell gcloud info --format="value(config.account)") || { echo "GCP dredentials do not exist. Run [gcloud auth login] to configure them"; exit 1; } + @test $(shell gcloud info --format="value(config.account)") || { echo "GCP credentials do not exist. Run [gcloud auth login] to configure them"; exit 1; } @$(call DOCKER_RUN_CMD, --install) uninstall: ## Uninstall Gitpod diff --git a/charts/assets/gitpod-values.yaml b/charts/assets/gitpod-values.yaml deleted file mode 100644 index d2b4652..0000000 --- a/charts/assets/gitpod-values.yaml +++ /dev/null @@ -1,398 +0,0 @@ -# Environment variables: -# - CONTAINER_REGISTRY_BUCKET -# - DOMAIN -# - GITPOD_VERSION -# - MYSQL_GITPOD_PASSWORD -# - MYSQL_INSTANCE_NAME -# - PROJECT_NAME -# - REGION - -hostname: $DOMAIN - -imagePrefix: eu.gcr.io/gitpod-core-dev/build/ -version: $GITPOD_VERSION - -installation: - region: $REGION - -certificatesSecret: - secretName: proxy-config-certificates - -installPodSecurityPolicies: true - -resources: - default: - cpu: 1m - memory: 256Mi - -workspaceSizing: - requests: - cpu: 1m - memory: 1.75Gi - storage: "" - limits: - cpu: "6" - memory: 12Gi - dynamic: - cpu: - buckets: - - budget: 144000 - limit: 600 - - budget: 144000 - limit: 400 - - budget: 54000 - limit: 200 - controlPeriod: 15m - samplingPeriod: 10s - -# default affinity for gitpod components -affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: gitpod.io/workload_services - operator: In - values: - - "true" - -db: - enabled: true - autoMigrate: true - port: 3306 - password: $MYSQL_GITPOD_PASSWORD - -# disable local mysql -mysql: - enabled: false -# do not install minio but configure AWS credentials -minio: - enabled: false - accessKey: "#" - secretKey: "#" - -# configure docker-registry to use GCS -docker-registry: - extraVolumeMounts: - - mountPath: /credentials - name: gcloud-creds - extraVolumes: - - name: gcloud-creds - secret: - secretName: remote-storage-gcloud - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: gitpod.io/workload_services - operator: In - values: - - "true" - replicas: 2 - enabled: true - fullnameOverride: registry - # Enable TLS - service: - port: 443 - tlsSecretName: "builtin-registry-certs" - storage: gcs - configData: - version: 0.1 - log: - accesslog: - disabled: true - level: info - formatter: json - storage: - gcs: - bucket: $CONTAINER_REGISTRY_BUCKET - keyfile: /credentials/key.json - rootdirectory: / - chunksize: 5242880 - cache: - blobdescriptor: inmemory - maintenance: - uploadpurging: - enabled: true - age: 168h - interval: 24h - dryrun: false - http: - addr: :5000 - debug: - addr: :5001 - prometheus: - enabled: true - path: /metrics - secret: phirDEX1torsyumiwnnuh6PRAU4hern - tls: - certificate: /etc/ssl/docker/tls.crt - key: /etc/ssl/docker/tls.key - minimumtls: tls1.2 - ciphersuites: - - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - headers: - X-Content-Type-Options: [nosniff] - health: - storagedriver: - enabled: true - interval: 10s - threshold: 3 - -rabbitmq: - readinessProbe: - enabled: false - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: gitpod.io/workload_services - operator: In - values: - - "true" - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - podAffinityTerm: - labelSelector: - matchLabels: - app.kubernetes.io/name: rabbitmq - topologyKey: kubernetes.io/hostname - weight: 1 - replicas: 2 - auth: - username: "gitpod" - password: "4B6e7m2QjpCgrz9DQT" - -defaults: - dnsPolicy: ClusterFirst - restartPolicy: Always - -tracing: - endoint: http://jaeger-collector:14268/api/traces - samplerType: const - samplerParam: "1" - -# TODO: empty array means no login. -authProviders: [] - -workspaceScheduler: default-scheduler - -components: - agentSmith: - disabled: true - - db: - gcloudSqlProxy: - enabled: true - instance: $PROJECT_NAME:$REGION:$MYSQL_INSTANCE_NAME - password: $MYSQL_GITPOD_PASSWORD - - dbMigrations: - enabled: true - - registryFacade: - hostname: $DOMAIN - daemonSet: true - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: gitpod.io/workload_workspaces - operator: In - values: - - "true" - server: - defaultFeatureFlags: [] - imageName: server - github: - app: {} - enableLocalApp: false - enableOAuthServer: true - blockNewUsers: false - blockNewUsersPasslist: [] - makeNewUsersAdmin: false - sessionMaxAgeMs: "28800000" # 8 hours - defaultBaseImageRegistryWhitelist: - - "https://index.docker.io/v1/" - incrementalPrebuilds: - commitHistory: 100 - repositoryPasslist: [] - wsman: [] - serverContainer: - env: - - name: ENABLE_PAYMENT - value: "false" - - workspace: - affinity: - prebuild: "gitpod.io/workload_workspaces" - probe: "gitpod.io/workload_workspaces" - default: "gitpod.io/workload_workspaces" - templates: - default: - spec: - #dnsConfig: - # nameservers: - # - 1.1.1.1 - # - 8.8.8.8 - #dnsPolicy: None # do NOT query against K8s DNS (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/) - env: - - name: THEIA_PREVENT_METADATA_ACCESS - value: true - regular: - spec: - containers: - - name: workspace - env: - - name: THEIA_RATELIMIT_LOG - value: "50" - prebuild: - spec: - containers: - - name: workspace - # Intended to reduce the density for prebuilds - resources: - limits: - cpu: "5" - memory: 12Gi - requests: - cpu: 1m - ephemeral-storage: 5Gi - memory: 4608Mi # = 2 * 2304Mi - - proxy: - replicas: 2 - ports: - http: - expose: true - containerPort: 80 - https: - expose: true - containerPort: 443 - certManager: - issuerName: gcloud-issuer - issuerKind: ClusterIssuer - serviceExternalTrafficPolicy: Local - serviceType: LoadBalancer - serviceAnnotations: - external-dns.alpha.kubernetes.io/hostname: $DOMAIN,*.$DOMAIN,*.ws.$DOMAIN - cloud.google.com/neg: '{"exposed_ports": {"80":{"name": "gitpod-proxy-http"},"443": {"name": "gitpod-proxy-https"}}}' - - wsManagerBridge: - defaultConfig: true - - wsDaemon: - hostWorkspaceArea: /mnt/workspaces - userNamespaces: - fsShift: fuse - shiftfsModuleLoader: - enabled: false - containerRuntime: - enabled: true - nodeRoots: - - /var/lib/containerd/io.containerd.runtime.v2.task/k8s.io - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: gitpod.io/workload_workspaces - operator: In - values: - - "true" - volumes: - - name: gcloud-creds - secret: - secretName: remote-storage-gcloud - volumeMounts: - - mountPath: /credentials - name: gcloud-creds - - contentService: - name: content-service - remoteStorage: - kind: gcloud - backupTrail: - enabled: true - maxLength: 2 - gcloud: - credentialsFile: /credentials/key.json - projectId: $PROJECT_NAME - region: $REGION - parallelUpload: 6 - volumes: - - name: gcloud-creds - secret: - secretName: remote-storage-gcloud - volumeMounts: - - mountPath: /credentials - name: gcloud-creds - - disabled: false - resources: - cpu: 100m - memory: 32Mi - ports: - rpc: - expose: true - containerPort: 8080 - metrics: - expose: false - containerPort: 9500 - - - wsScheduler: - name: ws-scheduler - disabled: true - scalerDisabled: true - - wsProxy: - name: ws-proxy - disabled: false - replicas: 2 - hostHeader: x-wsproxy-host - ports: - metrics: - expose: false - containerPort: 60095 - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: gitpod.io/workload_services - operator: In - values: - - "true" - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - podAffinityTerm: - labelSelector: - matchExpressions: - - key: component - operator: In - values: - - ws-proxy - topologyKey: kubernetes.io/hostname - weight: 100 - - wsManager: - volumes: - - name: gcloud-creds - secret: - secretName: remote-storage-gcloud - volumeMounts: - - mountPath: /credentials - name: gcloud-creds - -# configure default log level -log: - level: info - -cert-manager: - enabled: true - install: false diff --git a/charts/assets/issuer.yaml b/charts/assets/issuer.yaml index b1eb3a4..95375fa 100644 --- a/charts/assets/issuer.yaml +++ b/charts/assets/issuer.yaml @@ -1,7 +1,9 @@ +--- apiVersion: cert-manager.io/v1 -kind: ClusterIssuer +kind: Issuer metadata: - name: gcloud-issuer + name: gitpod-issuer + namespace: default spec: acme: email: $LETSENCRYPT_EMAIL @@ -15,3 +17,18 @@ spec: serviceAccountSecretRef: name: clouddns-dns01-solver-svc-acct key: key.json +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: https-certificates + namespace: default +spec: + secretName: https-certificates + issuerRef: + name: gitpod-issuer + kind: Issuer + dnsNames: + - $DOMAIN + - "*.$DOMAIN" + - "*.ws.$DOMAIN" diff --git a/charts/assets/jaeger-gitpod.yaml b/charts/assets/jaeger-gitpod.yaml deleted file mode 100644 index d30790c..0000000 --- a/charts/assets/jaeger-gitpod.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: jaegertracing.io/v1 -kind: Jaeger -metadata: - name: jaeger - namespace: default -spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: gitpod.io/workload_services - operator: In - values: - - "true" - strategy: allInOne - storage: - options: - memory: - max-traces: 500 - ingress: - enabled: false diff --git a/charts/assets/jaeger-values.yaml b/charts/assets/jaeger-values.yaml deleted file mode 100644 index 6d6bdaf..0000000 --- a/charts/assets/jaeger-values.yaml +++ /dev/null @@ -1,11 +0,0 @@ -rbac: - clusterRole: true -affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: gitpod.io/workload_services - operator: In - values: - - 'true' diff --git a/setup.sh b/setup.sh index 90be488..6fb6701 100755 --- a/setup.sh +++ b/setup.sh @@ -121,14 +121,13 @@ function create_service_account() { local ROLES=( "$@" ) if [ "$(gcloud iam service-accounts list --filter="displayName:${SA}" --format="value(displayName)" | grep "${SA}" || echo "empty")" == "${SA}" ]; then echo "IAM service account ${SA} already exists." - return + else + gcloud iam service-accounts create "${SA}" --display-name "${SA}" + for ROLE in ${ROLES[*]}; do + gcloud projects add-iam-policy-binding "${PROJECT_NAME}" \ + --member serviceAccount:"${EMAIL}" --role="${ROLE}" + done fi - - gcloud iam service-accounts create "${SA}" --display-name "${SA}" - for ROLE in ${ROLES[*]}; do - gcloud projects add-iam-policy-binding "${PROJECT_NAME}" \ - --member serviceAccount:"${EMAIL}" --role="${ROLE}" - done } function create_namespace() { @@ -210,25 +209,23 @@ function install_cert_manager() { function install_gitpod() { echo "Installing Gitpod..." - if ! kubectl get secret gcloud-sql-token >/dev/null 2>&1;then - kubectl create secret generic gcloud-sql-token --from-file=credentials.json="${DIR}/mysql-credentials.json" - fi - if ! kubectl get secret remote-storage-gcloud >/dev/null 2>&1;then - kubectl create secret generic remote-storage-gcloud --from-file=key.json="${DIR}/gs-credentials.json" - fi + local CONFIG_FILE="${DIR}/gitpod-config.yaml" - CONTAINER_REGISTRY_BUCKET="container-registry-${CLUSTER_NAME}-${PROJECT_ID}" - export CONTAINER_REGISTRY_BUCKET - # the bucket must exists before installing the docker-registry. - if ! gsutil acl get "gs://${CONTAINER_REGISTRY_BUCKET}" >/dev/null 2>&1;then - gsutil mb "gs://${CONTAINER_REGISTRY_BUCKET}" - fi + gitpod-installer init > "${CONFIG_FILE}" + + yq e -i ".domain = \"${DOMAIN}\"" "${CONFIG_FILE}" + yq e -i ".metadata.region = \"${REGION}\"" "${CONFIG_FILE}" + yq e -i '.workspace.runtime.containerdRuntimeDir = "/var/lib/containerd/io.containerd.runtime.v2.task/k8s.io"' "${CONFIG_FILE}" + + gitpod-installer \ + render \ + --config="${CONFIG_FILE}" > gitpod.yaml - envsubst < "${DIR}/charts/assets/gitpod-values.yaml" | helm upgrade --install gitpod gitpod/gitpod -f - + kubectl apply -f gitpod.yaml } -function service_account_exits() { +function service_account_exists() { local SA=$1 if [ "$(gcloud iam service-accounts list --filter="displayName:${SA}" --format="value(displayName)" | grep "${SA}" || echo "empty")" == "${SA}" ]; then return 0 @@ -257,13 +254,13 @@ function wait_for_load_balancer() { } function install() { + echo "Gitpod installer version: $(gitpod-installer version | jq -r '.version')" + check_prerequisites echo "Updating helm repositories..." - helm repo add jaegertracing https://jaegertracing.github.io/helm-charts helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add jetstack https://charts.jetstack.io - helm repo add gitpod https://aledbf.github.io/gitpod-chart-cleanup/ helm repo update gcloud config set project "${PROJECT_NAME}" @@ -287,7 +284,7 @@ function install() { gcloud services enable sqladmin.googleapis.com # Create service accounts - if service_account_exits "${GKE_SA}"; then + if service_account_exists "${GKE_SA}"; then echo "IAM service account ${GKE_SA} already exists." else local GKE_ROLES=( "roles/storage.admin" "roles/logging.logWriter" "roles/monitoring.metricWriter" "roles/container.admin") @@ -301,12 +298,15 @@ function install() { --iam-account "${MYSQL_SA_EMAIL}" "$DIR/mysql-credentials.json" fi - if service_account_exits "${OBJECT_STORAGE_SA}"; then + echo $OBJECT_STORAGE_SA + + if service_account_exists "${OBJECT_STORAGE_SA}"; then echo "IAM service account ${OBJECT_STORAGE_SA} already exists." else local OBJECT_STORAGE_ROLES=( "roles/storage.admin" "roles/storage.objectAdmin" ) create_service_account "${OBJECT_STORAGE_SA}" "${OBJECT_STORAGE_SA_EMAIL}" "${OBJECT_STORAGE_ROLES[@]}" fi + if [ ! -f "$DIR/gs-credentials.json" ]; then gcloud iam service-accounts keys create \ --iam-account "${OBJECT_STORAGE_SA_EMAIL}" "$DIR/gs-credentials.json" @@ -353,13 +353,13 @@ function install() { if [ "$(gcloud container node-pools list --cluster="${CLUSTER_NAME}" --region="${REGION}" --filter="name=${SERVICES_POOL}" --format="value(name)" | grep "${SERVICES_POOL}" || echo "empty")" == "${SERVICES_POOL}" ]; then echo "Node pool with name ${SERVICES_POOL} already exists in cluster ${CLUSTER_NAME}. Skip node-pool creation step."; else - create_node_pool "${SERVICES_POOL}" "gitpod.io/workload_services=true" + create_node_pool "${SERVICES_POOL}" "gitpod.io/workload_meta=true,gitpod.io/workload_ide=true" fi if [ "$(gcloud container node-pools list --cluster="${CLUSTER_NAME}" --region="${REGION}" --filter="name=${WORKSPACES_POOL}" --format="value(name)" | grep "${WORKSPACES_POOL}" || echo "empty")" == "${WORKSPACES_POOL}" ]; then echo "Node pool with name ${WORKSPACES_POOL} already exists in cluster ${CLUSTER_NAME}. Skip node-pool creation step."; else - create_node_pool "${WORKSPACES_POOL}" "gitpod.io/workload_workspaces=true" + create_node_pool "${WORKSPACES_POOL}" "gitpod.io/workload_workspace_services=true,gitpod.io/workload_workspace_regular=true,gitpod.io/workload_workspace_headless=true" fi if ! kubectl get clusterrolebinding cluster-admin-binding >/dev/null 2>&1; then @@ -369,27 +369,21 @@ function install() { --clusterrole=cluster-admin --user="$(gcloud config get-value core/account)" fi - # Create secret with container registry credentials - if [ -n "${IMAGE_PULL_SECRET_FILE}" ] && [ -f "${IMAGE_PULL_SECRET_FILE}" ]; then - if ! kubectl get secret gitpod-image-pull-secret; then - kubectl create secret generic gitpod-image-pull-secret \ - --from-file=.dockerconfigjson="${IMAGE_PULL_SECRET_FILE}" \ - --type=kubernetes.io/dockerconfigjson >/dev/null 2>&1 || true - fi - - yq e -i '.components.imageBuilderMk3.registry.secretName = "gitpod-image-pull-secret"' "${DIR}/charts/assets/gitpod-values.yaml" - else - # ensure the chart installation does not fail without a secret. - yq e -i '.components.imageBuilderMk3.registry = {}' "${DIR}/charts/assets/gitpod-values.yaml" - fi - install_cert_manager setup_managed_dns - setup_mysql_database - install_jaeger_operator +# setup_mysql_database install_gitpod wait_for_load_balancer + + # The load balancer wait clips message - extra line solves that + cat << EOF + +========================== +Gitpod is now installed on your cluster + +Please update your DNS record with the relevant nameserver. +EOF } function setup_kubectl() {