Skip to content

[kots]: add the KOTS installation manifests #8395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
/components/image-builder-bob @gitpod-io/engineering-workspace
/components/image-builder-mk3 @gitpod-io/engineering-workspace
/components/installation-telemetry @gitpod-io/engineering-self-hosted
/install @gitpod-io/engineering-self-hosted
/install/installer @gitpod-io/engineering-self-hosted
/install/installer/pkg/components/agent-smith @gitpod-io/engineering-workspace
/install/installer/pkg/components/blobserve @gitpod-io/engineering-workspace
Expand Down
6 changes: 6 additions & 0 deletions install/kots/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.DS_Store
Thumbs.db
.idea
charts/*/charts
charts/*/Chart.lock
*.tgz
20 changes: 20 additions & 0 deletions install/kots/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
CHANNEL_STABLE = Stable
CHANNEL_BETA = Beta
CHANNEL_UNSTABLE = Unstable
YAML_DIR = manifests

all: helm lint create_unstable_release

create_unstable_release:
replicated release create --lint --ensure-channel --yaml-dir ${YAML_DIR} --promote ${CHANNEL_UNSTABLE}
.PHONY: create_unstable_release

lint:
replicated release lint --yaml-dir ${YAML_DIR}
.PHONY: lint

helm:
@echo "Installing Helm dependencies"
@rm -f manifests/*.tgz
@for f in $(shell ls -d charts/*/); do cd $${f} && helm dep up && helm package . --destination ../../manifests && cd -; done
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this helm magic, couldn't we just download the cert-manager package like this?

$ curl -sSLO https://charts.jetstack.io/charts/cert-manager-v1.7.0.tgz

Or wouldn't this work for some reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would work, but I've done it as a general function in case we ever put a second Helm chart in there - we use external-dns in all the guides which might be worth offering in Kots in future

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's awesome that we have a generic solution for this problem at hand. To be honest, I would prefer the simple curl command, for now, to keep it simple. With that, we wouldn't need to add (and maintain) helm to the workspace image right now.

I know it's not easy to part with such an elegant, generic solution once you've poured it into code. However, this still exists in the archived gitpod-io/replicated repo and we can release it from there at any time as soon as we need it. You're not forgotten you beautiful code. We remember you! Promise! 👋

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh @corneliusludmann, you'll make me blush 😊

Again, as I said in the .gitpod.yml comment - I treat these as a proposition and then we all talk about it. I'm happy with switching to a simpler solution.

Copy link
Contributor Author

@mrsimonemms mrsimonemms Mar 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I think that my original approach is the correct one. Using the .tgz file provided by cert-manager has everything nested in a cert-manager folder. For KOTS to work, it needs to be in the root of the .tgz file - try pulling in the curl command you used and then run make lint to see the error.

So, that makes there two ways of achieving this:

  1. create a noddy Chart.yaml file and use the helm package function
  2. download the .tgz file, extract it, re-compress it with the cert-manager and use that new .tgz file

Whilst both have their limitations, I think approach 1 is the simpler and more maintainable one. We could just put the correct .tgz file in there, but I think that this might be forgotten when we come to do an update in future

Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. When a simple curl does not work, using helm seems to be the better approach.

.PHONY: helm
48 changes: 48 additions & 0 deletions install/kots/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# KOTS

[Kubernetes Off-The-Shelf(KOTS)](https://kots.io/) is how we deliver
Gitpod to enterprise customers.

# Getting started

You will need:
- a Kubernetes cluster
- a [Replicated](https://vendor.replicated.com) license file

Go to [our Replicated channels page](https://vendor.replicated.com/apps/gitpod/channels) and
follow the installation instructions on screen.

# Terminology

KOTS is the technology which is used to deliver a Replicated installation. Generally,
KOTS should refer to the underlying open source technology and Replicated is the
commercially supported project.

# Development

## Authentication

Two environment variables are required to be able to publish to our Replicated account:

- `REPLICATED_APP`: the unique application slug
- `REPLICATED_API_TOKEN`: a [User API Token](https://vendor.replicated.com/account-settings) with `Read/Write` permissions

## Naming conventions

- Starts with `kots` - part of the KOTS configuration. Typically, this will follow the KOTS documentation/conventions
- Starts with `gitpod` - part of the Gitpod application. Typically, this will be something we define/own
- Starts with `helm` - a Helm chart
- Starts with `crd` - a Custom Resource Definition

## Helm charts

KOTS [requires](https://kots.io/reference/v1beta1/helmchart) Helm charts to be uploaded as a `.tgz`
file. The `make helm` command iterates through everything inside `charts`, installs the dependencies
and packages them up as a `.tgz` file.

The `.tgz` files should not be committed to the repository.

## Create an unstable release

An unstable release can be created by running `make create_unstable_release`. This builds and publishes
a new unstable release to the account. This can be then applied to your development cluster.
8 changes: 8 additions & 0 deletions install/kots/charts/cert-manager/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v2
description: Gitpod cert-manager
name: cert-manager
version: 1.7.0
dependencies:
- name: cert-manager
version: 1.7.0
repository: https://charts.jetstack.io
4,180 changes: 4,180 additions & 0 deletions install/kots/manifests/crd-cert-manager.yaml

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions install/kots/manifests/gitpod-certificate-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: v1
kind: Secret
metadata:
name: https-certificates
annotations:
kots.io/when: '{{repl ConfigOptionEquals "cert_manager_enabled" "0" }}'
type: kubernetes.io/tls
data:
tls.crt: '{{repl ConfigOption "tls_crt" }}'
tls.key: '{{repl ConfigOption "tls_key" }}'
15 changes: 15 additions & 0 deletions install/kots/manifests/gitpod-certificate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: https-certificates
annotations:
kots.io/when: '{{repl ConfigOptionEquals "cert_manager_enabled" "1" }}'
spec:
secretName: https-certificates
issuerRef:
name: '{{repl if (ConfigOptionEquals "cert_manager_provider" "incluster" ) }}ca-issuer{{repl else }}gitpod-issuer{{repl end }}'
kind: '{{repl if (ConfigOptionEquals "cert_manager_provider" "azure") }}ClusterIssuer{{repl else }}Issuer{{repl end }}'
dnsNames:
- '{{repl ConfigOption "domain" }}'
- '*.{{repl ConfigOption "domain" }}'
- '*.ws.{{repl ConfigOption "domain" }}'
11 changes: 11 additions & 0 deletions install/kots/manifests/gitpod-cloudsql-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: Secret
metadata:
name: cloudsql
annotations:
kots.io/when: '{{repl and (ConfigOptionEquals "db_incluster" "0") (ConfigOptionEquals "db_cloudsql_enabled" "1") }}'
data:
credentials.json: '{{repl ConfigOption "db_gcp_credentials" }}'
encryptionKeys: '{{repl ConfigOption "db_encryption_keys" | Base64Encode }}'
password: '{{repl ConfigOption "db_password" | Base64Encode }}'
username: '{{repl ConfigOption "db_username" | Base64Encode }}'
15 changes: 15 additions & 0 deletions install/kots/manifests/gitpod-clusterrolebinding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: installer
labels:
app: gitpod
component: gitpod-installer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: installer
namespace: '{{repl Namespace }}'
8 changes: 8 additions & 0 deletions install/kots/manifests/gitpod-config-patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: gitpod-config-patch
labels:
app: gitpod
data:
gitpod-config-patch.yaml: '{{repl if and (ConfigOptionEquals "advanced_mode_enabled" "1") (ConfigOptionNotEquals "config_patch" "") }}{{repl ConfigOption "config_patch" }}{{repl else }}{{repl printf "{}" | Base64Encode }}{{repl end }}'
12 changes: 12 additions & 0 deletions install/kots/manifests/gitpod-database-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Secret
metadata:
name: database
annotations:
kots.io/when: '{{repl and (ConfigOptionEquals "db_incluster" "0") (ConfigOptionEquals "db_cloudsql_enabled" "0") }}'
data:
encryptionKeys: '{{repl ConfigOption "db_encryption_keys" | Base64Encode }}'
host: '{{repl ConfigOption "db_host" | Base64Encode }}'
password: '{{repl ConfigOption "db_password" | Base64Encode }}'
port: '{{repl ConfigOption "db_port" | Base64Encode }}'
username: '{{repl ConfigOption "db_username" | Base64Encode }}'
8 changes: 8 additions & 0 deletions install/kots/manifests/gitpod-dns-gcp-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: cert-manager-gcp-dns-solver
annotations:
kots.io/when: '{{repl and (ConfigOptionEquals "cert_manager_enabled" "1") (ConfigOptionEquals "cert_manager_provider" "gcp") }}'
data:
key.json: '{{repl ConfigOption "cert_manager_gcp_credentials" }}'
12 changes: 12 additions & 0 deletions install/kots/manifests/gitpod-installation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This exists as debug information for the support bundle
apiVersion: v1
kind: ConfigMap
metadata:
name: gitpod-installation
data:
channelName: '{{repl ChannelName }}'
cursor: '{{repl Cursor }}'
isAirgap: '{{repl IsAirgap }}'
releaseNotes: '{{repl ReleaseNotes }}'
sequence: '{{repl Sequence }}'
version: '{{repl VersionLabel }}'
195 changes: 195 additions & 0 deletions install/kots/manifests/gitpod-installer-job.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# The installer job is where the magic happens. It generates
# the config, installs Gitpod and then deletes itself when
# it's finished
apiVersion: batch/v1
kind: Job
metadata:
name: installer
labels:
app: gitpod
component: gitpod-installer
spec:
ttlSecondsAfterFinished: 60
template:
metadata:
labels:
app: gitpod
component: gitpod-installer
spec:
serviceAccountName: installer
restartPolicy: OnFailure
initContainers:
# Checks that the cert-manager installation is complete
- name: cert-manager
image: alpine/helm
command:
- /bin/sh
- -c
args:
- |
set -e

echo "Gitpod: Install jq"
apk add --no-cache jq

echo "Gitpod: Perform the check"
while [ "$(helm status -n {{repl Namespace }} cert-manager -o json | jq '.info.status == "deployed"')" = "false" ];
do
echo "Gitpod: Release not found - will retry in 10s"
sleep 10
done

echo "Gitpod: Release found - goodbye"
containers:
- name: installer
# This will normally be the release tag - using this tag as need the license evaluator
image: 'eu.gcr.io/gitpod-core-dev/build/installer:main.2569'
volumeMounts:
- mountPath: /config-patch
name: config-patch
readOnly: true
- mountPath: /mnt/node0
name: node-fs0
readOnly: true
env:
- name: CONFIG_FILE
value: /tmp/gitpod-config.yaml
- name: CONFIG_PATCH_FILE
value: /config-patch/gitpod-config-patch.yaml
- name: CONTAINERD_DIR_K3S
value: /run/k3s/containerd/io.containerd.runtime.v2.task/k8s.io
- name: CONTAINERD_SOCKET_K3S
value: /run/k3s/containerd/containerd.sock
- name: GITPOD_OBJECTS
value: /tmp/gitpod
command:
- /bin/sh
- -c
args:
- |
set -e

echo "Gitpod: Generate the base Installer config"
/app/installer init > "${CONFIG_FILE}"

echo "Gitpod: auto-detecting containerd location on host machine"
if [ -d "/mnt/node0${CONTAINERD_DIR_K3S}" ]; then
echo "Gitpod: containerd dir detected as k3s"

yq e -i ".workspace.runtime.containerdRuntimeDir = \"${CONTAINERD_DIR_K3S}\"" "${CONFIG_FILE}"
fi

if [ -S "/mnt/node0${CONTAINERD_SOCKET_K3S}" ]; then
echo "Gitpod: containerd socket detected as k3s"

yq e -i ".workspace.runtime.containerdSocket = \"${CONTAINERD_SOCKET_K3S}\"" "${CONFIG_FILE}"
fi

echo "Gitpod: Inject the Replicated variables into the config"
yq e -i '.domain = "{{repl ConfigOption "domain" }}"' "${CONFIG_FILE}"
yq e -i '.license.kind = "secret"' "${CONFIG_FILE}"
yq e -i '.license.name = "gitpod-license"' "${CONFIG_FILE}"

if [ '{{repl and (ConfigOptionEquals "db_incluster" "0") (ConfigOptionEquals "db_cloudsql_enabled" "1") }}' = "true" ];
then
echo "Gitpod: configuring CloudSQLProxy"

yq e -i ".database.inCluster = false" "${CONFIG_FILE}"
yq e -i ".database.cloudSQL.instance = \"{{repl ConfigOption "db_cloudsql_instance" }}\"" "${CONFIG_FILE}"
yq e -i ".database.cloudSQL.serviceAccount.kind = \"secret\"" "${CONFIG_FILE}"
yq e -i ".database.cloudSQL.serviceAccount.name = \"cloudsql\"" "${CONFIG_FILE}"
fi

if [ '{{repl and (ConfigOptionEquals "db_incluster" "0") (ConfigOptionEquals "db_cloudsql_enabled" "0") }}' = "true" ];
then
echo "Gitpod: configuring external database"

yq e -i ".database.inCluster = false" "${CONFIG_FILE}"
yq e -i ".database.external.certificate.kind = \"secret\"" "${CONFIG_FILE}"
yq e -i ".database.external.certificate.name = \"database\"" "${CONFIG_FILE}"
fi

if [ '{{repl ConfigOptionEquals "reg_incluster" "0" }}' = "true" ];
then
echo "Gitpod: configuring external container registry"

yq e -i ".containerRegistry.inCluster = false" "${CONFIG_FILE}"
yq e -i ".containerRegistry.external.url = \"{{repl ConfigOption "reg_url" }}\"" "${CONFIG_FILE}"
yq e -i ".containerRegistry.external.certificate.kind = \"secret\"" "${CONFIG_FILE}"
yq e -i ".containerRegistry.external.certificate.name = \"container-registry\"" "${CONFIG_FILE}"

if [ '{{repl ConfigOptionEquals "reg_s3storage" "1" }}' = "true" ];
then
echo "Gitpod: configuring container registry S3 backend"

yq e -i ".containerRegistry.s3storage.bucket = \"{{repl ConfigOption "reg_bucketname" }}\"" "${CONFIG_FILE}"
yq e -i ".containerRegistry.s3storage.certificate.kind = \"secret\"" "${CONFIG_FILE}"
yq e -i ".containerRegistry.s3storage.certificate.name = \"container-registry-s3-backend\"" "${CONFIG_FILE}"
fi
fi

if [ '{{repl ConfigOptionNotEquals "store_provider" "incluster" }}' = "true" ];
then
echo "Gitpod: configuring the storage"

yq e -i ".metadata.region = \"{{repl ConfigOption "store_region" }}\"" "${CONFIG_FILE}"
yq e -i ".objectStorage.inCluster = false" "${CONFIG_FILE}"

if [ '{{repl ConfigOptionEquals "store_provider" "azure" }}' = "true" ];
then
echo "Gitpod: configuring storage for Azure"

yq e -i ".objectStorage.azure.credentials.kind = \"secret\"" "${CONFIG_FILE}"
yq e -i ".objectStorage.azure.credentials.name = \"storage-azure\"" "${CONFIG_FILE}"
fi

if [ '{{repl ConfigOptionEquals "store_provider" "gcp" }}' = "true" ];
then
echo "Gitpod: configuring storage for GCP"

yq e -i ".objectStorage.cloudStorage.project = \"{{repl ConfigOption "store_gcp_project" }}\"" "${CONFIG_FILE}"
yq e -i ".objectStorage.cloudStorage.serviceAccount.kind = \"secret\"" "${CONFIG_FILE}"
yq e -i ".objectStorage.cloudStorage.serviceAccount.name = \"storage-gcp\"" "${CONFIG_FILE}"
fi

if [ '{{repl ConfigOptionEquals "store_provider" "s3" }}' = "true" ];
then
echo "Gitpod: configuring storage for S3"

yq e -i ".objectStorage.s3.endpoint = \"{{repl ConfigOption "store_s3_endpoint" }}\"" "${CONFIG_FILE}"
yq e -i ".objectStorage.s3.credentials.secret = \"secret\"" "${CONFIG_FILE}"
yq e -i ".objectStorage.s3.credentials.name = \"storage-s3\"" "${CONFIG_FILE}"
fi
fi

if [ '{{repl ConfigOptionEquals "ssh_gateway" "1" }}' = "true" ];
then
echo "Gitpod: Generate SSH host key"
apk update && apk add --no-cache openssh-keygen # TODO: Move installation of openssh-keygen to installer image
ssh-keygen -t rsa -q -N "" -f host.key
kubectl create secret generic ssh-gateway-host-key --from-file=host.key -n {{repl Namespace }} || echo "SSH Gateway Host Key secret has not been created. Does it exist already?"
yq e -i '.sshGatewayHostKey.kind = "secret"' "${CONFIG_FILE}"
yq e -i '.sshGatewayHostKey.name = "ssh-gateway-host-key"' "${CONFIG_FILE}"
fi

echo "Gitpod: Patch Gitpod config"
base64 -d "${CONFIG_PATCH_FILE}" > /tmp/patch.yaml
config_patch=$(cat /tmp/patch.yaml)
echo "Gitpod: ${CONFIG_PATCH_FILE}=${config_patch}"
yq eval-all --inplace 'select(fileIndex == 0) * select(fileIndex == 1)' "${CONFIG_FILE}" /tmp/patch.yaml

echo "Gitpod: Generate the Kubernetes objects and apply"
config=$(cat "${CONFIG_FILE}")
echo "Gitpod: ${CONFIG_FILE}=${config}"

/app/installer render -c "${CONFIG_FILE}" --namespace {{repl Namespace }} | kubectl apply -f -

echo "Gitpod: Installer job finished - goodbye"
volumes:
- name: config-patch
configMap:
name: gitpod-config-patch
- name: node-fs0
hostPath:
path: /
type: Directory
Loading